refund coin order
This commit is contained in:
parent
05fbd7e04e
commit
fca64cbb67
|
@ -54,3 +54,11 @@ type RefundOrderReq struct {
|
|||
|
||||
type RefundOrderData struct {
|
||||
}
|
||||
|
||||
type RefundCoinOrderReq struct {
|
||||
OrderId string `json:"order_id"`
|
||||
Operator string `json:"operator"`
|
||||
}
|
||||
|
||||
type RefundCoinOrderData struct {
|
||||
}
|
||||
|
|
|
@ -241,6 +241,7 @@ func Init(r *gin.Engine) {
|
|||
opVasPayGroup.POST("create_order", middleware.JSONParamValidator(vasproto.OpCreateOrderReq{}), OpCreateOrder)
|
||||
opVasPayGroup.POST("coin_order_list", middleware.JSONParamValidator(vasproto.OpCoinOrderListReq{}), OpOrderList)
|
||||
opVasPayGroup.POST("refund_order", middleware.JSONParamValidator(vasproto.RefundOrderReq{}), RefundOrder)
|
||||
opVasPayGroup.POST("refund_coin_order", middleware.JSONParamValidator(vasproto.RefundCoinOrderReq{}), RefundCoinOrder)
|
||||
|
||||
// 验证码
|
||||
opVeriCodeGroup := r.Group("/op/veri_code", PrepareOp())
|
||||
|
|
|
@ -324,3 +324,19 @@ func RefundOrder(ctx *gin.Context) {
|
|||
}
|
||||
ReplyOk(ctx, nil)
|
||||
}
|
||||
|
||||
// 金币订单退款
|
||||
func RefundCoinOrder(ctx *gin.Context) {
|
||||
req := ctx.MustGet("client_req").(*vasproto.RefundCoinOrderReq)
|
||||
ec, err := service.DefaultService.RefundCoinOrder(ctx, req)
|
||||
if ec != errcode.ErrCodeVasSrvOk {
|
||||
logger.Error("RefundCoinOrder fail, req: %v, ec: %v", util.ToJson(req), ec)
|
||||
if err != nil {
|
||||
ReplyErrorMsg(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
ReplyErrCodeMsg(ctx, ec)
|
||||
return
|
||||
}
|
||||
ReplyOk(ctx, nil)
|
||||
}
|
||||
|
|
|
@ -732,6 +732,21 @@ func (m *Mysql) GetUnlockWechatList(ctx *gin.Context, tx *sqlx.Tx, mid int64, of
|
|||
return
|
||||
}
|
||||
|
||||
// 删除会员解锁记录
|
||||
func (m *Mysql) DeleteUserVasUnlock(ctx *gin.Context, tx *sqlx.Tx, mid int64, orderId, productId string) (err error) {
|
||||
sqlStr := fmt.Sprintf("delete from %s where mid=? and order_id=? and product_id=?", TableVasUserUnlock)
|
||||
if tx != nil {
|
||||
_, err = tx.ExecContext(ctx, sqlStr, mid, orderId, productId)
|
||||
} else {
|
||||
db := m.getDBVas()
|
||||
_, err = db.ExecContext(ctx, sqlStr, mid, orderId, productId)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 获取消费历史
|
||||
func (m *Mysql) GetUCHList(ctx *gin.Context, tx *sqlx.Tx, mid int64, typ int32, offset, limit int) (list []*dbstruct.ConsumeHistory, err error) {
|
||||
list = make([]*dbstruct.ConsumeHistory, 0)
|
||||
|
|
|
@ -2431,6 +2431,7 @@ func (v *Vas) UnlockMembership(ctx *gin.Context, mid int64, product *dbstruct.Pr
|
|||
|
||||
// 订单退款,只退充值
|
||||
func (v *Vas) RefundOrder(ctx *gin.Context, req *vasproto.RefundOrderReq) error {
|
||||
// 获取订单
|
||||
order, err := v.store.GetOrderById(ctx, nil, req.OrderId)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2439,8 +2440,17 @@ func (v *Vas) RefundOrder(ctx *gin.Context, req *vasproto.RefundOrderReq) error
|
|||
return errs.ErrVasOrderNotExists
|
||||
}
|
||||
|
||||
switch order.GetProductId() {
|
||||
case dbstruct.ProductIdMembership:
|
||||
// 获取商品
|
||||
product, err := v.store.GetProductById(ctx, order.GetProductId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if product == nil {
|
||||
return errs.ErrVasProductNotExists
|
||||
}
|
||||
|
||||
switch product.Type {
|
||||
case dbstruct.ProductTypeMoneyMembership:
|
||||
return v.refundMembership(ctx, order, req)
|
||||
default:
|
||||
return errors.New("invalid product")
|
||||
|
@ -2610,7 +2620,7 @@ func (v *Vas) refundMembership(ctx *gin.Context, order *dbstruct.Order, req *vas
|
|||
Uid: goproto.Int64(ch.GetMid()),
|
||||
Did: goproto.String(ch.GetDid()),
|
||||
Type: goproto.Int32(dbstruct.CHTypeIncome),
|
||||
SType: goproto.Int32(dbstruct.CHSTypeIncomeRefund),
|
||||
SType: goproto.Int32(dbstruct.CHSTypeIncomeRefundMembership),
|
||||
TypeId: goproto.String(ch.GetTypeId()),
|
||||
OrderId: goproto.String(ch.GetOrderId()),
|
||||
Change: goproto.Int64(-change),
|
||||
|
@ -2691,3 +2701,218 @@ func (v *Vas) refundMembership(ctx *gin.Context, order *dbstruct.Order, req *vas
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 金币订单退款
|
||||
func (v *Vas) RefundCoinOrder(ctx *gin.Context, req *vasproto.RefundCoinOrderReq) error {
|
||||
// 获取订单
|
||||
order, err := v.store.GetCoinOrderById(ctx, nil, req.OrderId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if order == nil {
|
||||
return errs.ErrVasOrderNotExists
|
||||
}
|
||||
|
||||
// 获取商品
|
||||
product, err := v.store.GetProductById(ctx, order.GetProductId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if product == nil {
|
||||
return errs.ErrVasProductNotExists
|
||||
}
|
||||
|
||||
switch order.GetProductId() {
|
||||
case dbstruct.ProductIdContactWechat:
|
||||
return v.refundContactWechat(ctx, order, req)
|
||||
default:
|
||||
return errors.New("invalid product")
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Vas) refundContactWechat(ctx *gin.Context, order *dbstruct.CoinOrder, req *vasproto.RefundCoinOrderReq) error {
|
||||
switch order.GetOrderStatus() {
|
||||
case dbstruct.VasCoinOrderStatusNone, dbstruct.VasOrderStatusInit:
|
||||
return errors.New("订单还未成功支付,无法退款")
|
||||
case dbstruct.VasCoinOrderStatusRefund:
|
||||
return errors.New("已退款,请勿重复操作")
|
||||
}
|
||||
|
||||
var (
|
||||
mid = order.GetMid()
|
||||
orderId = order.GetID()
|
||||
isFinish = order.GetOrderStatus() == dbstruct.VasCoinOrderStatusFinish
|
||||
)
|
||||
|
||||
// 开启事务
|
||||
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.VasCoinOrderStatusRefund,
|
||||
},
|
||||
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
|
||||
}
|
||||
}()
|
||||
|
||||
// 用户消费记录
|
||||
costChList, err := v.store.GetCostCHList(ctx, tx, orderId)
|
||||
if err != nil {
|
||||
logger.Error("GetCostCHList fail, err: %v", err)
|
||||
return err
|
||||
}
|
||||
if len(costChList) <= 0 || len(costChList) > 1 {
|
||||
err = errors.New("invalid cost ch list")
|
||||
logger.Error("invalid cost ch list, orderId: %v, err: %v", orderId, err)
|
||||
return err
|
||||
}
|
||||
costCh := costChList[0]
|
||||
costChNew := &dbstruct.ConsumeHistory{
|
||||
Mid: goproto.Int64(costCh.GetMid()),
|
||||
Uid: goproto.Int64(costCh.GetUid()),
|
||||
Did: goproto.String(costCh.GetDid()),
|
||||
Type: goproto.Int32(costCh.GetType()),
|
||||
SType: goproto.Int32(dbstruct.CHSTypeCostRefundContactWechat),
|
||||
TypeId: goproto.String(costCh.GetTypeId()),
|
||||
OrderId: goproto.String(costCh.GetOrderId()),
|
||||
Change: goproto.Int64(-costCh.GetChange()),
|
||||
Ct: goproto.Int64(time.Now().Unix()),
|
||||
}
|
||||
err = v.store.CreateConsumeHistory(ctx, tx, costChNew)
|
||||
if err != nil {
|
||||
logger.Error("CreateConsumeHistory fail, costChNew: %v, err: %v", util.ToJson(costChNew), err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 给用户加回金币
|
||||
err = v.store.IncCoins(ctx, tx, mid, costCh.GetChange())
|
||||
if err != nil {
|
||||
logger.Error("IncCoins fail, mid: %v, change: %v, err: %v", mid, costCh.GetChange(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改金币订单状态 xx -> 已退款
|
||||
err = v.store.UpdateCoinOrderStatus(ctx, tx, orderId, dbstruct.VasCoinOrderStatusRefund)
|
||||
if err != nil {
|
||||
logger.Error("UpdateCoinOrderStatus fail, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除微信解锁记录
|
||||
err = v.store.DeleteUserVasUnlock(ctx, tx, mid, orderId, dbstruct.ProductIdContactWechat)
|
||||
if err != nil {
|
||||
logger.Error("UpdateCoinOrderStatus fail, mid: %v, orderId: %v, err: %v", mid, orderId, 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)
|
||||
}
|
||||
if len(incomeChList) > 1 {
|
||||
err = errors.New("收入记录错误,请找开发同学排查")
|
||||
return err
|
||||
}
|
||||
|
||||
// 有分成的情况
|
||||
if len(incomeChList) > 0 {
|
||||
ch := incomeChList[0]
|
||||
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.CHSTypeIncomeRefundContactWechat),
|
||||
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()),
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ func (s *Service) chListCost(ctx *gin.Context, chList []*dbstruct.ConsumeHistory
|
|||
if chDB.GetChange() < 0 {
|
||||
item.Change = fmt.Sprintf("%d金币", chDB.GetChange())
|
||||
}
|
||||
case dbstruct.CHSTypeCostRefund:
|
||||
case dbstruct.CHSTypeCostRefundContactWechat:
|
||||
item.Desc = fmt.Sprintf("购买\"%s\"微信退款", util.DerefString(acnt.Name))
|
||||
item.Change = changeMark + fmt.Sprintf("%d金币", chDB.GetChange())
|
||||
case dbstruct.CHSTypeCostMembership:
|
||||
|
@ -315,8 +315,11 @@ func (s *Service) chListIncome(ctx *gin.Context, chList []*dbstruct.ConsumeHisto
|
|||
case dbstruct.CHSTypeIncomeInvite:
|
||||
item.Desc = "邀请收益"
|
||||
item.Change = changeMark + fmt.Sprintf("%d钻石", chDB.GetChange())
|
||||
case dbstruct.CHSTypeIncomeRefund:
|
||||
item.Desc = "会员退款"
|
||||
case dbstruct.CHSTypeIncomeRefundMembership:
|
||||
item.Desc = "用户会员退款"
|
||||
item.Change = changeMark + fmt.Sprintf("%d钻石", chDB.GetChange())
|
||||
case dbstruct.CHSTypeIncomeRefundContactWechat:
|
||||
item.Desc = "用户微信退款"
|
||||
item.Change = changeMark + fmt.Sprintf("%d钻石", chDB.GetChange())
|
||||
}
|
||||
list = append(list, item)
|
||||
|
@ -546,3 +549,14 @@ func (s *Service) RefundOrder(ctx *gin.Context, req *vasproto.RefundOrderReq) (e
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) RefundCoinOrder(ctx *gin.Context, req *vasproto.RefundCoinOrderReq) (ec errcode.ErrCode, err error) {
|
||||
ec = errcode.ErrCodeVasSrvOk
|
||||
err = _DefaultVas.RefundCoinOrder(ctx, req)
|
||||
if err != nil {
|
||||
logger.Error("RefundCoinOrder fail, req: %v, err: %v", util.ToJson(req), err)
|
||||
ec = errcode.ErrCodeVasSrvFail
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -490,10 +490,10 @@ const (
|
|||
CHTypeIncome = 3 // 收入明细(钻石)
|
||||
CHTypeWithdraw = 4 // 提现明细(钻石)
|
||||
|
||||
CHSTypeCostContact = 10001 // 消费明细,联系方式
|
||||
CHSTypeCostRefund = 10002 // 消费明细,金币退款
|
||||
CHSTypeCostMembership = 10003 // 消费明细,会员资格解锁(伪金币记录,会员资格解锁中间无转金币过程)
|
||||
CHSTypeCostRefundMembership = 10004 // 消费明细,会员资格解锁退款(伪金币记录,会员资格解锁中间无转金币过程)
|
||||
CHSTypeCostContact = 10001 // 消费明细,联系方式
|
||||
CHSTypeCostRefundContactWechat = 10002 // 消费明细,微信联系方式退款
|
||||
CHSTypeCostMembership = 10003 // 消费明细,会员资格解锁(伪金币记录,会员资格解锁中间无转金币过程)
|
||||
CHSTypeCostRefundMembership = 10004 // 消费明细,会员资格解锁退款(伪金币记录,会员资格解锁中间无转金币过程)
|
||||
|
||||
CHSTypeChargeUser = 20001 // 充值明细,用户自己冲
|
||||
CHSTypeChargeOp = 20002 // 充值明细,OP充值
|
||||
|
@ -501,9 +501,10 @@ const (
|
|||
CHSTypeChargeRefundMembership = 20004 // 充值明细,会员退款
|
||||
CHSTypeChargeMembership = 20005 // 充值明细,会员充值
|
||||
|
||||
CHSTypeIncomeContact = 30001 // 收入明细,联系方式
|
||||
CHSTypeIncomeInvite = 30002 // 收入明细,邀请分成
|
||||
CHSTypeIncomeRefund = 30003 // 收入明细,退款
|
||||
CHSTypeIncomeContact = 30001 // 收入明细,联系方式
|
||||
CHSTypeIncomeInvite = 30002 // 收入明细,邀请分成
|
||||
CHSTypeIncomeRefundMembership = 30003 // 收入明细,会员退款
|
||||
CHSTypeIncomeRefundContactWechat = 30004 // 收入明细,微信退款
|
||||
|
||||
CHSTypeWithdrawDiamondAuto = 40001 // 自动提现明细
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue