feat-20240121-001-Robin #64

Merged
chenhao merged 5 commits from feat-20240121-001-Robin into test 2024-01-22 15:14:18 +08:00
8 changed files with 282 additions and 15 deletions
Showing only changes of commit 851e62264f - Show all commits

View File

@ -134,3 +134,30 @@ type AlipayCallbackParamIn struct {
OrderId string `json:"order_id"` // 我们自己服务的订单id
AlipayOrderId string `json:"alipay_order_id"` // 支付宝订单id
}
// 解锁会员资格
type UnlockMembershipReq struct {
base.BaseRequest
Ip string `json:"ip"`
MembershipProductId string `json:"membership_product_id"` // 商品iddbstruct.ProductIdMembership*
InviterMid int64 // 邀请人mid
}
type UnlockMembershipData struct {
LockType int32 `json:"lock_type"` // 见 dbstruct.UserVasLockType
OrderId string `json:"order_id"` // 订单id
}
// h5直接解锁会员资格
type H5DirectUnlockMembershipReq struct {
base.BaseRequest
PayType string `json:"pay_type"` // 支付类型
InviterMid int64
}
type H5DirectUnlockMembershipData struct {
CoinEnough int32 `json:"coin_enough"` // 0:不够(用下面的支付宝参数)1:够
OrderId string `json:"order_id"` // 订单id
AlipayParamStr string `json:"alipay_param_str"` // 支付宝 app支付参数
AlipayH5ParamStr string `json:"alipay_h5_param_str"` // 支付宝 h5支付参数
}

View File

