1471 lines
45 KiB
Go
1471 lines
45 KiB
Go
package logic
|
||
|
||
import (
|
||
"database/sql"
|
||
"fmt"
|
||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||
"service/api/errs"
|
||
vasproto "service/api/proto/vas/proto"
|
||
zone_collaborator_proto "service/api/proto/zone_collaborator/proto"
|
||
"service/bizcommon/common"
|
||
"service/bizcommon/util"
|
||
"service/dbstruct"
|
||
"service/library/logger"
|
||
"service/library/redis"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/jmoiron/sqlx"
|
||
goproto "google.golang.org/protobuf/proto"
|
||
)
|
||
|
||
// 检查mid对空间的解锁是否存在
|
||
func (v *Vas) CheckZoneUnlockExist(ctx *gin.Context, tx *sqlx.Tx, mid, zid int64) (zu *dbstruct.ZoneUnlock, exist bool) {
|
||
if mid <= 0 {
|
||
return
|
||
}
|
||
zu, err := v.store.GetZoneUnlock(ctx, tx, mid, zid)
|
||
switch err {
|
||
case sql.ErrNoRows:
|
||
err = v.store.CreateZoneUnlock(ctx, tx, mid, zid)
|
||
if err != nil {
|
||
logger.Error("CreateZoneUnlock fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return
|
||
}
|
||
logger.Info("CreateZoneUnlock success, mid: %v, zid: %v", mid, zid)
|
||
zu, err = v.store.GetZoneUnlock(ctx, tx, mid, zid)
|
||
if err != nil {
|
||
logger.Error("GetWalletByMid after create fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return
|
||
}
|
||
case nil:
|
||
err = nil
|
||
default:
|
||
logger.Error("GetWalletByMid fail, mid: %v, err: %v", mid, err)
|
||
return
|
||
}
|
||
if zu == nil {
|
||
logger.Error("zu not exist, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return
|
||
}
|
||
exist = true
|
||
return
|
||
}
|
||
|
||
// 设置空间价格
|
||
func (v *Vas) UpdateZoneVasInfo(ctx *gin.Context, req *vasproto.UpdateZoneVasReq) error {
|
||
if req.AdmissionPrice <= 0 && req.IronfanshipPrice <= 0 && req.SuperfanshipPrice <= 0 {
|
||
return nil
|
||
}
|
||
err := v.store.UpsertZoneVas(ctx, req.ZoneVas)
|
||
return err
|
||
}
|
||
|
||
// 增加空间消费
|
||
func (v *Vas) IncZoneConsume(ctx *gin.Context, tx *sqlx.Tx, zid, mid, streamerMid, price int64, orderId, productId string) error {
|
||
if zid <= 0 && streamerMid > 0 {
|
||
// 获取空间id
|
||
zone, _ := v.zone.GetByMid(ctx, streamerMid)
|
||
if zone != nil {
|
||
zid = zone.GetId()
|
||
}
|
||
}
|
||
if zid <= 0 {
|
||
return fmt.Errorf("zone not exist, streamerMid: %v", streamerMid)
|
||
}
|
||
|
||
// 增加空间消费
|
||
err := v.store.IncZoneConsume(ctx, tx, mid, zid, price)
|
||
if err != nil {
|
||
logger.Error("IncZoneConsume fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
||
return err
|
||
}
|
||
|
||
// 增加空间消费记录
|
||
zch := &dbstruct.ZoneConsumeHis{
|
||
Mid: goproto.Int64(mid),
|
||
Zid: goproto.Int64(zid),
|
||
Consume: goproto.Int64(price),
|
||
OrderId: goproto.String(orderId),
|
||
ProductId: goproto.String(productId),
|
||
}
|
||
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) 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
|
||
}
|
||
|
||
// 是否不够解锁铁粉
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||
if zu == nil {
|
||
logger.Warn("CheckZoneUnlockExist fail, mid: %v, zid: %v", mid, zid)
|
||
return nil
|
||
}
|
||
zv, _ := v.store.GetZoneVasById(ctx, zid)
|
||
if zv == nil {
|
||
logger.Warn("GetZoneVasById fail, mid: %v, zid: %v", mid, zid)
|
||
return nil
|
||
}
|
||
if zu.IsUnlockIronfanship() && zu.GetConsume() < zv.IronfanshipPrice {
|
||
// 回滚超粉
|
||
err = v.store.RefundZoneIronfanship(ctx, tx, mid, zid)
|
||
if err != nil {
|
||
logger.Error("RefundZoneIronfanship fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 设置动态价格
|
||
func (v *Vas) UpdateZoneMomentPrice(ctx *gin.Context, req *vasproto.UpdateZoneMomentPriceReq) error {
|
||
if req.Price <= 0 {
|
||
return nil
|
||
}
|
||
err := v.store.UpsertZoneMomentPrice(ctx, req.ZoneMomentPrice)
|
||
return err
|
||
}
|
||
|
||
// 创建空间订单
|
||
func (v *Vas) ZoneCreateOrder(ctx *gin.Context, req *vasproto.ZoneCreateOrderReq) (*vasproto.ZoneCreateOrderData, error) {
|
||
var (
|
||
mid = req.Mid
|
||
zid = req.Zid
|
||
productId = req.ProductId
|
||
calcPrice = int64(0)
|
||
uid = int64(0)
|
||
oid2 = ""
|
||
)
|
||
|
||
switch productId {
|
||
case dbstruct.ProductIdH5ZoneMoment:
|
||
// 获取空间动态
|
||
mpr, err := v.store.GetZoneMomentPriceById(ctx, req.MomentId)
|
||
if err != nil {
|
||
logger.Error("GetZoneMomentPriceById fail, id: %v, err: %v", req.MomentId, err)
|
||
return nil, err
|
||
}
|
||
if mpr == nil {
|
||
err = fmt.Errorf("GetZoneMomentPriceById nil, id: %v", req.MomentId)
|
||
return nil, err
|
||
}
|
||
calcPrice = mpr.Price
|
||
uid = mpr.Mid
|
||
oid2 = fmt.Sprintf("%d", req.MomentId)
|
||
case dbstruct.ProductIdH5ZoneAdmission:
|
||
// 是否已开通会员,不让创建订单
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, nil, mid, zid)
|
||
if zu.IsUnlockAdmission() {
|
||
return nil, fmt.Errorf("您已开通该空间会员")
|
||
}
|
||
|
||
// 获取空间价格相关
|
||
zv, err := v.store.GetZoneVasById(ctx, req.Zid)
|
||
if err != nil {
|
||
logger.Error("GetZoneVasById fail, id: %v, err: %v", req.Zid, err)
|
||
return nil, err
|
||
}
|
||
if zv == nil {
|
||
err = fmt.Errorf("GetZoneVasById nil, id: %v", req.Zid)
|
||
return nil, err
|
||
}
|
||
|
||
calcPrice = zv.AdmissionPrice
|
||
uid = zv.Mid
|
||
case dbstruct.ProductIdH5ZoneSuperfanship:
|
||
// 是否已开通超粉,不让创建订单
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, nil, mid, zid)
|
||
if zu.IsUnlockSuperfanship() {
|
||
return nil, fmt.Errorf("您已开通该空间超粉")
|
||
}
|
||
|
||
// 获取空间价格相关
|
||
zv, err := v.store.GetZoneVasById(ctx, req.Zid)
|
||
if err != nil {
|
||
logger.Error("GetZoneVasById fail, id: %v, err: %v", req.Zid, err)
|
||
return nil, err
|
||
}
|
||
if zv == nil {
|
||
err = fmt.Errorf("GetZoneVasById nil, id: %v", req.Zid)
|
||
return nil, err
|
||
}
|
||
|
||
calcPrice = zv.SuperfanshipPrice
|
||
uid = zv.Mid
|
||
oid2 = fmt.Sprintf("%d", time.Now().Unix()+zv.GetSuperfanshipDurationSecond())
|
||
if zv.SuperfanshipValidPeriod == dbstruct.SuperfanshipValidPeriodEternal {
|
||
oid2 = "-1"
|
||
}
|
||
}
|
||
|
||
createOrderReq := &vasproto.CreateOrderReq{
|
||
BaseRequest: req.BaseRequest,
|
||
Ip: ctx.ClientIP(),
|
||
ProductId: productId,
|
||
PayType: req.PayType,
|
||
From: req.From,
|
||
WechatAuthCode: req.WechatAuthCode,
|
||
CalcPrice: calcPrice,
|
||
Uid: uid,
|
||
Oid1: fmt.Sprintf("%d", req.Zid),
|
||
Oid2: oid2,
|
||
RedirectUrl: req.RedirectUrl,
|
||
}
|
||
createOrderData, err := v.CreateOrder(ctx, createOrderReq)
|
||
if err != nil {
|
||
logger.Error("CreateOrder fail, err: %v", err)
|
||
return nil, err
|
||
}
|
||
data := &vasproto.ZoneCreateOrderData{
|
||
OrderId: createOrderData.OrderId,
|
||
AlipayParamStr: createOrderData.AlipayParamStr,
|
||
AlipayH5ParamStr: createOrderData.AlipayH5ParamStr,
|
||
WxpayNativeParamStr: createOrderData.WxpayNativeParamStr,
|
||
WxpayJsapiParamObj: createOrderData.WxpayJsapiParamObj,
|
||
WxpayH5ParamStr: createOrderData.WxpayH5ParamStr,
|
||
YeepayAlipayH5ParamStr: createOrderData.YeepayAlipayH5ParamStr,
|
||
YeepayWxpayH5ParamStr: createOrderData.YeepayWxpayH5ParamStr,
|
||
}
|
||
return data, nil
|
||
}
|
||
|
||
// 解锁空间动态
|
||
func (v *Vas) UnlockZoneMoment(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.Order) error {
|
||
if tx == nil {
|
||
err := fmt.Errorf("nil tx")
|
||
return err
|
||
}
|
||
|
||
var (
|
||
orderId = order.GetID()
|
||
mid = order.GetMid()
|
||
zid = order.GetZid()
|
||
momentId = order.GetMomentId()
|
||
)
|
||
|
||
// 解锁动态
|
||
err := v.store.UnlockZoneMoment(ctx, tx, mid, zid, momentId, orderId)
|
||
if err != nil {
|
||
logger.Error("UnlockZoneMoment fail, mid: %v, zid: %v, mmid: %v, orderId: %v, err: %v", mid, zid, momentId, orderId, err)
|
||
return err
|
||
}
|
||
|
||
// 计算收入
|
||
var totalDias = int64(float64(order.GetPayAmount()) / 100.0 * 10.0)
|
||
if order.GetPayType() == vasproto.PayTypeCoin {
|
||
totalDias = order.GetPayAmount()
|
||
}
|
||
incomeList, err := v.calcAndUpdateIncome(ctx, tx, order.GetUid(), mid, order.GetDid(), orderId, order.GetProductId(), totalDias, dbstruct.CHSTypeIncomeZoneStreamer)
|
||
if err != nil {
|
||
logger.Error("calcAndUpdateIncome fail, order: %v, err: %v", util.ToJson(order), err)
|
||
return err
|
||
}
|
||
|
||
// 把分成信息写到订单ext里面
|
||
orderExt := map[string]any{
|
||
"total_dias": totalDias,
|
||
"income_list": incomeList,
|
||
}
|
||
err = v.store.UpdateOrderExt(ctx, tx, orderId, util.ToJson(orderExt))
|
||
if err != nil {
|
||
logger.Error("UpdateOrderExt fail, order_id: %v, ext: %v, err: %v", orderId, util.ToJson(orderExt), err)
|
||
return err
|
||
}
|
||
|
||
// 增加空间消费
|
||
consume := order.GetPayAmount()
|
||
if order.GetPayType() == vasproto.PayTypeCoin {
|
||
consume = consume * 10
|
||
}
|
||
_err := v.IncZoneConsume(ctx, tx, zid, mid, 0, consume, orderId, order.GetProductId())
|
||
if _err != nil {
|
||
logger.Error("IncZoneConsume fail, mid: %v, zid: %v, mmid: %v, orderId: %v, err: %v", mid, zid, momentId, orderId, err)
|
||
}
|
||
|
||
// 动态购买信息
|
||
_err = v.store.IncZoneMomentBuyerCnt(ctx, zid, momentId)
|
||
if _err != nil {
|
||
logger.Error("IncZoneMomentBuyerCnt fail, zid: %v, momentId: %v, err: %v", zid, momentId, _err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 解锁空间会员
|
||
func (v *Vas) UnlockZoneAdmission(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.Order, unlockType int32) error {
|
||
if tx == nil {
|
||
err := fmt.Errorf("nil tx")
|
||
return err
|
||
}
|
||
|
||
var (
|
||
orderId = order.GetID()
|
||
mid = order.GetMid()
|
||
zid = order.GetZid()
|
||
)
|
||
|
||
// 解锁空间会员
|
||
err := v.MustUnlockAdmission(ctx, tx, mid, zid, -1, orderId, unlockType)
|
||
if err != nil {
|
||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
||
return err
|
||
}
|
||
|
||
// 获取空间解锁
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||
|
||
// 添加到空间成员
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeNormal)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
// 如果之前开通过铁粉,这次要添加到成员列表
|
||
if zu.IsUnlockIronfanship() {
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeIronfan)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
}
|
||
// 如果之前开通过超粉,这次要添加到成员列表
|
||
if zu.IsUnlockSuperfanship() {
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeSuperfan)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
}
|
||
|
||
// 计算收入
|
||
var totalDias = int64(float64(order.GetPayAmount()) / 100.0 * 10.0)
|
||
if order.GetPayType() == vasproto.PayTypeCoin {
|
||
totalDias = order.GetPayAmount()
|
||
}
|
||
incomeList, err := v.calcAndUpdateIncome(ctx, tx, order.GetUid(), mid, order.GetDid(), orderId, order.GetProductId(), totalDias, dbstruct.CHSTypeIncomeZoneStreamer)
|
||
if err != nil {
|
||
logger.Error("calcAndUpdateIncome fail, order: %v, err: %v", util.ToJson(order), err)
|
||
return err
|
||
}
|
||
|
||
// 把分成信息写到订单ext里面
|
||
orderExt := map[string]any{
|
||
"total_dias": totalDias,
|
||
"income_list": incomeList,
|
||
}
|
||
err = v.store.UpdateOrderExt(ctx, tx, orderId, util.ToJson(orderExt))
|
||
if err != nil {
|
||
logger.Error("UpdateOrderExt fail, order_id: %v, ext: %v, err: %v", orderId, util.ToJson(orderExt), err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 解锁空间超粉
|
||
func (v *Vas) UnlockZoneSuperfanship(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.Order, unlockType int32) error {
|
||
if tx == nil {
|
||
err := fmt.Errorf("nil tx")
|
||
return err
|
||
}
|
||
|
||
var (
|
||
orderId = order.GetID()
|
||
mid = order.GetMid()
|
||
zid = order.GetZid()
|
||
)
|
||
|
||
// 解锁空间超粉
|
||
err := v.MustUnlockSuperfanship(ctx, tx, mid, zid, order.GetSuperfanshipUntil(), orderId, unlockType)
|
||
if err != nil {
|
||
logger.Error("MustUnlockSuperfanship fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
||
return err
|
||
}
|
||
|
||
// 添加到空间成员
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeSuperfan)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember superfan fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
// 计算收入
|
||
var totalDias = int64(float64(order.GetPayAmount()) / 100.0 * 10.0)
|
||
if order.GetPayType() == vasproto.PayTypeCoin {
|
||
totalDias = order.GetPayAmount()
|
||
}
|
||
incomeList, err := v.calcAndUpdateIncome(ctx, tx, order.GetUid(), mid, order.GetDid(), orderId, order.GetProductId(), totalDias, dbstruct.CHSTypeIncomeZoneStreamer)
|
||
if err != nil {
|
||
logger.Error("calcAndUpdateIncome fail, order: %v, err: %v", util.ToJson(order), err)
|
||
return err
|
||
}
|
||
|
||
// 把分成信息写到订单ext里面
|
||
orderExt := map[string]any{
|
||
"total_dias": totalDias,
|
||
"income_list": incomeList,
|
||
}
|
||
err = v.store.UpdateOrderExt(ctx, tx, orderId, util.ToJson(orderExt))
|
||
if err != nil {
|
||
logger.Error("UpdateOrderExt fail, order_id: %v, ext: %v, err: %v", orderId, util.ToJson(orderExt), err)
|
||
return err
|
||
}
|
||
|
||
// 增加空间消费
|
||
consume := order.GetPayAmount()
|
||
if order.GetPayType() == vasproto.PayTypeCoin {
|
||
consume = consume * 10
|
||
}
|
||
_err := v.IncZoneConsume(ctx, tx, zid, mid, 0, consume, orderId, order.GetProductId())
|
||
if _err != nil {
|
||
logger.Error("IncZoneConsume fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
||
}
|
||
|
||
// 超粉赠送微信
|
||
zv, _ := v.store.GetZoneVasById(ctx, zid)
|
||
if zv.IsSuperfanshipGiveWechat != 1 {
|
||
return nil
|
||
}
|
||
_err = v.SuperfanUnlockContact(ctx, tx, mid, zv.Mid, orderId)
|
||
if _err != nil {
|
||
logger.Error("SuperfanUnlockContact fail, mid: %v, zid: %v, uid: %v, orderId: %v, err: %v", mid, zid, zv.Mid, orderId, _err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 满足消费额解锁空间铁粉
|
||
func (v *Vas) UnlockZoneIronfanshipReachConsume(ctx *gin.Context, tx *sqlx.Tx, mid, zid, streamerMid int64) error {
|
||
if tx == nil {
|
||
err := fmt.Errorf("nil tx")
|
||
return err
|
||
}
|
||
|
||
// 获取zid
|
||
if zid <= 0 && streamerMid > 0 {
|
||
// 获取空间id
|
||
zone, _ := v.zone.GetByMid(ctx, streamerMid)
|
||
if zone != nil {
|
||
zid = zone.GetId()
|
||
}
|
||
}
|
||
if zid <= 0 {
|
||
return fmt.Errorf("zone not exist, mid: %v, streamerMid: %v", mid, streamerMid)
|
||
}
|
||
|
||
// 获取空间价格
|
||
zVas, _ := v.store.GetZoneVasById(ctx, zid)
|
||
if zVas == nil {
|
||
return fmt.Errorf("zone vas not exist, zid: %v", zid)
|
||
}
|
||
|
||
// 获取空间消费信息
|
||
zUnlock, exists := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||
if !exists {
|
||
return fmt.Errorf("zone unlock not exist, mid: %v, zid: %v", mid, zid)
|
||
}
|
||
|
||
// 消费额是否达到了解锁铁粉的消费额
|
||
if zUnlock.GetConsume() < zVas.IronfanshipPrice {
|
||
return fmt.Errorf("not reach ironfan unlock consume, mid: %v, zid: %v", mid, zid)
|
||
}
|
||
|
||
// 解锁铁粉
|
||
err := v.MustUnlockIronfanship(ctx, tx, mid, zid, -1, "ironfan", dbstruct.ZoneUnlockTypeReachConsume)
|
||
if err != nil {
|
||
logger.Error("MustUnlockIronfanship fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
// 添加到空间成员
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeIronfan)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
type IncomeInfo struct {
|
||
Mid int64 `json:"mid"` // mid
|
||
IncomeSType int32 `json:"income_stype"` // 收入类类型
|
||
Rate float64 `json:"rate"` // 收入比例
|
||
Dias int64 `json:"dias"` // 收入钻石
|
||
SettingRate float64 `json:"setting_rate"` // 设置的收入比例
|
||
}
|
||
|
||
func (v *Vas) calcAndUpdateIncome(ctx *gin.Context, tx *sqlx.Tx, streamerMid, uid int64, did, orderId, typeId string, totalDias int64, defaultIncomeSType int32) ([]IncomeInfo, error) {
|
||
// 没有主播mid,则不给主播分成(会员)
|
||
if streamerMid == 0 {
|
||
// 官方
|
||
chOfficial := &dbstruct.ConsumeHistory{
|
||
Mid: goproto.Int64(common.OfficialMid),
|
||
Uid: goproto.Int64(uid),
|
||
Did: goproto.String(did),
|
||
Type: goproto.Int32(dbstruct.CHTypeIncome),
|
||
SType: goproto.Int32(dbstruct.CHSTypeIncomeContact),
|
||
TypeId: goproto.String(typeId),
|
||
OrderId: goproto.String(orderId),
|
||
Change: goproto.Int64(totalDias),
|
||
Ct: goproto.Int64(time.Now().Unix()),
|
||
}
|
||
err := v.store.CreateConsumeHistory(ctx, tx, chOfficial)
|
||
if err != nil {
|
||
logger.Error("CreateConsumeHistory fail, ch: %v, err: %v", util.ToJson(chOfficial), err)
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
err = v.store.IncDiamonds(ctx, tx, common.OfficialMid, totalDias)
|
||
if err != nil {
|
||
logger.Error("IncDiamonds fail, official, mid: %v, dias: %v, err: %v", common.OfficialMid, totalDias, err)
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
|
||
list := []IncomeInfo{
|
||
{
|
||
Mid: common.OfficialMid,
|
||
IncomeSType: dbstruct.CHSTypeIncomeMembership,
|
||
Rate: 1.0,
|
||
Dias: totalDias,
|
||
},
|
||
}
|
||
return list, nil
|
||
}
|
||
|
||
// 获取主播
|
||
streamerUserId := int64(0)
|
||
acntMap, _ := v.account.GetAccountMapByMids(ctx, []int64{streamerMid})
|
||
if acnt, ok := acntMap[streamerMid]; ok {
|
||
streamerUserId = acnt.GetUserId()
|
||
}
|
||
|
||
// 获取主播空间
|
||
zone, err := v.zone.GetByMid(ctx, streamerMid)
|
||
if err != nil {
|
||
logger.Error("zone.GetByMid fail, mid: %v, err: %v", streamerMid, err)
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
logger.Info("_IL orderId: %v, zone: %v", orderId, util.ToJson(zone))
|
||
|
||
// 获取代运营
|
||
zid := zone.GetId()
|
||
tp, err := v.zonetp.GetZoneThirdPartnerByZid(ctx, zid)
|
||
if err != nil {
|
||
logger.Error("zonetp.GetZoneThirdPartnerByZid fail, zid: %v, err: %v", zid, err)
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
logger.Info("_IL orderId: %v, zid: %v, tp: %v", orderId, zid, util.ToJson(tp))
|
||
|
||
// 获取协作者
|
||
zclrList, err := v.zoneclr.OpList(ctx, &zone_collaborator_proto.OpListReq{
|
||
Zid: goproto.Int64(zid),
|
||
Offset: 0,
|
||
Limit: 100000000,
|
||
})
|
||
if err != nil {
|
||
logger.Error("zoneclr.OpList fail, zid: %v, err: %v", zid, err)
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
logger.Info("_IL orderId: %v, zid: %v, zclrList: %v", orderId, zid, util.ToJson(zclrList))
|
||
|
||
var list = make([]IncomeInfo, 0)
|
||
switch {
|
||
case zone == nil:
|
||
// 没有空间
|
||
logger.Info("_IL (1) orderId: %v, zid: %v", orderId, zid)
|
||
list = v._calcWithoutZone(streamerMid, totalDias, defaultIncomeSType)
|
||
case zone != nil && tp == nil:
|
||
// 有空间,没代运营
|
||
logger.Info("_IL (2) orderId: %v, zid: %v", orderId, zid)
|
||
list = v._calcWithZoneWithoutTp(streamerMid, totalDias, defaultIncomeSType)
|
||
case zone != nil && tp != nil:
|
||
// 有空间,有代运营
|
||
logger.Info("_IL (3) orderId: %v, zid: %v", orderId, zid)
|
||
list = v._calcWithZoneWithTp(streamerMid, totalDias, defaultIncomeSType, tp, zclrList)
|
||
}
|
||
logger.Info("_IL incomeList: %v", util.ToJson(list))
|
||
|
||
// 写记录
|
||
for _, ii := range list {
|
||
// 写消费记录
|
||
wallet, _ := v.CheckWalletExist(ctx, tx, ii.Mid)
|
||
if wallet == nil {
|
||
logger.Error("CheckWalletExist fail, mid: %v", ii.Mid)
|
||
err = errs.ErrVasWalletNotExist
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
ch := &dbstruct.ConsumeHistory{
|
||
Mid: goproto.Int64(ii.Mid),
|
||
Uid: goproto.Int64(streamerUserId),
|
||
Did: goproto.String(did),
|
||
Type: goproto.Int32(dbstruct.CHTypeIncome),
|
||
SType: goproto.Int32(ii.IncomeSType),
|
||
TypeId: goproto.String(typeId),
|
||
OrderId: goproto.String(orderId),
|
||
Change: goproto.Int64(ii.Dias),
|
||
Before: goproto.Int64(wallet.GetDiamonds()),
|
||
After: goproto.Int64(wallet.GetDiamonds() + ii.Dias),
|
||
Count: goproto.Int64(ii.Dias),
|
||
Ct: goproto.Int64(time.Now().Unix()),
|
||
}
|
||
err = v.store.CreateConsumeHistory(ctx, tx, ch)
|
||
if err != nil {
|
||
logger.Error("CreateConsumeHistory fail, ch: %v, err: %v", util.ToJson(ch), err)
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
|
||
// 增加钻石
|
||
err = v.store.IncDiamonds(ctx, tx, ii.Mid, ii.Dias)
|
||
if err != nil {
|
||
logger.Error("IncDiamonds fail, ch: %v, err: %v", util.ToJson(ch), err)
|
||
return make([]IncomeInfo, 0), err
|
||
}
|
||
}
|
||
return list, nil
|
||
}
|
||
|
||
// 没空间的情况
|
||
func (v *Vas) _calcWithoutZone(streamerMid, totalDias int64, defaultIncomeSType int32) []IncomeInfo {
|
||
list := make([]IncomeInfo, 0)
|
||
|
||
// 官方
|
||
officialDias := int64(float64(totalDias) * 0.2)
|
||
list = append(list, IncomeInfo{
|
||
Mid: common.OfficialMid,
|
||
IncomeSType: defaultIncomeSType,
|
||
Rate: 0.2,
|
||
Dias: officialDias,
|
||
})
|
||
|
||
// 主播
|
||
list = append(list, IncomeInfo{
|
||
Mid: streamerMid,
|
||
IncomeSType: defaultIncomeSType,
|
||
Rate: 0.8,
|
||
Dias: totalDias - officialDias,
|
||
})
|
||
|
||
return list
|
||
}
|
||
|
||
// 有空间,没代运营
|
||
func (v *Vas) _calcWithZoneWithoutTp(streamerMid, totalDias int64, defaultIncomeSType int32) []IncomeInfo {
|
||
list := make([]IncomeInfo, 0)
|
||
|
||
// 官方
|
||
officialDias := int64(float64(totalDias) * 0.2)
|
||
list = append(list, IncomeInfo{
|
||
Mid: common.OfficialMid,
|
||
IncomeSType: defaultIncomeSType,
|
||
Rate: 0.2,
|
||
Dias: officialDias,
|
||
})
|
||
|
||
// 主播
|
||
list = append(list, IncomeInfo{
|
||
Mid: streamerMid,
|
||
IncomeSType: defaultIncomeSType,
|
||
Rate: 0.8,
|
||
Dias: totalDias - officialDias,
|
||
})
|
||
|
||
return list
|
||
}
|
||
|
||
// 有空间,有代运营
|
||
func (v *Vas) _calcWithZoneWithTp(streamerMid, totalDias int64, defaultIncomeSType int32, ztp *dbstruct.ZoneThirdPartner, zclList []*dbstruct.ZoneCollaborator) []IncomeInfo {
|
||
list := make([]IncomeInfo, 0)
|
||
leftDias := totalDias
|
||
|
||
// 官方
|
||
officialDias := int64(float64(totalDias) * 0.2)
|
||
list = append(list, IncomeInfo{
|
||
Mid: common.OfficialMid,
|
||
IncomeSType: defaultIncomeSType,
|
||
Rate: 0.2,
|
||
Dias: officialDias,
|
||
SettingRate: 0.2,
|
||
})
|
||
leftDias -= officialDias
|
||
|
||
// 代运营+协作者真正分成rate
|
||
partsRate := 0.8 * ztp.GetSharingRatio()
|
||
ztpRate := partsRate
|
||
|
||
// 主播
|
||
streamerRate := 0.8 - partsRate
|
||
streamerDias := int64(float64(totalDias) * streamerRate)
|
||
list = append(list, IncomeInfo{
|
||
Mid: streamerMid,
|
||
IncomeSType: defaultIncomeSType,
|
||
Rate: streamerRate,
|
||
Dias: streamerDias,
|
||
SettingRate: 1.0 - ztp.GetSharingRatio(),
|
||
})
|
||
leftDias -= streamerDias
|
||
|
||
// 协作者
|
||
for _, zcl := range zclList {
|
||
zclRate := 0.8 * zcl.GetSharingRatio()
|
||
zclDias := int64(float64(totalDias) * zclRate)
|
||
list = append(list, IncomeInfo{
|
||
Mid: zcl.GetCollaboratorMid(),
|
||
IncomeSType: dbstruct.CHSTypeIncomeCollaborator,
|
||
Rate: zclRate,
|
||
Dias: zclDias,
|
||
SettingRate: zcl.GetSharingRatio(),
|
||
})
|
||
leftDias -= zclDias
|
||
ztpRate -= zclRate
|
||
}
|
||
|
||
// 代运营
|
||
ztpDias := int64(float64(totalDias) * ztpRate)
|
||
list = append(list, IncomeInfo{
|
||
Mid: ztp.GetThirdPartnerMid(),
|
||
IncomeSType: dbstruct.CHSTypeIncomeThirdPartner,
|
||
Rate: ztpRate,
|
||
Dias: ztpDias,
|
||
SettingRate: ztp.GetSharingRatio(),
|
||
})
|
||
leftDias -= ztpDias
|
||
|
||
// 如果剩余钻石,加到官方上
|
||
list = append(list, IncomeInfo{
|
||
Mid: common.OfficialMid,
|
||
IncomeSType: defaultIncomeSType,
|
||
Rate: 0,
|
||
Dias: leftDias,
|
||
})
|
||
|
||
return list
|
||
}
|
||
|
||
// 空间增值价格信息
|
||
func (v *Vas) GetZoneVasInfo(ctx *gin.Context, zid int64) (uVas *dbstruct.ZoneVas, err error) {
|
||
uVas, err = v.store.GetZoneVasById(ctx, zid)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
// 付费动态价格
|
||
func (v *Vas) GetZoneMomentPriceById(ctx *gin.Context, momentId int64) (zmp *dbstruct.ZoneMomentPrice, err error) {
|
||
zmp, err = v.store.GetZoneMomentPriceById(ctx, momentId)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
// 批量获取付费动态价格,返回值: key: moment_id, value: moment价格信息
|
||
func (v *Vas) GetZoneMomentPriceByIds(ctx *gin.Context, momentIds []int64) (zmpMap map[int64]*dbstruct.ZoneMomentPrice, err error) {
|
||
zmpMap = make(map[int64]*dbstruct.ZoneMomentPrice)
|
||
list, err := v.store.GetZoneMomentPriceByIds(ctx, momentIds)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, v := range list {
|
||
zmpMap[v.MomentId] = v
|
||
}
|
||
return
|
||
}
|
||
|
||
// 批量获取空间价格信息,返回值: key: zid, value: 空间价格信息
|
||
func (v *Vas) GetZoneVasByIds(ctx *gin.Context, zids []int64) (zvMap map[int64]*dbstruct.ZoneVas, err error) {
|
||
zvMap = make(map[int64]*dbstruct.ZoneVas)
|
||
list, err := v.store.GetZoneVasByIds(ctx, zids)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, v := range list {
|
||
zvMap[v.Zid] = v
|
||
}
|
||
return
|
||
}
|
||
|
||
// 批量获取付费动态数据,返回值: key: moment_id, value: moment数据信息
|
||
func (v *Vas) GetZoneMomentStatByIds(ctx *gin.Context, momentIds []int64) (midZmsMap map[int64]*dbstruct.ZoneMomentStat, err error) {
|
||
midZmsMap = make(map[int64]*dbstruct.ZoneMomentStat)
|
||
list, err := v.store.GetZoneMomentStatByIds(ctx, momentIds)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, v := range list {
|
||
midZmsMap[v.MomentId] = v
|
||
}
|
||
return
|
||
}
|
||
|
||
// 获取用户已解锁的空间的信息,返回值:key: zid, value: 解锁信息
|
||
func (v *Vas) GetZoneUnlockMapByMid(ctx *gin.Context, mid int64) (zidZuMap map[int64]*dbstruct.ZoneUnlock, err error) {
|
||
zidZuMap = make(map[int64]*dbstruct.ZoneUnlock)
|
||
list, err := v.store.GetZoneUnlockListByMid(ctx, nil, mid)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, v := range list {
|
||
if !v.IsUnlockAdmission() {
|
||
continue
|
||
}
|
||
zidZuMap[v.GetZid()] = v
|
||
}
|
||
logger.Info("GetZoneUnlockMapByMid, zidZuMap: %v", util.ToJson(zidZuMap))
|
||
return
|
||
}
|
||
|
||
// 获取空间解锁人的信息,返回值:key: mid, value: 解锁信息
|
||
func (v *Vas) GetZoneUnlockMapByZid(ctx *gin.Context, zid int64) (midZuMap map[int64]*dbstruct.ZoneUnlock, err error) {
|
||
midZuMap = make(map[int64]*dbstruct.ZoneUnlock)
|
||
list, err := v.store.GetZoneUnlockListByZid(ctx, nil, zid)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, v := range list {
|
||
midZuMap[v.GetMid()] = v
|
||
}
|
||
return
|
||
}
|
||
|
||
// 获取mid对空间解锁信息,返回值:key: zid, value: 解锁信息
|
||
func (v *Vas) GetZoneUnlockMapByMidZids(ctx *gin.Context, mid int64, zids []int64) (zidZuMap map[int64]*dbstruct.ZoneUnlock, err error) {
|
||
zidZuMap = make(map[int64]*dbstruct.ZoneUnlock)
|
||
list, err := v.store.GetZoneUnlockListByMidZids(ctx, nil, mid, zids)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, v := range list {
|
||
zidZuMap[v.GetZid()] = v
|
||
}
|
||
return
|
||
}
|
||
|
||
// 获取mid对动态解锁信息 by mid, momentIds,返回值:key: moment_id, value: 解锁信息
|
||
func (v *Vas) GetZoneMomentUnlockMapByMidMomentIds(ctx *gin.Context, mid int64, momentIds []int64) (momentIdZmuMap map[int64]*dbstruct.ZoneMomentUnlock, err error) {
|
||
momentIdZmuMap = make(map[int64]*dbstruct.ZoneMomentUnlock)
|
||
list, err := v.store.GetZoneMomentUnlockListByMidMomentIds(ctx, nil, mid, momentIds)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, v := range list {
|
||
momentIdZmuMap[v.GetMomentId()] = v
|
||
}
|
||
return
|
||
}
|
||
|
||
// 空间退款页面
|
||
func (v *Vas) ZoneGetRefundPage(ctx *gin.Context, req *vasproto.ZoneRefundPageReq) (string, int64, string, error) {
|
||
zv, err := v.store.GetZoneUnlock(ctx, nil, req.Mid, req.Zid)
|
||
if err == sql.ErrNoRows || zv == nil {
|
||
logger.Error("no zone unlock info, mid: %v, zid: %v", req.Mid, req.Zid)
|
||
return "", 0, "", fmt.Errorf("没有解锁信息")
|
||
}
|
||
if err != nil {
|
||
logger.Error("get zone unlock info fail, mid: %v, zid: %v, err: %v", req.Mid, req.Zid, err)
|
||
return "", 0, "", err
|
||
}
|
||
|
||
// 判断
|
||
if zv.GetAdmissionUntil() == 0 || len(zv.GetAdmissionOrderId()) <= 0 {
|
||
err = fmt.Errorf("无空间解锁记录")
|
||
logger.Error("no zone unlock info, mid: %v, zid: %v", req.Mid, req.Zid)
|
||
return "", 0, "", err
|
||
}
|
||
|
||
// 获取订单
|
||
order, err := v.store.GetOrderById(ctx, nil, zv.GetAdmissionOrderId())
|
||
if err == sql.ErrNoRows || order == nil {
|
||
logger.Error("no zone admission order info, mid: %v, zid: %v, orderId: %v", req.Mid, req.Zid, zv.GetAdmissionOrderId())
|
||
return "", 0, "", fmt.Errorf("没有相关订单")
|
||
}
|
||
if err != nil {
|
||
logger.Error("get zone admission order info fail, mid: %v, zid: %v, orderId: %v, err: %v", req.Mid, req.Zid, zv.GetAdmissionOrderId(), err)
|
||
return "", 0, "", err
|
||
}
|
||
|
||
return "空间成员权限", order.GetPayAmount(), order.GetPayType(), nil
|
||
}
|
||
|
||
// 空间退款
|
||
func getCheckZoneRefundFreqKey(mid int64) string {
|
||
return fmt.Sprintf("zone_refund_freq_%d", mid)
|
||
}
|
||
|
||
func (v *Vas) ZoneRefund(ctx *gin.Context, req *vasproto.ZoneRefundReq) error {
|
||
redisKey := getCheckZoneRefundFreqKey(req.Mid)
|
||
redisCli := redis.GetRedisClient()
|
||
// 获取上次退款时间
|
||
t, _ := redisCli.GetInt64(redisKey)
|
||
logger.Info("ZoneRefund check freq, key: %v, t: %v", redisKey, t)
|
||
if time.Now().Unix()-t < 3600*12 {
|
||
return fmt.Errorf("12小时内您最多只能进行1次退款")
|
||
}
|
||
// redis记录退款时间
|
||
_ = redisCli.Set(redisKey, time.Now().Unix(), 86400)
|
||
|
||
// 获取解锁信息
|
||
zv, err := v.store.GetZoneUnlock(ctx, nil, req.Mid, req.Zid)
|
||
if err == sql.ErrNoRows || zv == nil {
|
||
logger.Error("no zone unlock info, mid: %v, zid: %v", req.Mid, req.Zid)
|
||
return fmt.Errorf("没有解锁信息")
|
||
}
|
||
if err != nil {
|
||
logger.Error("get zone unlock info fail, mid: %v, zid: %v, err: %v", req.Mid, req.Zid, err)
|
||
return err
|
||
}
|
||
|
||
// 判断
|
||
if zv.GetAdmissionUntil() == 0 || len(zv.GetAdmissionOrderId()) <= 0 {
|
||
err = fmt.Errorf("无空间解锁记录")
|
||
logger.Error("no zone unlock info, mid: %v, zid: %v", req.Mid, req.Zid)
|
||
return err
|
||
}
|
||
|
||
// 获取订单
|
||
order, err := v.store.GetOrderById(ctx, nil, zv.GetAdmissionOrderId())
|
||
if err == sql.ErrNoRows || order == nil {
|
||
logger.Error("no zone admission order info, mid: %v, zid: %v, orderId: %v", req.Mid, req.Zid, zv.GetAdmissionOrderId())
|
||
return fmt.Errorf("没有相关订单")
|
||
}
|
||
if err != nil {
|
||
logger.Error("get zone admission order info fail, mid: %v, zid: %v, orderId: %v, err: %v", req.Mid, req.Zid, zv.GetAdmissionOrderId(), err)
|
||
return err
|
||
}
|
||
//if order.GetOid3() == alipaycli.AppIdMiYuanTianShi {
|
||
// return fmt.Errorf("订单正在处理,请24小时后重试")
|
||
//}
|
||
|
||
// 退款
|
||
err = v.RefundOrder(ctx, &vasproto.RefundOrderReq{
|
||
OrderId: order.GetID(),
|
||
Operator: fmt.Sprintf("%d", req.Mid),
|
||
}, vasproto.NewRefundOrderOpt().SetZoneRefundReq(req))
|
||
if err != nil {
|
||
logger.Error("RefundOrder fail, mid: %v, zid: %v, orderId: %v, err: %v", req.Mid, req.Zid, order.GetID(), err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 退款接口 v2
|
||
func (v *Vas) ZoneRefundV2(ctx *gin.Context, req *vasproto.ZoneRefundReq) error {
|
||
redisKey := getCheckZoneRefundFreqKey(req.Mid)
|
||
redisCli := redis.GetRedisClient()
|
||
// 获取上次退款时间
|
||
t, _ := redisCli.GetInt64(redisKey)
|
||
logger.Info("ZoneRefundV2 check freq, key: %v, t: %v", redisKey, t)
|
||
if time.Now().Unix()-t < 3600*12 {
|
||
return fmt.Errorf("12小时内您最多只能进行1次退款")
|
||
}
|
||
// redis记录退款时间
|
||
_ = redisCli.Set(redisKey, time.Now().Unix(), 86400)
|
||
|
||
// 获取解锁信息
|
||
zv, err := v.store.GetZoneUnlock(ctx, nil, req.Mid, req.Zid)
|
||
if err == sql.ErrNoRows || zv == nil {
|
||
logger.Error("ZoneRefundV2 no zone unlock info, mid: %v, zid: %v", req.Mid, req.Zid)
|
||
return fmt.Errorf("没有解锁信息")
|
||
}
|
||
if err != nil {
|
||
logger.Error("ZoneRefundV2 get zone unlock info fail, mid: %v, zid: %v, err: %v", req.Mid, req.Zid, err)
|
||
return err
|
||
}
|
||
|
||
// 判断
|
||
if zv.GetAdmissionUntil() == 0 || len(zv.GetAdmissionOrderId()) <= 0 {
|
||
err = fmt.Errorf("无空间解锁记录")
|
||
logger.Error("ZoneRefundV2 no zone unlock info, mid: %v, zid: %v", req.Mid, req.Zid)
|
||
return err
|
||
}
|
||
|
||
// 获取订单
|
||
order, err := v.store.GetOrderById(ctx, nil, zv.GetAdmissionOrderId())
|
||
if err == sql.ErrNoRows || order == nil {
|
||
logger.Error("ZoneRefundV2 no zone admission order info, mid: %v, zid: %v, orderId: %v", req.Mid, req.Zid, zv.GetAdmissionOrderId())
|
||
return fmt.Errorf("没有相关订单")
|
||
}
|
||
if err != nil {
|
||
logger.Error("ZoneRefundV2 get zone admission order info fail, mid: %v, zid: %v, orderId: %v, err: %v", req.Mid, req.Zid, zv.GetAdmissionOrderId(), err)
|
||
return err
|
||
}
|
||
//if order.GetOid3() == alipaycli.AppIdMiYuanTianShi {
|
||
// return fmt.Errorf("订单正在处理,请24小时后重试")
|
||
//}
|
||
|
||
// [0,2]: 直接退款
|
||
// (2,24]: 主播审核,发送消息通知
|
||
// (24, ♾️): 不支持页面退款,需要客服处理
|
||
refundsStatus := dbstruct.Refunds_Awaiting
|
||
timeInterval := time.Now().Unix() - order.GetCt()
|
||
if timeInterval >= 0 && timeInterval <= 2*3600 {
|
||
// 退款
|
||
err = v.RefundOrder(ctx, &vasproto.RefundOrderReq{
|
||
OrderId: order.GetID(),
|
||
Operator: fmt.Sprintf("%d", req.Mid),
|
||
}, vasproto.NewRefundOrderOpt().SetZoneRefundReq(req))
|
||
if err != nil {
|
||
logger.Error("ZoneRefundV2 RefundOrder fail, mid: %v, zid: %v, orderId: %v, err: %v", req.Mid, req.Zid, order.GetID(), err)
|
||
return err
|
||
}
|
||
refundsStatus = dbstruct.Refunds_Automatic
|
||
|
||
} else if timeInterval > 2*3600 && timeInterval <= 24*3600 {
|
||
// refundsStatus = dbstruct.Refunds_Awaiting
|
||
// 发送主播审核消息
|
||
|
||
} else {
|
||
refundsStatus = dbstruct.Refunds_Prohibit
|
||
}
|
||
|
||
refund := &dbstruct.RefundInfo{
|
||
AuditId: primitive.NewObjectID().Hex(),
|
||
Zid: req.Zid,
|
||
Mid: req.Mid,
|
||
ByTime: order.GetCt(),
|
||
RefundT: time.Now().Unix(),
|
||
RefundsStatus: int64(refundsStatus),
|
||
ContactName: req.ContactName,
|
||
ContactPhone: req.ContactPhone,
|
||
Note: req.Note,
|
||
Ct: time.Now().Unix(),
|
||
Ut: time.Now().Unix(),
|
||
}
|
||
|
||
if order.GetPayType() == vasproto.PayTypeCoin {
|
||
refund.CoinPrice = order.GetPayAmount()
|
||
} else {
|
||
refund.Price = order.GetPayAmount()
|
||
}
|
||
|
||
err = v.store.AddZoneRefundAutomatic(ctx, refund)
|
||
if err != nil {
|
||
logger.Error("ZoneRefundV2 AddZoneRefundAutomatic fail, mid: %v, zid: %v, orderId: %v, err: %v", req.Mid, req.Zid, order.GetID(), err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 退款审核列表页
|
||
func (v *Vas) ZoneRefundList(ctx *gin.Context, req *vasproto.ZoneRefundListReq) (list []*dbstruct.RefundInfo, err error) {
|
||
refundsStatusList := make([]int64, 0)
|
||
if req.AuditType == 1 {
|
||
refundsStatusList = append(refundsStatusList, dbstruct.Refunds_Awaiting)
|
||
} else if req.AuditType == 2 {
|
||
refundsStatusList = append(refundsStatusList, dbstruct.Refunds_Approved, dbstruct.Refunds_Rejected, dbstruct.Refunds_Overtime)
|
||
}
|
||
list, err = v.store.GetZoneRefundList(ctx, req.Zid, refundsStatusList)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
// 退款审核详情页
|
||
func (v *Vas) ZoneRefundInfo(ctx *gin.Context, req *vasproto.ZoneRefundInfoReq) (info *dbstruct.RefundInfo, err error) {
|
||
info, err = v.store.GetZoneRefundInfo(ctx, req.AuditId)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
// 退款主播审核
|
||
func (v *Vas) ZoneRefundAudit(ctx *gin.Context, req *vasproto.ZoneRefundAuditReq) error {
|
||
err := v.store.SetZoneRefundAuditInfo(ctx, req.AuditId, req.RefundsStatus)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 空间成员列表
|
||
func (v *Vas) GetZoneMemberList(ctx *gin.Context, zid int64, memType int32) (list []*dbstruct.ZoneMember, err error) {
|
||
list, err = v.store.GetZoneMemberList(ctx, nil, zid, memType)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
// 空间成员列表
|
||
func (v *Vas) GetZoneMemberListV2(ctx *gin.Context, zid, offset, limit int64, memType int32) (list []*dbstruct.ZoneMember, err error) {
|
||
list, err = v.store.GetZoneMemberListV2(ctx, nil, zid, offset, limit, memType)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
// 空间成员列表
|
||
func (v *Vas) GetZoneMemberCnt(ctx *gin.Context, zid int64, memType int32) (cnt int64, err error) {
|
||
cnt, err = v.store.GetZoneMemberCnt(ctx, nil, zid, memType)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
// 搜索空间成员
|
||
func (v *Vas) SearchZoneMember(ctx *gin.Context, zid, memMid int64) (list []*dbstruct.ZoneMember, err error) {
|
||
list, err = v.store.SearchZoneMember(ctx, nil, zid, memMid)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return
|
||
}
|
||
|
||
func (v *Vas) MustUnlockAdmission(ctx *gin.Context, tx *sqlx.Tx, mid, zid, until int64, orderId string, unlockType int32) error {
|
||
// 检查解锁
|
||
_, exist := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||
if !exist {
|
||
logger.Error("MustUnlockAdmission fail, checkZU fail, mid: %v, zid: %v", mid, zid)
|
||
return fmt.Errorf("无法解锁,请联系运营")
|
||
}
|
||
|
||
// 解锁空间会员
|
||
return v.store.UnlockZoneAdmission(ctx, tx, mid, zid, until, orderId, unlockType)
|
||
}
|
||
|
||
func (v *Vas) MustUnlockIronfanship(ctx *gin.Context, tx *sqlx.Tx, mid, zid, until int64, orderId string, unlockType int32) error {
|
||
// 检查解锁
|
||
_, exist := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||
if !exist {
|
||
logger.Error("MustUnlockIronfanship fail, checkZU fail, mid: %v, zid: %v", mid, zid)
|
||
return fmt.Errorf("无法解锁,请联系运营")
|
||
}
|
||
|
||
// 解锁铁粉
|
||
return v.store.UnlockZoneIronfanship(ctx, tx, mid, zid, until, orderId, unlockType)
|
||
}
|
||
|
||
func (v *Vas) MustUnlockSuperfanship(ctx *gin.Context, tx *sqlx.Tx, mid, zid, until int64, orderId string, unlockType int32) error {
|
||
// 检查解锁
|
||
_, exist := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||
if !exist {
|
||
logger.Error("MustUnlockSuperfanship fail, checkZU fail, mid: %v, zid: %v", mid, zid)
|
||
return fmt.Errorf("无法解锁,请联系运营")
|
||
}
|
||
|
||
// 解锁超粉
|
||
return v.store.UnlockZoneSuperfanship(ctx, tx, mid, zid, until, orderId, unlockType)
|
||
}
|
||
|
||
// 免费加入空间
|
||
func (v *Vas) ZoneFreeJoin(ctx *gin.Context, mid, zid int64) error {
|
||
// 查询空间价格
|
||
zv, _ := v.store.GetZoneVasById(ctx, zid)
|
||
if zv == nil {
|
||
logger.Error("GetZoneVasById fail, no such zid: %v", zid)
|
||
return fmt.Errorf("该主播没有开通空间")
|
||
}
|
||
if zv.AdmissionPrice != 0 {
|
||
return fmt.Errorf("不是免费空间")
|
||
}
|
||
|
||
// 是否已加入
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, nil, mid, zid)
|
||
if zu.IsUnlockAdmission() {
|
||
return fmt.Errorf("不能重复加入空间")
|
||
}
|
||
|
||
// 开启事务
|
||
tx, err := v.store.VasBegin(ctx)
|
||
if err != nil {
|
||
logger.Error("vas begin fail, err: %v", err)
|
||
return err
|
||
}
|
||
defer func() {
|
||
errTx := v.store.DealTxCR(tx, err)
|
||
if errTx != nil {
|
||
logger.Error("DealTxCR fail, err: %v", errTx)
|
||
return
|
||
}
|
||
}()
|
||
|
||
// 解锁空间会员
|
||
err = v.MustUnlockAdmission(ctx, tx, mid, zid, -1, "", dbstruct.ZoneUnlockTypeFree)
|
||
if err != nil {
|
||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
// 添加到空间成员
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeNormal)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 代运营加入空间
|
||
func (v *Vas) ZoneFreeJoinThirdPartner(ctx *gin.Context, mid, zid int64) error {
|
||
// 查询空间价格
|
||
zv, _ := v.store.GetZoneVasById(ctx, zid)
|
||
if zv == nil {
|
||
logger.Error("GetZoneVasById fail, no such zid: %v", zid)
|
||
return fmt.Errorf("该主播没有开通空间")
|
||
}
|
||
|
||
// 是否已加入
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, nil, mid, zid)
|
||
if zu.IsUnlockAdmission() {
|
||
return fmt.Errorf("不能重复加入空间")
|
||
}
|
||
|
||
// 开启事务
|
||
tx, err := v.store.VasBegin(ctx)
|
||
if err != nil {
|
||
logger.Error("vas begin fail, err: %v", err)
|
||
return err
|
||
}
|
||
defer func() {
|
||
errTx := v.store.DealTxCR(tx, err)
|
||
if errTx != nil {
|
||
logger.Error("DealTxCR fail, err: %v", errTx)
|
||
return
|
||
}
|
||
}()
|
||
|
||
// 解锁空间会员
|
||
err = v.MustUnlockAdmission(ctx, tx, mid, zid, -1, "", dbstruct.ZoneUnlockTypeThirdPartner)
|
||
if err != nil {
|
||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
// 删除空间成员
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeNormal)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 协作者加入空间
|
||
func (v *Vas) ZoneFreeJoinCollaborator(ctx *gin.Context, mid, zid int64) error {
|
||
// 查询空间价格
|
||
zv, _ := v.store.GetZoneVasById(ctx, zid)
|
||
if zv == nil {
|
||
logger.Error("GetZoneVasById fail, no such zid: %v", zid)
|
||
return fmt.Errorf("该主播没有开通空间")
|
||
}
|
||
|
||
// 是否已加入
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, nil, mid, zid)
|
||
if zu.IsUnlockAdmission() {
|
||
return fmt.Errorf("不能重复加入空间")
|
||
}
|
||
|
||
// 开启事务
|
||
tx, err := v.store.VasBegin(ctx)
|
||
if err != nil {
|
||
logger.Error("vas begin fail, err: %v", err)
|
||
return err
|
||
}
|
||
defer func() {
|
||
errTx := v.store.DealTxCR(tx, err)
|
||
if errTx != nil {
|
||
logger.Error("DealTxCR fail, err: %v", errTx)
|
||
return
|
||
}
|
||
}()
|
||
|
||
// 解锁空间会员
|
||
err = v.MustUnlockAdmission(ctx, tx, mid, zid, -1, "", dbstruct.ZoneUnlockTypeCollaborator)
|
||
if err != nil {
|
||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
// 添加到空间成员
|
||
err = v.store.AddZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeNormal)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 退出空间
|
||
func (v *Vas) ZoneExit(ctx *gin.Context, mid, zid int64) error {
|
||
// 是否已加入
|
||
zu, _ := v.CheckZoneUnlockExist(ctx, nil, mid, zid)
|
||
if !zu.IsUnlockAdmission() {
|
||
return fmt.Errorf("不能退出未加入的空间")
|
||
}
|
||
|
||
// 开启事务
|
||
tx, err := v.store.VasBegin(ctx)
|
||
if err != nil {
|
||
logger.Error("vas begin fail, err: %v", err)
|
||
return err
|
||
}
|
||
defer func() {
|
||
errTx := v.store.DealTxCR(tx, err)
|
||
if errTx != nil {
|
||
logger.Error("DealTxCR fail, err: %v", errTx)
|
||
return
|
||
}
|
||
}()
|
||
|
||
// 退出空间
|
||
err = v.store.ExitZoneAdmission(ctx, tx, mid, zid)
|
||
if err != nil {
|
||
logger.Error("ExitZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
|
||
// 删除空间成员
|
||
err = v.store.DeleteZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeNormal)
|
||
if err != nil {
|
||
logger.Error("AddZoneMember normal fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||
return err
|
||
}
|
||
if zu.IsUnlockIronfanship() {
|
||
_ = v.store.DeleteZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeIronfan)
|
||
}
|
||
if zu.IsUnlockSuperfanship() {
|
||
_ = v.store.DeleteZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeSuperfan)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 获取动态解锁记录
|
||
func (v *Vas) GetZoneMomentUnlockList(ctx *gin.Context, momentId int64, offset, limit int) (list []*dbstruct.ZoneMomentUnlock, err error) {
|
||
return v.store.GetZoneMomentUnlockList(ctx, nil, momentId, offset, limit)
|
||
}
|
||
|
||
// 统计时段内空间报表信息
|
||
func (v *Vas) GetLastHourZoneProfit(ctx *gin.Context, st, et int64) ([]*dbstruct.ZoneProfit, []*dbstruct.ZoneProfit, error) {
|
||
zoneprofits, err := v.store.GetLastHourZoneProfit(ctx, nil, st, et)
|
||
if err != nil {
|
||
logger.Error("GetLastHourZoneProfit fail err: %v", err)
|
||
return make([]*dbstruct.ZoneProfit, 0), make([]*dbstruct.ZoneProfit, 0), err
|
||
}
|
||
|
||
zonerefunds, err := v.store.GetLastHourZoneRefund(ctx, nil, st, et)
|
||
if err != nil {
|
||
logger.Error("GetLastHourZoneRefund fail err: %v", err)
|
||
return make([]*dbstruct.ZoneProfit, 0), make([]*dbstruct.ZoneProfit, 0), err
|
||
}
|
||
|
||
return zoneprofits, zonerefunds, nil
|
||
}
|
||
|
||
// 统计时段内空间进入人数
|
||
func (v *Vas) GetLastHourZoneAdmissionInfo(ctx *gin.Context, tx *sqlx.Tx, st, et int64) ([]*dbstruct.ZoneAdmissionInfo, error) {
|
||
return v.store.GetLastHourZoneAdmissionInfo(ctx, tx, st, et)
|
||
}
|
||
|
||
// 统计空间总人数
|
||
func (v *Vas) GetZoneMemberCountGroupByZoneMemberType(ctx *gin.Context, zids []int64) (map[int64]map[int64]int64, error) {
|
||
countMp := make(map[int64]map[int64]int64, 0)
|
||
list, err := v.store.GetZoneMemberCountGroupByZoneMemberType(ctx, nil, zids)
|
||
if err != nil {
|
||
logger.Error("GetZoneMemberCountGroupByZoneMemberType fail err: %v", err)
|
||
return make(map[int64]map[int64]int64), nil
|
||
}
|
||
|
||
for _, zonemembercount := range list {
|
||
mp, ok := countMp[zonemembercount.GetZid()]
|
||
if ok {
|
||
mp[zonemembercount.GetMemberType()] = zonemembercount.GetNum()
|
||
} else {
|
||
mp = make(map[int64]int64)
|
||
mp[zonemembercount.GetMemberType()] = zonemembercount.GetNum()
|
||
countMp[zonemembercount.GetZid()] = mp
|
||
}
|
||
}
|
||
|
||
return countMp, nil
|
||
}
|
||
|
||
// 空间收入页面
|
||
func (v *Vas) ZoneGetIncomePage(ctx *gin.Context, mid int64) (*vasproto.IncomePageData, error) {
|
||
data := new(vasproto.IncomePageData)
|
||
// 获取钻石、可提现钻石、待结算钻石
|
||
wallet, _ := v.CheckWalletExist(ctx, nil, mid)
|
||
data.Diamonds = wallet.GetDiamonds()
|
||
data.WithdrawDiamonds = wallet.GetWithdrawDiamonds()
|
||
data.WaitDealIncome = wallet.GetDiamonds() - wallet.GetWithdrawDiamonds()
|
||
|
||
// 获取今日收入
|
||
todayIncome, err := v.store.GetTodayIncome(ctx, nil, mid)
|
||
if err != nil {
|
||
logger.Error("GetTodayIncome fail, mid: %v, err: %v", mid, err)
|
||
}
|
||
data.TodayIncome = todayIncome
|
||
|
||
// 获取看病
|
||
doc, err := v.store.GetZoneUserIncome(ctx, mid)
|
||
if err != nil {
|
||
logger.Error("GetZoneUserIncome fail, mid: %v, err: %v", mid, err)
|
||
}
|
||
if doc != nil {
|
||
data.WeekDashboard = doc.WeekDashboard
|
||
data.IncomeFromDashboard = doc.WeekFromDashboard
|
||
}
|
||
return data, nil
|
||
}
|
||
|
||
// 获取动态解锁记录
|
||
func (v *Vas) RollbackZoneAdmissionExitStatus(ctx *gin.Context, ct, mid, zid int64) (err error) {
|
||
return v.store.RollbackZoneAdmissionExitStatus(ctx, nil, ct, mid, zid)
|
||
}
|
||
|
||
func (v *Vas) RollbackZoneAdmissionRefundStatus(ctx *gin.Context, mid, zid int64) (err error) {
|
||
return v.store.RollbackZoneAdmissionRefundStatus(ctx, nil, mid, zid)
|
||
}
|