add zone refund

This commit is contained in:
lwl0608 2024-04-29 15:47:45 +08:00
parent a167a0ddff
commit c470a8e77f
5 changed files with 640 additions and 109 deletions

View File

@ -108,7 +108,7 @@ func (m *Mysql) CreateZoneUnlock(ctx *gin.Context, tx *sqlx.Tx, mid, zid int64)
return nil
}
// 添加到空间成员
// 解锁空间动态
func (m *Mysql) UnlockZoneMoment(ctx *gin.Context, tx *sqlx.Tx, mid, zid, momentId int64, orderId string) error {
var err error
// 先获取,没有再添加
@ -144,6 +144,23 @@ func (m *Mysql) UnlockZoneMoment(ctx *gin.Context, tx *sqlx.Tx, mid, zid, moment
return err
}
// 退款空间动态
func (m *Mysql) RefundZoneMoment(ctx *gin.Context, tx *sqlx.Tx, mid, zid, momentId int64) error {
var err error
sqlStr := fmt.Sprintf("delete from %s where mid=? and zid=? and moment_id=?", TableVasZoneMomentUnlock)
args := []any{mid, zid, momentId}
if tx != nil {
_, err = tx.ExecContext(ctx, sqlStr, args...)
} else {
db := m.getDBVas()
_, err = db.ExecContext(ctx, sqlStr, args...)
}
if err != nil {
return err
}
return err
}
// 获取动态解锁信息 by mid, moment_ids
func (m *Mysql) GetZoneMomentUnlockListByMidMomentIds(ctx *gin.Context, tx *sqlx.Tx, mid int64, momentIds []int64) (list []*dbstruct.ZoneMomentUnlock, err error) {
list = make([]*dbstruct.ZoneMomentUnlock, 0)
@ -249,6 +266,23 @@ func (m *Mysql) UnlockZoneSuperfanship(ctx *gin.Context, tx *sqlx.Tx, mid, zid,
return err
}
// 超粉退款
func (m *Mysql) RefundZoneSuperfanship(ctx *gin.Context, tx *sqlx.Tx, mid, zid int64) error {
var err error
sqlStr := "update " + TableVasZoneUnlock + " set superfanship_ct=?, superfanship_until=?, superfanship_unlock_type=? where mid=? and zid=?"
args := []any{0, 0, dbstruct.ZoneUnlockTypeRefund, mid, zid}
if tx != nil {
_, err = tx.ExecContext(ctx, sqlStr, args...)
} else {
db := m.getDBVas()
_, err = db.ExecContext(ctx, sqlStr, args...)
}
if err != nil {
return err
}
return err
}
// 增加空间消费详情
func (m *Mysql) CreateZoneConsumeHis(ctx *gin.Context, tx *sqlx.Tx, zch *dbstruct.ZoneConsumeHis) error {
var err error
@ -268,6 +302,26 @@ func (m *Mysql) CreateZoneConsumeHis(ctx *gin.Context, tx *sqlx.Tx, zch *dbstruc
return nil
}
// 获取空间消费详情
func (m *Mysql) GetZoneConsumeHisByOrderId(ctx *gin.Context, tx *sqlx.Tx, orderId string) (list []*dbstruct.ZoneConsumeHis, err error) {
list = make([]*dbstruct.ZoneConsumeHis, 0)
sqlStr := fmt.Sprintf("select * from %s where order_id=?", TableVasZoneMomentUnlock)
if tx != nil {
err = tx.SelectContext(ctx, &list, sqlStr, orderId)
} else {
db := m.getDBVas()
err = db.SelectContext(ctx, &list, sqlStr, orderId)
}
if err == sql.ErrNoRows {
err = nil
return
}
if err != nil {
return
}
return
}
// 增加空间消费
func (m *Mysql) IncZoneConsume(ctx *gin.Context, tx *sqlx.Tx, mid, zid, inc int64) error {
var err error

View File

@ -9,7 +9,6 @@ import (
"encoding/pem"
"errors"
"fmt"
"github.com/go-pay/gopay/wechat/v3"
"service/api/base"
"service/api/errs"
accountproto "service/api/proto/account/proto"
@ -2042,14 +2041,19 @@ func (v *Vas) RefundOrder(ctx *gin.Context, req *vasproto.RefundOrderReq, opt *v
switch product.Id {
case dbstruct.ProductIdH5ZoneAdmission:
if opt == nil {
opt = vasproto.NewRefundOrderOpt().SetZoneRefundReq(&vasproto.ZoneRefundReq{
Zid: order.GetZid(),
ContactName: "op",
ContactPhone: "op",
Note: "op",
})
opt = vasproto.NewRefundOrderOpt().SetZoneRefundReq(&vasproto.ZoneRefundReq{Zid: order.GetZid(), ContactName: "op", ContactPhone: "op", Note: "op"})
}
err = v.refundZoneAdmission(ctx, order, req, opt)
case dbstruct.ProductIdH5ZoneMoment:
if opt == nil {
opt = vasproto.NewRefundOrderOpt().SetZoneRefundReq(&vasproto.ZoneRefundReq{Zid: order.GetZid(), ContactName: "op", ContactPhone: "op", Note: "op"})
}
err = v.refundZoneMoment(ctx, order, req, opt)
case dbstruct.ProductIdH5ZoneSuperfanship:
if opt == nil {
opt = vasproto.NewRefundOrderOpt().SetZoneRefundReq(&vasproto.ZoneRefundReq{Zid: order.GetZid(), ContactName: "op", ContactPhone: "op", Note: "op"})
}
err = v.refundZoneSuperfanship(ctx, order, req, opt)
default:
err = fmt.Errorf("不支持该商品退款: %s", product.Id)
}
@ -2177,6 +2181,13 @@ func (v *Vas) refundMembership(ctx *gin.Context, order *dbstruct.Order, req *vas
return err
}
// 扣除空间消费
err = v.RollbackZoneConsume(ctx, tx, orderId)
if err != nil {
logger.Error("RollbackZoneConsume fail, orderId: %v", orderId)
return err
}
// 删除会员解锁记录
err = v.store.DeleteUserVasMembershipUnlock(ctx, tx, order.GetMid())
if err != nil {
@ -2284,29 +2295,9 @@ func (v *Vas) refundMembership(ctx *gin.Context, order *dbstruct.Order, req *vas
}
// 退款
switch order.GetPayType() {
case vasproto.PayTypeAlipay, vasproto.PayTypeAlipayH5:
alipayCli := alipaycli.GetAlipayClientByAppId(order.GetOid3())
resp, err := alipayCli.RefundOne(ctx, &alipaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("alipayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
case vasproto.PayTypeWxpayNative, vasproto.PayTypeWxpayJsapi, vasproto.PayTypeWxpayH5:
wxpayCli := wxpaycli.GetDefaultWxpayClient()
resp, err := wxpayCli.RefundOne(ctx, &wxpaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("wxpayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
err = v.payRefund(ctx, order)
if err != nil {
return err
}
return nil
@ -2402,29 +2393,9 @@ func (v *Vas) refundCoins(ctx *gin.Context, order *dbstruct.Order, req *vasproto
}
// 退款
switch order.GetPayType() {
case vasproto.PayTypeAlipay, vasproto.PayTypeAlipayH5:
alipayCli := alipaycli.GetAlipayClientByAppId(order.GetOid3())
resp, err := alipayCli.RefundOne(ctx, &alipaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("alipayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
case vasproto.PayTypeWxpayNative, vasproto.PayTypeWxpayJsapi, vasproto.PayTypeWxpayH5:
wxpayCli := wxpaycli.GetDefaultWxpayClient()
resp, err := wxpayCli.RefundOne(ctx, &wxpaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("wxpayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
err = v.payRefund(ctx, order)
if err != nil {
return err
}
return nil
@ -2520,35 +2491,15 @@ func (v *Vas) refundMoneyContactWechat(ctx *gin.Context, order *dbstruct.Order,
}
// 退款
switch order.GetPayType() {
case vasproto.PayTypeAlipay, vasproto.PayTypeAlipayH5:
alipayCli := alipaycli.GetAlipayClientByAppId(order.GetOid3())
resp, err := alipayCli.RefundOne(ctx, &alipaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("alipayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
case vasproto.PayTypeWxpayNative, vasproto.PayTypeWxpayJsapi, vasproto.PayTypeWxpayH5:
wxpayCli := wxpaycli.GetDefaultWxpayClient()
resp, err := wxpayCli.RefundOne(ctx, &wxpaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("wxpayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
err = v.payRefund(ctx, order)
if err != nil {
return err
}
return nil
}
// todo: 现金:普通空间退款
// 现金:普通空间退款
func (v *Vas) refundZoneAdmission(ctx *gin.Context, order *dbstruct.Order, req *vasproto.RefundOrderReq, opt *vasproto.RefundOrderOpt) error {
switch order.GetOrderStatus() {
case dbstruct.VasOrderStatusNone, dbstruct.VasOrderStatusInit:
@ -2766,31 +2717,469 @@ func (v *Vas) refundZoneAdmission(ctx *gin.Context, order *dbstruct.Order, req *
}
// 退款
switch order.GetPayType() {
case vasproto.PayTypeAlipay, vasproto.PayTypeAlipayH5:
alipayCli := alipaycli.GetAlipayClientByAppId(order.GetOid3())
var resp *alipay.TradeRefundResponse
resp, err = alipayCli.RefundOne(ctx, &alipaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
err = v.payRefund(ctx, order)
if err != nil {
return err
}
return nil
}
// 现金:空间动态退款
func (v *Vas) refundZoneMoment(ctx *gin.Context, order *dbstruct.Order, req *vasproto.RefundOrderReq, opt *vasproto.RefundOrderOpt) error {
switch order.GetOrderStatus() {
case dbstruct.VasOrderStatusNone, dbstruct.VasOrderStatusInit:
return errors.New("订单还未支付,无法退款")
case dbstruct.VasOrderStatusRefund:
return errors.New("已退款,请勿重复操作")
}
var (
mid = order.GetMid()
zid = int64(0)
momentId = order.GetMomentId()
orderId = order.GetID()
isFinish = order.GetOrderStatus() == dbstruct.VasOrderStatusFinish
zoneRefundReq *vasproto.ZoneRefundReq
)
if opt.GetZoneRefundReq() != nil {
zoneRefundReq = opt.GetZoneRefundReq()
}
if zoneRefundReq == nil {
return fmt.Errorf("zoneRefundReq is nil, order_id: %v", orderId)
}
zid = zoneRefundReq.Zid
// 和订单的zid不匹配
if order.GetZid() != zid {
err := fmt.Errorf("与订单的zid不匹配orderZid: %v, reqZid: %v", order.GetZid(), zid)
return err
}
// 开启事务
tx, err := v.store.VasBegin(ctx)
if err != nil {
logger.Error("vas begin fail, err: %v", err)
return err
}
defer func() {
if order != nil {
_ = v.AddOplogOrder(
ctx,
&dbstruct.OplogOrder{
OrderId: orderId,
Action: dbstruct.OrderOpLogActionUpdate,
Operator: req.Operator,
Detail: fmt.Sprintf("空间动态退款"),
BeforeStatus: order.GetOrderStatus(),
AfterStatus: dbstruct.VasOrderStatusRefund,
},
err,
)
}
if err != nil {
logger.Error("alipayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
logger.Error("global err, req: %v, err: %v", util.ToJson(req), err)
}
errTx := v.store.DealTxCR(tx, err)
if errTx != nil {
logger.Error("DealTxCR fail, err: %v", errTx)
return
}
}()
// 用户的充值退款记录
chargeChList, err := v.store.GetChargeCHList(ctx, tx, orderId)
if err != nil {
logger.Error("GetChargeCHList fail, orderId: %v, err: %v", orderId, err)
return err
}
if len(chargeChList) <= 0 || len(chargeChList) > 1 {
err = errors.New("invalid charge ch list")
logger.Error("invalid charge ch list, orderId: %v, err: %v", orderId, err)
return err
}
chargeCh := chargeChList[0]
chargeChNew := &dbstruct.ConsumeHistory{
Mid: goproto.Int64(chargeCh.GetMid()),
Uid: goproto.Int64(chargeCh.GetUid()),
Did: goproto.String(chargeCh.GetDid()),
Type: goproto.Int32(chargeCh.GetType()),
SType: goproto.Int32(dbstruct.CHSTypeChargeZoneRefundMoment),
TypeId: goproto.String(chargeCh.GetTypeId()),
OrderId: goproto.String(chargeCh.GetOrderId()),
Change: goproto.Int64(-chargeCh.GetChange()),
Ct: goproto.Int64(time.Now().Unix()),
}
err = v.store.CreateConsumeHistory(ctx, tx, chargeChNew)
if err != nil {
logger.Error("CreateConsumeHistory fail, chargeChNew: %v, err: %v", util.ToJson(chargeChNew), err)
return err
}
// 修改订单状态 xx -> 已退款
err = v.store.UpdateOrderStatus(ctx, tx, orderId, order.GetOrderStatus(), dbstruct.VasOrderStatusRefund)
if err != nil {
logger.Error("UpdateOrderStatus fail, err: %v", err)
return err
}
// 扣除空间消费
err = v.RollbackZoneConsume(ctx, tx, orderId)
if err != nil {
logger.Error("RollbackZoneConsume fail, orderId: %v", orderId)
return err
}
// 删除空间动态
err = v.store.RefundZoneMoment(ctx, tx, mid, zid, momentId)
if err != nil {
logger.Error("RefundZoneMoment fail, mid: %v, zid: %v, momentId: %v, err: %v", mid, zid, momentId, err)
return err
}
// 添加退款记录
zrh := &dbstruct.ZoneRefundHis{
Mid: goproto.Int64(mid),
Zid: goproto.Int64(zid),
ContactName: goproto.String(zoneRefundReq.ContactName),
ContactPhone: goproto.String(zoneRefundReq.ContactPhone),
Note: goproto.String(fmt.Sprintf("{\"moment_id\":%d}", momentId)),
OrderId: goproto.String(orderId),
ProductId: goproto.String(order.GetProductId()),
}
err = v.store.CreateZoneRefundHis(ctx, tx, zrh)
if err != nil {
logger.Error("CreateZoneRefundHis fail, zrh: %v, err: %v", util.ToJson(zrh), err)
return err
}
// 回滚收入记录
chListTmp, err := v.store.GetIncomeCHList(ctx, tx, orderId)
if err != nil {
return err
}
incomeChList := make([]*dbstruct.ConsumeHistory, 0)
for _, ch := range chListTmp {
if ch.GetMid() == common.OfficialMid {
continue
}
incomeChList = append(incomeChList, ch)
}
for _, ch := range incomeChList {
streamerMid := ch.GetMid()
if streamerMid <= 0 {
err = errors.New("收入streamerMid错误")
logger.Error("invalid streamerMid: %v", streamerMid)
return err
}
case vasproto.PayTypeWxpayNative, vasproto.PayTypeWxpayJsapi, vasproto.PayTypeWxpayH5:
wxpayCli := wxpaycli.GetDefaultWxpayClient()
var resp *wechat.RefundRsp
resp, err = wxpayCli.RefundOne(ctx, &wxpaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
// 获取钱包
wallet, err := v.store.GetWalletForUpdate(ctx, tx, streamerMid)
if err != nil {
logger.Error("wxpayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
// 扣钻石
change := ch.GetChange()
err = v.store.DecDiamonds(ctx, tx, streamerMid, change)
if err != nil {
return err
}
// 扣钻石收入记录
chNew := &dbstruct.ConsumeHistory{
Mid: goproto.Int64(streamerMid),
Uid: goproto.Int64(ch.GetMid()),
Did: goproto.String(ch.GetDid()),
Type: goproto.Int32(dbstruct.CHTypeIncome),
SType: goproto.Int32(dbstruct.CHSTypeIncomeRefundZoneAdmission),
TypeId: goproto.String(ch.GetTypeId()),
OrderId: goproto.String(ch.GetOrderId()),
Change: goproto.Int64(-change),
Before: goproto.Int64(wallet.GetDiamonds()),
After: goproto.Int64(wallet.GetDiamonds() - change),
Ct: goproto.Int64(time.Now().Unix()),
}
switch ch.GetSType() {
case dbstruct.CHSTypeIncomeThirdPartner:
chNew.SType = goproto.Int32(dbstruct.CHSTypeIncomeRefundThirdPartner)
case dbstruct.CHSTypeIncomeCollaborator:
chNew.SType = goproto.Int32(dbstruct.CHSTypeIncomeRefundCollaborator)
case dbstruct.CHSTypeIncomeZoneStreamer:
chNew.SType = goproto.Int32(dbstruct.CHSTypeIncomeRefundZoneStreamer)
}
err = v.store.CreateConsumeHistory(ctx, tx, chNew)
if err != nil {
logger.Error("CreateConsumeHistory fail, ch: %v, err: %v", util.ToJson(chNew), err)
return err
}
// 已结算的情况,扣提现钻石
if isFinish {
// 扣提现钻石
err = v.store.DecWithdrawDiamonds(ctx, tx, streamerMid, change)
if err != nil {
logger.Error("DecWithdrawDiamonds fail, streamerMid: %v, change: %v, err: %v", streamerMid, change, err)
return err
}
// 提现钻石记录
wh := &dbstruct.WithdrawDiamondsHis{
Mid: goproto.Int64(streamerMid),
IncomeChId: goproto.Int64(ch.GetId()),
OrderId: goproto.String(ch.GetOrderId()),
Ct: goproto.Int64(time.Now().Unix()),
BeforeWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds()),
AfterWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds() - change),
Change: goproto.Int64(-change),
ProductId: goproto.String(order.GetProductId()),
}
err = v.store.CreateWithdrawDiamondsHis(ctx, tx, wh)
if err != nil {
logger.Error("CreateWithdrawDiamondsHis fail, wh: %v, err: %v", util.ToJson(wh), err)
return err
}
}
}
// 退款
err = v.payRefund(ctx, order)
if err != nil {
return err
}
return nil
}
// 现金:超粉退款
func (v *Vas) refundZoneSuperfanship(ctx *gin.Context, order *dbstruct.Order, req *vasproto.RefundOrderReq, opt *vasproto.RefundOrderOpt) error {
switch order.GetOrderStatus() {
case dbstruct.VasOrderStatusNone, dbstruct.VasOrderStatusInit:
return errors.New("订单还未支付,无法退款")
case dbstruct.VasOrderStatusRefund:
return errors.New("已退款,请勿重复操作")
}
var (
mid = order.GetMid()
zid = int64(0)
orderId = order.GetID()
isFinish = order.GetOrderStatus() == dbstruct.VasOrderStatusFinish
zoneRefundReq *vasproto.ZoneRefundReq
)
if opt.GetZoneRefundReq() != nil {
zoneRefundReq = opt.GetZoneRefundReq()
}
if zoneRefundReq == nil {
return fmt.Errorf("zoneRefundReq is nil, order_id: %v", orderId)
}
zid = zoneRefundReq.Zid
// 和订单的zid不匹配
if order.GetZid() != zid {
err := fmt.Errorf("与订单的zid不匹配orderZid: %v, reqZid: %v", order.GetZid(), zid)
return err
}
// 开启事务
tx, err := v.store.VasBegin(ctx)
if err != nil {
logger.Error("vas begin fail, err: %v", err)
return err
}
defer func() {
if order != nil {
_ = v.AddOplogOrder(
ctx,
&dbstruct.OplogOrder{
OrderId: orderId,
Action: dbstruct.OrderOpLogActionUpdate,
Operator: req.Operator,
Detail: fmt.Sprintf("空间超粉退款"),
BeforeStatus: order.GetOrderStatus(),
AfterStatus: dbstruct.VasOrderStatusRefund,
},
err,
)
}
if err != nil {
logger.Error("global err, req: %v, err: %v", util.ToJson(req), err)
}
errTx := v.store.DealTxCR(tx, err)
if errTx != nil {
logger.Error("DealTxCR fail, err: %v", errTx)
return
}
}()
// 用户的充值退款记录
chargeChList, err := v.store.GetChargeCHList(ctx, tx, orderId)
if err != nil {
logger.Error("GetChargeCHList fail, orderId: %v, err: %v", orderId, err)
return err
}
if len(chargeChList) <= 0 || len(chargeChList) > 1 {
err = errors.New("invalid charge ch list")
logger.Error("invalid charge ch list, orderId: %v, err: %v", orderId, err)
return err
}
chargeCh := chargeChList[0]
chargeChNew := &dbstruct.ConsumeHistory{
Mid: goproto.Int64(chargeCh.GetMid()),
Uid: goproto.Int64(chargeCh.GetUid()),
Did: goproto.String(chargeCh.GetDid()),
Type: goproto.Int32(chargeCh.GetType()),
SType: goproto.Int32(dbstruct.CHSTypeChargeZoneRefundSuperfanship),
TypeId: goproto.String(chargeCh.GetTypeId()),
OrderId: goproto.String(chargeCh.GetOrderId()),
Change: goproto.Int64(-chargeCh.GetChange()),
Ct: goproto.Int64(time.Now().Unix()),
}
err = v.store.CreateConsumeHistory(ctx, tx, chargeChNew)
if err != nil {
logger.Error("CreateConsumeHistory fail, chargeChNew: %v, err: %v", util.ToJson(chargeChNew), err)
return err
}
// 修改订单状态 xx -> 已退款
err = v.store.UpdateOrderStatus(ctx, tx, orderId, order.GetOrderStatus(), dbstruct.VasOrderStatusRefund)
if err != nil {
logger.Error("UpdateOrderStatus fail, err: %v", err)
return err
}
// 扣除空间消费
err = v.RollbackZoneConsume(ctx, tx, orderId)
if err != nil {
logger.Error("RollbackZoneConsume fail, orderId: %v", orderId)
return err
}
// 删除空间超粉
err = v.store.RefundZoneSuperfanship(ctx, tx, mid, zid)
if err != nil {
logger.Error("RefundZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
return err
}
// 删除空间超粉成员
err = v.store.DeleteZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeSuperfan)
if err != nil {
logger.Error("DeleteZoneMember fail, mid: %v, zid: %v, err: %v", mid, zid, err)
return err
}
// 添加退款记录
zrh := &dbstruct.ZoneRefundHis{
Mid: goproto.Int64(mid),
Zid: goproto.Int64(zid),
ContactName: goproto.String(zoneRefundReq.ContactName),
ContactPhone: goproto.String(zoneRefundReq.ContactPhone),
Note: goproto.String(zoneRefundReq.Note),
OrderId: goproto.String(orderId),
ProductId: goproto.String(order.GetProductId()),
}
err = v.store.CreateZoneRefundHis(ctx, tx, zrh)
if err != nil {
logger.Error("CreateZoneRefundHis fail, zrh: %v, err: %v", util.ToJson(zrh), err)
return err
}
// 回滚收入记录
chListTmp, err := v.store.GetIncomeCHList(ctx, tx, orderId)
if err != nil {
return err
}
incomeChList := make([]*dbstruct.ConsumeHistory, 0)
for _, ch := range chListTmp {
if ch.GetMid() == common.OfficialMid {
continue
}
incomeChList = append(incomeChList, ch)
}
for _, ch := range incomeChList {
streamerMid := ch.GetMid()
if streamerMid <= 0 {
err = errors.New("收入streamerMid错误")
logger.Error("invalid streamerMid: %v", streamerMid)
return err
}
// 获取钱包
wallet, err := v.store.GetWalletForUpdate(ctx, tx, streamerMid)
if err != nil {
return err
}
// 扣钻石
change := ch.GetChange()
err = v.store.DecDiamonds(ctx, tx, streamerMid, change)
if err != nil {
return err
}
// 扣钻石收入记录
chNew := &dbstruct.ConsumeHistory{
Mid: goproto.Int64(streamerMid),
Uid: goproto.Int64(ch.GetMid()),
Did: goproto.String(ch.GetDid()),
Type: goproto.Int32(dbstruct.CHTypeIncome),
SType: goproto.Int32(dbstruct.CHSTypeIncomeRefundZoneAdmission),
TypeId: goproto.String(ch.GetTypeId()),
OrderId: goproto.String(ch.GetOrderId()),
Change: goproto.Int64(-change),
Before: goproto.Int64(wallet.GetDiamonds()),
After: goproto.Int64(wallet.GetDiamonds() - change),
Ct: goproto.Int64(time.Now().Unix()),
}
switch ch.GetSType() {
case dbstruct.CHSTypeIncomeThirdPartner:
chNew.SType = goproto.Int32(dbstruct.CHSTypeIncomeRefundThirdPartner)
case dbstruct.CHSTypeIncomeCollaborator:
chNew.SType = goproto.Int32(dbstruct.CHSTypeIncomeRefundCollaborator)
case dbstruct.CHSTypeIncomeZoneStreamer:
chNew.SType = goproto.Int32(dbstruct.CHSTypeIncomeRefundZoneStreamer)
}
err = v.store.CreateConsumeHistory(ctx, tx, chNew)
if err != nil {
logger.Error("CreateConsumeHistory fail, ch: %v, err: %v", util.ToJson(chNew), err)
return err
}
// 已结算的情况,扣提现钻石
if isFinish {
// 扣提现钻石
err = v.store.DecWithdrawDiamonds(ctx, tx, streamerMid, change)
if err != nil {
logger.Error("DecWithdrawDiamonds fail, streamerMid: %v, change: %v, err: %v", streamerMid, change, err)
return err
}
// 提现钻石记录
wh := &dbstruct.WithdrawDiamondsHis{
Mid: goproto.Int64(streamerMid),
IncomeChId: goproto.Int64(ch.GetId()),
OrderId: goproto.String(ch.GetOrderId()),
Ct: goproto.Int64(time.Now().Unix()),
BeforeWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds()),
AfterWithdrawDiamonds: goproto.Int64(wallet.GetWithdrawDiamonds() - change),
Change: goproto.Int64(-change),
ProductId: goproto.String(order.GetProductId()),
}
err = v.store.CreateWithdrawDiamondsHis(ctx, tx, wh)
if err != nil {
logger.Error("CreateWithdrawDiamondsHis fail, wh: %v, err: %v", util.ToJson(wh), err)
return err
}
}
}
// 退款
err = v.payRefund(ctx, order)
if err != nil {
return err
}
return nil
@ -2907,6 +3296,13 @@ func (v *Vas) refundCoinContactWechat(ctx *gin.Context, order *dbstruct.CoinOrde
return err
}
// 回滚空间消费
err = v.RollbackZoneConsume(ctx, tx, orderId)
if err != nil {
logger.Error("RollbackZoneConsume fail, orderId: %v", orderId)
return err
}
// 删除微信解锁记录
err = v.store.DeleteUserVasUnlock(ctx, tx, mid, orderId, dbstruct.ProductIdContactWechat)
if err != nil {
@ -3004,3 +3400,34 @@ func (v *Vas) refundCoinContactWechat(ctx *gin.Context, order *dbstruct.CoinOrde
return nil
}
func (v *Vas) payRefund(ctx *gin.Context, order *dbstruct.Order) error {
var (
orderId = order.GetID()
)
switch order.GetPayType() {
case vasproto.PayTypeAlipay, vasproto.PayTypeAlipayH5:
alipayCli := alipaycli.GetAlipayClientByAppId(order.GetOid3())
resp, err := alipayCli.RefundOne(ctx, &alipaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("alipayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
case vasproto.PayTypeWxpayNative, vasproto.PayTypeWxpayJsapi, vasproto.PayTypeWxpayH5:
wxpayCli := wxpaycli.GetDefaultWxpayClient()
resp, err := wxpayCli.RefundOne(ctx, &wxpaycli.RefundOneParam{
OutTradeNo: orderId,
RefundAmount: order.GetPayAmount(),
RefundReason: "用户退款",
})
if err != nil {
logger.Error("wxpayCli.RefundOne fail, orderId: %v, resp: %v, err: %v", orderId, util.ToJson(resp), err)
return err
}
}
return nil
}

View File

@ -93,6 +93,47 @@ func (v *Vas) IncZoneConsume(ctx *gin.Context, tx *sqlx.Tx, zid, mid, streamerMi
return nil
}
// 减少空间消费
func (v *Vas) RollbackZoneConsume(ctx *gin.Context, tx *sqlx.Tx, orderId string) error {
var err error
// 获取空间消费记录如果不是1个则非法
zchList, _ := v.store.GetZoneConsumeHisByOrderId(ctx, tx, orderId)
if len(zchList) == 0 {
logger.Info("nil zone consume, orderId: %v", orderId)
return nil
}
if len(zchList) != 1 {
logger.Error("invalid zchList: %v, orderId: %v", util.ToJson(zchList), orderId)
err = fmt.Errorf("非法空间消费历史,请找开发")
return err
}
zchOld := zchList[0]
zid := zchOld.GetZid()
mid := zchOld.GetMid()
// 扣除空间消费
err = v.store.DecZoneConsume(ctx, tx, mid, zid, zchOld.GetConsume())
if err != nil {
logger.Error("DecZoneConsume fail, mid: %v, zid: %v, pay: %v, err: %v", mid, zid, zchOld.GetConsume(), err)
return err
}
// 增加空间消费记录
zch := &dbstruct.ZoneConsumeHis{
Mid: goproto.Int64(mid),
Zid: goproto.Int64(zid),
Consume: goproto.Int64(-zchOld.GetConsume()),
OrderId: goproto.String(zchOld.GetOrderId()),
ProductId: goproto.String(zchOld.GetProductId()),
}
err = v.store.CreateZoneConsumeHis(ctx, tx, zch)
if err != nil {
logger.Error("CreateZoneConsumeHis fail, zch, err: %v", util.ToJson(zch), err)
return err
}
return nil
}
// 设置动态价格
func (v *Vas) UpdateZoneMomentPrice(ctx *gin.Context, req *vasproto.UpdateZoneMomentPriceReq) error {
err := v.store.UpsertZoneMomentPrice(ctx, req.ZoneMomentPrice)

View File

@ -305,6 +305,12 @@ func (s *Service) chListCharge(ctx *gin.Context, chList []*dbstruct.ConsumeHisto
case dbstruct.CHSTypeChargeZoneRefundAdmission:
item.Desc = "加入空间退款"
item.Change = fmt.Sprintf("+%.1f元", float64(util.AbsInt64(chDB.GetChange()))/100.0)
case dbstruct.CHSTypeChargeZoneRefundMoment:
item.Desc = "解锁空间动态退款"
item.Change = fmt.Sprintf("+%.1f元", float64(util.AbsInt64(chDB.GetChange()))/100.0)
case dbstruct.CHSTypeChargeZoneRefundSuperfanship:
item.Desc = "解锁空间超粉退款"
item.Change = fmt.Sprintf("+%.1f元", float64(util.AbsInt64(chDB.GetChange()))/100.0)
}
list = append(list, item)

View File

@ -524,16 +524,18 @@ const (
CHSTypeCostMembership = 10003 // 消费明细,会员资格解锁(伪金币记录,会员资格解锁中间无转金币过程)
CHSTypeCostRefundMembership = 10004 // 消费明细,会员资格解锁退款(伪金币记录,会员资格解锁中间无转金币过程)
CHSTypeChargeUser = 20001 // 充值明细,用户自己冲
CHSTypeChargeOp = 20002 // 充值明细OP充值
CHSTypeChargeRefundCoins = 20003 // 充值明细,金币退款
CHSTypeChargeRefundMembership = 20004 // 充值明细,会员退款
CHSTypeChargeMembership = 20005 // 充值明细,会员充值
CHSTypeChargeRefundContactWechat = 20006 // 充值明细,微信金币退款
CHSTypeChargeZoneMoment = 20007 // 充值明细,动态解锁
CHSTypeChargeZoneAdmission = 20008 // 充值明细,空间会员
CHSTypeChargeZoneSuperfanship = 20009 // 充值明细,空间超粉
CHSTypeChargeZoneRefundAdmission = 20010 // 充值明细,空间普通会员退款
CHSTypeChargeUser = 20001 // 充值明细,用户自己冲
CHSTypeChargeOp = 20002 // 充值明细OP充值
CHSTypeChargeRefundCoins = 20003 // 充值明细,金币退款
CHSTypeChargeRefundMembership = 20004 // 充值明细,会员退款
CHSTypeChargeMembership = 20005 // 充值明细,会员充值
CHSTypeChargeRefundContactWechat = 20006 // 充值明细,微信金币退款
CHSTypeChargeZoneMoment = 20007 // 充值明细,动态解锁
CHSTypeChargeZoneAdmission = 20008 // 充值明细,空间会员
CHSTypeChargeZoneSuperfanship = 20009 // 充值明细,空间超粉
CHSTypeChargeZoneRefundAdmission = 20010 // 充值明细,空间普通会员退款
CHSTypeChargeZoneRefundMoment = 20011 // 充值明细,空间动态退款
CHSTypeChargeZoneRefundSuperfanship = 20012 // 充值明细,空间动态退款
CHSTypeIncomeContact = 30001 // 收入明细,联系方式
CHSTypeIncomeInvite = 30002 // 收入明细,邀请分成
@ -1224,8 +1226,9 @@ func (p *ZoneMember) GetCt() int64 {
// 空间动态解锁
const (
ZoneMomentUnlockStatusLock = 0 // 未解锁
ZoneMomentUnlockStatusUnlock = 1 // 已解锁
ZoneMomentUnlockStatusRefund = -1 // 已退款
ZoneMomentUnlockStatusLock = 0 // 未解锁
ZoneMomentUnlockStatusUnlock = 1 // 已解锁
)
type ZoneMomentUnlock struct {