@ -61,16 +61,17 @@ func (m *Mysql) DealTxCR(tx *sqlx.Tx, errIn error) (errOut error) {
// mysql
const (
DatabaseVas = "vas"
TableOrder = "vas_order" // 订单表
TableWallet = "vas_wallet" // 钱包
TableCoinOrder = "vas_coin_order" // 金币订单
TableConsumeHistoryCost = "vas_ch_cost" // 消费明细
TableConsumeHistoryCharge = "vas_ch_charge" // 充值明细
TableConsumeHistoryIncome = "vas_ch_income" // 收入明细
TableConsumeHistoryWithdraw = "vas_ch_withdraw" // 提现明细
TableVasUserUnlock = "vas_user_unlock" // 用增解锁
TableWithdrawOrder = "vas_withdraw_order" // 提现订单表
DatabaseVas = "vas"
TableOrder = "vas_order" // 订单表
TableWallet = "vas_wallet" // 钱包
TableCoinOrder = "vas_coin_order" // 金币订单
TableConsumeHistoryCost = "vas_ch_cost" // 消费明细
TableConsumeHistoryCharge = "vas_ch_charge" // 充值明细
TableConsumeHistoryIncome = "vas_ch_income" // 收入明细
TableConsumeHistoryWithdraw = "vas_ch_withdraw" // 提现明细
TableVasUserUnlock = "vas_user_unlock" // 用增解锁
TableWithdrawOrder = "vas_withdraw_order" // 提现订单表
TableVasUserMembershipUnlock = "vas_user_membership_unlock" // 会员资格解锁
)
func (m *Mysql) ChTableName(ch *dbstruct.ConsumeHistory) (string, error) {
@ -580,6 +581,51 @@ func (m *Mysql) GetUserVasUnlock(ctx *gin.Context, tx *sqlx.Tx, mid, uid int64,
return
}
// 创建解锁记录
func (m *Mysql) CreateUserVasMembershipUnlock(ctx *gin.Context, tx *sqlx.Tx, uu *dbstruct.UserVasMembershipUnlock) error {
var (
err error
timeNow = time.Now().Unix()
)
sqlStr := "insert into " + TableVasUserMembershipUnlock +
" (mid, product_id, ct, means, order_id) " +
" values (?, ?, ?, ?, ?) "
if tx != nil {
_, err = tx.ExecContext(ctx, sqlStr,
uu.Mid, uu.ProductId, timeNow, uu.Means, uu.OrderId,
)
} else {
db := m.getDBVas()
_, err = db.ExecContext(ctx, sqlStr,
uu.Mid, uu.ProductId, timeNow, uu.Means, uu.OrderId,
)
}
if err != nil {
logger.Error("CreateUserVasMembershipUnlock fail, uu: %v, err: %v", util.ToJson(uu), err)
return err
}
return err
}
// 获取会员资格解锁记录
func (m *Mysql) GetUserVasMembershipUnlock(ctx *gin.Context, tx *sqlx.Tx, mid int64, productId string) (uu *dbstruct.UserVasMembershipUnlock, err error) {
var uuTmp dbstruct.UserVasMembershipUnlock
sqlStr := fmt.Sprintf("select * from %s where mid=? and product_id=?", TableVasUserMembershipUnlock)
fmt.Println(sqlStr)
if tx != nil {
err = tx.GetContext(ctx, &uuTmp, sqlStr, mid, productId)
} else {
db := m.getDBVas()
err = db.GetContext(ctx, &uuTmp, sqlStr, mid, productId)
}
if err != nil {
return
}
uu = &uuTmp
return
}
// 获取解锁记录
func (m *Mysql) GetUnlockWechatList(ctx *gin.Context, tx *sqlx.Tx, mid int64, offset, limit int) (list []*dbstruct.UserVasUnlock, err error) {
list = make([]*dbstruct.UserVasUnlock, 0)

View File

@ -163,6 +163,30 @@ func (p *Account) OpCount(ctx *gin.Context, req *accountproto.OpCountReq) (int64
return count, err
}
func (p *Account) GetInviterMid(ctx *gin.Context, mid int64) (int64, error) {
inviterMid := int64(0)
userAcct, err := p.OpListByMid(ctx, &accountproto.OpListByMidReq{
Mid: goproto.Int64(mid),
})
if err != nil {
logger.Error("OpListByMid fail, err: %v", err)
return 0, err
}
if userAcct != nil && userAcct.Inviter != nil {
inviterAcct, err := p.OpListByUserId(ctx, &accountproto.OpListByUserIdReq{
UserId: userAcct.Inviter,
})
if err != nil {
logger.Error("OpListByUserId fail, err: %v", err)
return 0, err
}
if inviterAcct != nil {
inviterMid = util.DerefInt64(inviterAcct.Mid)
}
}
return inviterMid, nil
}
func (p *Account) GenerateOriginalAccount() (*dbstruct.Account, error) {
key := "account_init"
cfg := apollostruct.AccountInitCfg{}

View File

@ -9,9 +9,9 @@ import (
"encoding/pem"
"errors"
"fmt"
"github.com/go-pay/gopay/alipay"
"service/api/base"
"service/api/errs"
accountproto "service/api/proto/account/proto"
vasproto "service/api/proto/vas/proto"
"service/app/mix/dao"
"service/bizcommon/common"
@ -22,6 +22,8 @@ import (
"service/library/payclients/alipaycli"
"time"
"github.com/go-pay/gopay/alipay"
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
"github.com/samber/lo"
@ -31,11 +33,13 @@ import (
type Vas struct {
store *dao.Store
streamer *Streamer
account *Account
}
func NewVas(store *dao.Store, streamer *Streamer) (v *Vas) {
func NewVas(store *dao.Store, streamer *Streamer, account *Account) (v *Vas) {
return &Vas{
store: store,
account: account,
streamer: streamer,
}
}
@ -1679,6 +1683,13 @@ func (v *Vas) AlipayCallback(ctx *gin.Context, p *vasproto.AlipayCallbackParamIn
logger.Error("IncCoins fail, order: %v", util.ToJson(order))
return
}
case product.Id == dbstruct.ProductIdMembership:
// 解锁会员资格
_, err = v.UnlockMembership(ctx, util.DerefInt64(order.Mid), product, order)
if err != nil {
logger.Error("UnlockMembership fail, order: %v", util.ToJson(order))
return
}
}
}
@ -1968,3 +1979,134 @@ func (v *Vas) DealOneCoinOrder(ctx *gin.Context, coinOrder *dbstruct.CoinOrder)
// 更新订单状态
}
// 解锁会员资格
func (v *Vas) UnlockMembership(ctx *gin.Context, mid int64, product *dbstruct.Product, order *dbstruct.Order) (orderId string, err error) {
var (
did = util.DerefString(order.Did)
productId = product.Id
timeNow = time.Now().Unix()
)
orderId = util.DerefString(order.ID)
// 查询邀请人mid
inviterMid, _ := v.account.GetInviterMid(ctx, mid)
// 是否已经解锁过
unlockInfo, _ := v.store.GetUserVasMembershipUnlock(ctx, nil, mid, product.Id)
if unlockInfo != nil {
err = errs.ErrVasAlreadyUnlock
return
}
// 开启事务
tx, err := v.store.VasBegin(ctx)
if err != nil {
logger.Error("vas begin fail, err: %v", err)
return
}
defer func() {
errTx := v.store.DealTxCR(tx, err)
if errTx != nil {
logger.Error("DealTxCR fail, err: %v", errTx)
return
}
}()
// 官网钱包
officialWallet, _ := v.CheckWalletExist(ctx, common.OfficialMid)
// 邀请人钱包
var inviterWallet *dbstruct.Wallet
if inviterMid > 0 {
inviterWallet, _ = v.CheckWalletExist(ctx, inviterMid)
}
// 解锁记录
userVasMembershipUnlock := &dbstruct.UserVasMembershipUnlock{
Mid: goproto.Int64(mid),
ProductId: goproto.String(productId),
Ct: goproto.Int64(timeNow),
Means: goproto.String(dbstruct.UserVasUnlockMeansMoney),
OrderId: order.ID,
}
err = v.store.CreateUserVasMembershipUnlock(ctx, tx, userVasMembershipUnlock)
if err != nil {
logger.Error("CreateUserVasMembershipUnlock fail, userVasMembershipUnlock: %v, err: %v", util.ToJson(userVasMembershipUnlock), err)
return
}
// 加钻石
var (
TotalDias = product.RealCoinPrice
InviterDias = int64(0)
OfficialDias = int64(0)
)
if inviterMid > 0 {
InviterDias = int64(float64(TotalDias) * 0.05)
}
OfficialDias = TotalDias - InviterDias
// 官方
chOfficial := &dbstruct.ConsumeHistory{
Mid: goproto.Int64(common.OfficialMid),
Uid: goproto.Int64(mid),
Did: goproto.String(did),
Type: goproto.Int32(dbstruct.CHTypeIncome),
SType: goproto.Int32(dbstruct.CHSTypeIncomeContact),
TypeId: goproto.String(productId),
OrderId: goproto.String(orderId),
Change: goproto.Int64(OfficialDias),
Before: goproto.Int64(officialWallet.GetDiamonds()),
After: goproto.Int64(officialWallet.GetDiamonds() + OfficialDias),
Ct: goproto.Int64(timeNow),
}
err = v.store.CreateConsumeHistory(ctx, tx, chOfficial)
if err != nil {
logger.Error("CreateConsumeHistory fail, ch: %v, err: %v", util.ToJson(chOfficial), err)
return
}
err = v.store.IncDiamonds(ctx, tx, common.OfficialMid, OfficialDias)
if err != nil {
logger.Error("IncDiamonds fail, official, mid: %v, dias: %v, err: %v", common.OfficialMid, OfficialDias, err)
return
}
if InviterDias > 0 {
chInviter := &dbstruct.ConsumeHistory{
Mid: goproto.Int64(inviterMid),
Uid: goproto.Int64(mid),
Did: goproto.String(did),
Type: goproto.Int32(dbstruct.CHTypeIncome),
SType: goproto.Int32(dbstruct.CHSTypeIncomeInvite),
TypeId: goproto.String(productId),
OrderId: goproto.String(orderId),
Change: goproto.Int64(InviterDias),
Before: goproto.Int64(inviterWallet.GetDiamonds()),
After: goproto.Int64(inviterWallet.GetDiamonds() + InviterDias),
Ct: goproto.Int64(timeNow),
}
err = v.store.CreateConsumeHistory(ctx, tx, chInviter)
if err != nil {
logger.Error("CreateConsumeHistory fail, ch: %v, err: %v", util.ToJson(chInviter), err)
return
}
err = v.store.IncDiamonds(ctx, tx, inviterMid, InviterDias)
if err != nil {
logger.Error("IncDiamonds fail, inviter, mid: %v, dias: %v, err: %v", inviterMid, InviterDias, err)
return
}
}
err = v.account.OpUpdate(ctx, &accountproto.OpUpdateReq{
Account: &dbstruct.Account{
Mid: goproto.Int64(mid),
IsAMember: goproto.Int64(1),
},
})
if err != nil {
logger.Error("OpUpdate fail, err: %v", err)
return
}
return
}

View File

@ -157,7 +157,7 @@ func (s *Service) Init(c any) (err error) {
_DefaultImageAuditTask = logic.NewImageAuditTask(store)
_DefaultTextAudit = logic.NewTextAudit(store)
_DefaultTextAuditTask = logic.NewTextAuditTask(store)
_DefaultVas = logic.NewVas(store, _DefaultStreamer)
_DefaultVas = logic.NewVas(store, _DefaultStreamer, _DefaultAccount)
_DefaultContactCustomerServiceSession = logic.NewContactCustomerServiceSession(store)
_DefaultDailyStatement = logic.NewDailyStatement(store)
return

View File

@ -21,6 +21,7 @@ type Account struct {
GoldNum *int64 `json:"gold_num" bson:"gold_num"` // 金币数量
DiamondNum *int64 `json:"diamond_num" bson:"diamond_num"` // 钻石数量
Inviter *int64 `json:"inviter" bson:"inviter"` // 邀请人user_id
IsAMember *int64 `json:"is_a_member" bson:"is_a_member"` // 是否是会员0-否1-是
Latitude *float64 `bson:"latitude"` // 纬度
Longitude *float64 `bson:"longitude"` // 经度
Ct *int64 `json:"ct" bson:"ct"` // 创建时间

View File

@ -17,12 +17,16 @@ const (
ProductIdContactWechat = "contact_wechat" // 微信联系方式
ProductIdH5ContactWechat = "h5_contact_wechat" // h5的联系方式rmb直接解锁
ProductIdMembership = "membership" // 会员
ProductIdH5Membership = "h5_membership" // 会员
)
// 商品类型
const (
ProductTypeCoins = "coins" // 商品类型:金币
ProductTypeMoneyContact = "money_contact" // 商品类型:联系方式
ProductTypeCoins = "coins" // 商品类型:金币
ProductTypeMoneyContact = "money_contact" // 商品类型:联系方式
ProductTypeMoneyMembership = "money_membership" // 商品类型:会员资格
)
// 商品支付手段

View File

@ -781,3 +781,26 @@ func (p *WithdrawOrder) GetOpTime() int64 {
}
return 0
}
type UserVasMembershipUnlock struct {
Id *int64 `json:"id" db:"id"`
Mid *int64 `json:"mid" db:"mid"`
ProductId *string `json:"product_id" db:"product_id"`
Ct *int64 `json:"ct" db:"ct"`
Means *string `json:"means" db:"means"` // 解锁方式UserVasUnlockMeans
OrderId *string `json:"order_id" db:"order_id"` // 关联的订单id
}
func (p *UserVasMembershipUnlock) GetMid() int64 {
if p != nil && p.Mid != nil {
return *p.Mid
}
return 0
}
func (p *UserVasMembershipUnlock) GetOrderId() string {
if p != nil && p.OrderId != nil {
return *p.OrderId
}
return ""
}