service/app/mix/service/logic/vas_zone.go

1471 lines
45 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}