Files
huangguo_server/web/service/userser/user.go
T
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

1202 lines
38 KiB
Go

package userser
import (
"91porn-server/common/game"
"91porn-server/models/v/aiplazamod"
"91porn-server/models/v/cmtmod"
"91porn-server/models/v/mediamod"
"errors"
"fmt"
"strconv"
"sync"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"91porn-server/common"
"91porn-server/common/constant"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/pageopt"
"91porn-server/common/stderr"
"91porn-server/common/ysqr"
"91porn-server/models/commod"
"91porn-server/models/l/playlgmod"
"91porn-server/models/v/operationlogmod"
"91porn-server/models/v/proxymod"
"91porn-server/models/v/txnmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/videodiscountmod"
"91porn-server/models/v/vidmod"
"91porn-server/models/v/walletmod"
"91porn-server/web/webg"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type UserEx struct {
TotalViewTime *int64 `json:"totalViewTime,omitempty"` //总观看时长/秒
InviterID *uint64 `json:"inviterID,omitempty"` //邀请者ID
InviteCount *int64 `json:"inviteCount,omitempty"` //邀请数
Amount *int64 `json:"amount,omitempty"` //金币余额
Income *int64 `json:"income,omitempty"` //作品收益
VideoCnt *int `json:"videoCnt,omitempty"` //视频数
UndealVideoCnt *int `json:"undealVideoCnt,omitempty"` //未审核视频数
Integral int64 `json:"integral"` // 积分
}
type UserDoc struct {
ID *primitive.ObjectID `json:"id"`
UID *uint64 `json:"uid,omitempty"` //用户id
DevID *string `json:"devID,omitempty"` //设备id
HasPrivateZone *bool `json:"hasPrivateZone"` //已禁言
RegisterIP *string `json:"registerIP,omitempty"` //注册ip
DevType *string `json:"devType,omitempty"` //设备类型
Mobile *string `json:"mobile,omitempty"` //手机号
Gender *string `json:"gender,omitempty"` //性别
Name *string `json:"name,omitempty"` //昵称
Age *int `json:"age,omitempty"` //年龄
Region *string `json:"region,omitempty"` //地区
PromotionCode *string `json:"promotionCode,omitempty"` //推广码
VipLevel *int `json:"vipLevel,omitempty"` //vip等级
VipExpireDate *time.Time `json:"vipExpireDate,omitempty"` //vip过期时间
CreatedAt *time.Time `json:"createdAt,omitempty"` //注册时间
UpdatedAt *time.Time `json:"updatedAt,omitempty"` //更新时间
HasLocked *bool `json:"hasLocked"` //已禁止登陆
HasBanned *bool `json:"hasBanned"` //已禁言
ForbidUpload *bool `json:"forbidUpload"` //是否禁止上传帖子
Portrait *string `json:"portrait,omitempty"`
Summary *string `json:"summary,omitempty"`
LastActive *time.Time `json:"lastActive,omitempty"` //最后活跃时间
SuperUser *bool `json:"superUser"` //大v
ActiveValue *int `json:"activeValue"` //活跃度
OfficialCert *bool `json:"officialCert"` //是否官方认证
IsMadou *bool `json:"isMadou"` //是否麻豆账号
TaxLevel *int64 `json:"taxLevel"` //税收等级
LoginDays *int64 `json:"loginDays"` //登陆天数
LockReason *string `json:"lockReason"` //禁止登录的原因
LockImg *string `json:"lockImg"` //禁止登录的原因截图
LockAt *time.Time `json:"lockAt"` //封禁开始时间
commod.DiscDoc //商区
BankActName *string `json:"bankActName" bson:"bankActName"` //银行卡绑定名字
OriginalSort *int `json:"originalSort" bson:"originalSort"` // 新版原创排序
VideoDeduction *float64 `json:"videoDeduction"` //视频扣量
MerchantAccount *string `json:"merchantAccount" bson:"merchantAccount"` //商家账号
AiUndressCount *int64 `json:"aiUndressCount" bson:"aiUndressCount"`
DownloadCount *int64 `json:"downloadCount" bson:"downloadCount"`
IsAdvertiser *bool `json:"isAdvertiser" bson:"isAdvertiser"` // 是否是打广告用户
AllGoldVideoFree *bool `json:"allGoldVideoFree" bson:"allGoldVideoFree"` // 是否是全金币视频免费
GoldVideoFreeExpire *time.Time `json:"goldVideoFreeExpire" bson:"goldVideoFreeExpire,omitempty"` // 金币视频免费日期
BannedTime *time.Time `json:"bannedTime" bson:"bannedTime"` // 禁言时间
AiMateBalance float64 `json:"aiMateBalance" bson:"aiMateBalance"` //ai伴侣余额
AiMateUid string `json:"aiMateUid" bson:"aiMateUid"` // ai伴侣uid,对应第三方服务
ChatPrice int64 `json:"chatPrice" bson:"chatPrice,omitempty"` // 私信价格
BroadcastExpire time.Time `json:"broadcastExpire" bson:"broadcastExpire,omitempty"` //直播有效期
DramaExpire time.Time `json:"dramaExpire" bson:"dramaExpire,omitempty"` //短剧权益有效期
GoldVideoFreeLimit int64 `json:"goldVideoFreeLimit" bson:"goldVideoFreeLimit"`
}
type UserRecord struct {
UserDoc
UserEx
}
type UserPage struct {
Total int64 `json:"total"`
List []UserRecord `json:"list"`
}
func UserPages(sort D, skip, limit int64,
uidMatch pageopt.UIDMatch,
devIdMatch usermod.DevIDRegexMatch,
vipLevelMatch usermod.VipLevelMatch,
nameMatch usermod.NameRegexMatch,
mobileMatch usermod.MobileMatch,
promotionCodeMatch usermod.PromotionCodeMatch,
hasPromotionCodeMatch usermod.HasPromotionCodeMatch,
lockedMatch usermod.LockedMatch,
bannedMatch usermod.BannedMatch,
forbidUploadMatch usermod.ForbidUploadMatch,
createdAtGTEAndLTMatch usermod.CreatedAtGTEAndLTMatch,
districtCodeMatch usermod.DistrictCodeMatch,
autoFollowMatch usermod.AutoFollowMatch,
registerIPMatch usermod.RegisterIpMatch,
) (page UserPage, err error) {
matchList := []Matcher{
uidMatch.New(),
devIdMatch.New(),
vipLevelMatch.New(),
nameMatch.New(),
mobileMatch.New(),
promotionCodeMatch.New(),
hasPromotionCodeMatch.New(),
lockedMatch.New(),
bannedMatch.New(),
forbidUploadMatch.New(),
createdAtGTEAndLTMatch.New(),
districtCodeMatch.New(),
autoFollowMatch.New(),
registerIPMatch.New(),
}
//获取user列表
userList, err := usermod.List(sort, &skip, &limit, matchList...)
count, err := usermod.Count(matchList...)
if err != nil {
return UserPage{}, err
}
var (
uidList = make([]uint64, len(userList))
userMap = make(map[uint64]usermod.User, len(userList))
)
for i, user := range userList {
uidList[i] = user.UID
userMap[user.UID] = user
if user.TaxLevel == 0 {
userList[i].TaxLevel = constant.DefaultBloggerVideoIncomeTaxLevel // 用户未设置分成,则展示系统默认值
}
}
var (
wg = sync.WaitGroup{}
playSecMap = make(map[uint64]int64)
invrLv1Map = make(map[uint64]uint64)
invrCountMap = make(map[uint64]int64)
walletMap = make(map[uint64]walletmod.Wallet)
videoCntMap = make(map[uint64]int)
unDealVideoCntMap = make(map[uint64]int)
totalCount int64 //文档总数
)
wg.Add(6)
common.Go(func() {
defer wg.Done()
totalCount = count
})
common.Go(func() {
defer wg.Done()
m, err := playlgmod.GetUIDPlaySecondMap(uidList)
if err != nil {
log.Error("GetUIDPlaySecondMap err", log.E(err))
return
}
playSecMap = m
})
common.Go(func() {
defer wg.Done()
m, err := proxymod.GetInvrLv1Map(uidList)
if err != nil {
log.Error("GetInvrLv1Map err", log.E(err))
return
}
invrLv1Map = m
})
common.Go(func() {
defer wg.Done()
m, err := proxymod.GetInviteesCountMap(uidList)
if err != nil {
log.Error("GetInviteesCountMap err", log.E(err))
return
}
invrCountMap = m
})
common.Go(func() {
defer wg.Done()
m, err := walletmod.GetWalletMap(uidList)
if err != nil {
log.Error("GetWalletMap err", log.E(err))
return
}
walletMap = m
})
common.Go(func() {
defer wg.Done()
videoCntMap, unDealVideoCntMap, err = vidmod.GetVideosByUIDs(uidList)
if err != nil {
log.Error("GetSumDateCountMap err", log.E(err))
return
}
})
wg.Wait()
list := toUserRecordList(
userList,
playSecMap,
invrLv1Map,
invrCountMap,
walletMap,
videoCntMap,
unDealVideoCntMap,
)
return UserPage{
List: list,
Total: totalCount,
}, nil
}
func toUserRecordList(
userList []usermod.User,
playSecMap map[uint64]int64,
invrLv1Map map[uint64]uint64,
invrCountMap map[uint64]int64,
walletMap map[uint64]walletmod.Wallet,
videoCntMap map[uint64]int,
unDealVideoCntMap map[uint64]int,
) []UserRecord {
recordList := make([]UserRecord, len(userList))
for i, user := range userList {
_user := user
//UserDoc
age := _user.Age()
aiUndressFreeTimes := walletMap[_user.UID].AiUndressFreeTimes
downloadCount := walletMap[_user.UID].DownloadCount
aiMateBalance := walletMap[_user.UID].AiMateBalance
userDoc := UserDoc{
ID: &_user.ID,
UID: &_user.UID,
DevID: &_user.DevID,
HasPrivateZone: &_user.HasPrivateZone,
RegisterIP: &_user.RegisterIP,
DevType: &_user.DevType,
Mobile: &_user.Mobile,
Gender: &_user.Gender,
Name: &_user.Name,
Age: &age,
Region: &_user.Region,
PromotionCode: &_user.PromCode,
VipLevel: &_user.VipLevel,
VipExpireDate: &_user.VipExpireDate,
CreatedAt: &_user.CreatedAt,
UpdatedAt: &_user.UpdatedAt,
HasLocked: &_user.HasLocked,
HasBanned: &_user.HasBanned,
ForbidUpload: &_user.ForbidUpload,
Portrait: &_user.Portrait,
Summary: &_user.Summary,
LastActive: &_user.LastVisitAt,
SuperUser: &_user.SuperUser,
ActiveValue: &_user.ActiveValue,
OfficialCert: &_user.OfficialCert,
TaxLevel: &_user.TaxLevel,
LoginDays: &_user.LoginDays,
DiscDoc: _user.DiscDoc,
LockReason: &_user.LockReason,
LockImg: _user.LockImg,
BankActName: &_user.BankActName,
OriginalSort: &_user.OriginalSort,
VideoDeduction: &_user.VideoDeduction,
MerchantAccount: &_user.MerchantAccount,
AiUndressCount: &aiUndressFreeTimes,
DownloadCount: &downloadCount,
IsAdvertiser: &_user.IsAdvertiser,
BannedTime: &_user.BannedTime,
AllGoldVideoFree: &_user.AllGoldVideoFree,
AiMateBalance: aiMateBalance,
AiMateUid: _user.AiMateUid,
ChatPrice: _user.ChatPrice,
BroadcastExpire: user.BroadcastExpire,
DramaExpire: user.DramaExpire,
}
if !_user.LockAt.IsZero() {
userDoc.LockAt = &_user.LockAt
}
if _user.GoldVideoFreeExpire.After(time.Now()) {
userDoc.GoldVideoFreeExpire = &_user.GoldVideoFreeExpire
userDoc.GoldVideoFreeLimit = _user.GoldVideoFreeLimit
}
if _user.BannedTime.After(time.Now()) {
status := true
userDoc.HasBanned = &status
}
//UserEx
uid := _user.UID
totalViewTime := playSecMap[uid]
inviterId := invrLv1Map[uid]
inviteCount := invrCountMap[uid]
amount := walletMap[uid].Amount
integral := walletMap[uid].Integral
income := walletMap[uid].Income
total := videoCntMap[uid]
unDeal := unDealVideoCntMap[uid]
userEx := UserEx{
TotalViewTime: &totalViewTime,
InviterID: &inviterId,
InviteCount: &inviteCount,
Amount: &amount,
Income: &income,
VideoCnt: &total,
UndealVideoCnt: &unDeal,
Integral: integral,
}
//Record
recordList[i] = UserRecord{
UserDoc: userDoc,
UserEx: userEx,
}
}
return recordList
}
func ModifyUserMobile(uid uint64, mobile string) error {
user, err := usermod.FindUserByUID(uid)
if err != nil {
log.Error(fmt.Sprintf("用户ID:%d;手机号:%s;查询用户信息异常:%v", uid, mobile, err))
return err
}
// 怀疑是这里引起的 invalid memory address or nil pointer dereference
if user == nil {
log.Warn(fmt.Sprintf("用户ID:%d;手机号:%s;用户不存在", uid, mobile))
return errors.New(stderr.UserIsNotExists.Msg())
}
if user.Mobile == mobile {
return nil
}
if user.MobileBindAt == nil && mobile != "" { //首次绑定
ok, err := usermod.BindMobile(nil, uid, mobile, "")
if err != nil {
return err
}
if !ok {
return errors.New("mobile bind faild")
}
return nil
}
if user.Mobile != "" && mobile == "" { //解绑手机
ok, err := usermod.UnBindMobile(uid)
if err != nil {
return err
}
if !ok {
return errors.New("mobile unbind faild")
}
return nil
}
ok, err := usermod.ChangeMobile(uid, mobile) //更换手机号
if err != nil {
return err
}
if !ok {
return errors.New("mobile change faild")
}
return nil
}
// ModifyUserAmount 修改账号金币
func ModifyUserAmount(uid uint64, amt int64, manager, reason string) (operationlogmod.OperationLog, error) {
var opLog operationlogmod.OperationLog
if err := webg.VideoDB.Trans(func(t *db.MongoTool) error {
amount, err := walletmod.GetAmount(t, uid)
if err != nil {
return err
}
opLog = operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.User_INCoin,
BeforeContent: strconv.FormatInt(amount, 10),
AfterContent: strconv.FormatInt(amount+amt, 10),
Reason: reason,
UserID: int64(uid),
CreatedUser: manager,
}
amount += amt
if amount < 0 {
amount = 0
}
return walletmod.SetAmount(t, amount, uid)
}); err != nil {
log.Error(fmt.Sprintf("modifyUserAmount Trans fail error:%+v:", err))
return opLog, err
}
return opLog, nil
}
func CreditAmount(uid uint64, amount int64, manager, reason string) error {
var tranType string
var tranTypeInt int64
var desc string
// var f func(t *db.MongoTool, amt int64, uid uint64) error
if amount > 0 { //上分
tranType = txnmod.AdminCreaditAmount.Key()
tranTypeInt = int64(txnmod.AdminCreaditAmount)
desc = txnmod.AdminCreaditAmount.Key() + strconv.FormatInt(amount, 10)
} else if amount < 0 { //减分
tranType = txnmod.AdminDebitAmount.Key()
tranTypeInt = int64(txnmod.AdminDebitAmount)
desc = txnmod.AdminDebitAmount.Key() + strconv.FormatInt(amount, 10)
} else { //不操作
return nil
}
// f = walletmod.CreditAmount
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
amountOld, err := walletmod.GetAmount(t, uid)
if err != nil {
return err
}
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.User_INCoin,
BeforeContent: strconv.FormatInt(amountOld, 10),
AfterContent: strconv.FormatInt(amountOld+amount, 10),
Reason: reason,
UserID: int64(uid),
CreatedUser: manager,
}); err != nil {
return err
}
wallet, err := walletmod.CreditAmount(t, amount, uid)
if err != nil {
return err
}
return txnmod.InsertTransactionLog(t, &(txnmod.TransactionLog{
UID: uid,
Amount: amount,
ActualAmount: float64(amount),
Tax: 0,
TaxAmount: 0,
TranType: tranType,
TranTypeInt: tranTypeInt,
Desc: desc,
RealAmount: walletmod.GetRealAmount(wallet),
}))
})
}
// FruitCoinChange 果币变更
func FruitCoinChange(uid uint64, fruitCoin int64, manager, reason string) error {
var (
tranType string
tranTypeInt int64
desc string
)
if fruitCoin > 0 { //上分
tranType = txnmod.AdminCreditFruitCoin.Key()
tranTypeInt = int64(txnmod.AdminCreditFruitCoin)
desc = txnmod.AdminCreditFruitCoin.Key() + strconv.FormatInt(fruitCoin, 10)
} else if fruitCoin < 0 { //减分
tranType = txnmod.AdminDebitFruitCoin.Key()
tranTypeInt = int64(txnmod.AdminDebitFruitCoin)
desc = txnmod.AdminDebitFruitCoin.Key() + strconv.FormatInt(fruitCoin, 10)
} else { //不操作
return nil
}
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
fruitAmountOld, err := walletmod.GetFruitAmount(t, uid)
if err != nil {
return err
}
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.User_INFruitCoin,
BeforeContent: strconv.FormatInt(fruitAmountOld, 10),
AfterContent: strconv.FormatInt(fruitAmountOld+fruitCoin, 10),
Reason: reason,
UserID: int64(uid),
CreatedUser: manager,
}); err != nil {
return err
}
wallet, err := walletmod.CreditFruitCoin(t, uid, fruitCoin)
if err != nil {
return err
}
return txnmod.InsertTransactionLog(t, &(txnmod.TransactionLog{
UID: uid,
FruitCoin: fruitCoin,
TranType: tranType,
TranTypeInt: tranTypeInt,
Desc: desc,
FruitCoinBalance: wallet.FruitCoin,
}))
})
}
func CheckVipRenew(user *usermod.User, vipDay int, vipLevel int) (time.Time, int) {
now := time.Now()
var end time.Time
level := vipLevel
d := time.Hour * 24 * time.Duration(vipDay)
if user.VipExpireDate.After(now) { //renew
end = user.VipExpireDate.Add(d)
if user.VipLevel > level { //当前用户的vip等级比这次购买的大,使用用户的
level = user.VipLevel
}
} else {
end = now.Add(d)
}
return end, level
}
// Info () 我的信息
func Info(uid uint64) (userInfo usermod.UserVipInfoRes, err error) {
var u *usermod.User
var videoDiscountLog videodiscountmod.VideoDiscountLog //视频折扣卡
u, err = usermod.FindUserByUID(uid)
if err != nil || u == nil {
return
}
//金币视频免费
videoDiscountLog, err = videodiscountmod.GetByUIDAndWeb(uid)
//信息组装
if err == nil && !videoDiscountLog.ID.IsZero() {
userInfo.VideoDiscountExpiration = videoDiscountLog.Expiration
userInfo.VideoDiscount = videoDiscountLog.VideoDiscount
}
userInfo.VipExpireDate = u.VipExpireDate
userInfo.GoldVideoFreeExpire = u.GoldVideoFreeExpire
userInfo.PayVidDiscount = u.PayVidDiscount
userInfo.VideoFreeExpiration = u.VideoFreeExpiration
userInfo.GoldVideoCoupon = u.GoldVideoCoupon
return
}
func CreditGold(uid uint64, amount, income int64, manager, reason string) error {
if amount > 0 && income > 0 {
return errors.New("金额不能为正")
}
if amount == 0 && income == 0 {
return errors.New("金额不能为0")
}
if reason == "" {
return errors.New("描述不能为空")
}
wallet, err := walletmod.GetWallet(uid)
if err != nil {
return err
}
if amount < 0 && wallet.Amount <= 0 {
return errors.New("用户余额不足, 回收失败")
}
if income < 0 && wallet.Income <= 0 {
return errors.New("用户余额不足, 回收失败")
}
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
p := &walletmod.DebitPlan{}
if amount < 0 && wallet.Amount > 0 {
//walletmod.Debit调用的是扣除方法, 所以这里金额要为正才是减少
p.Amount = -amount
//余额不足时, 扣为0
if wallet.Amount < p.Amount {
p.Amount = wallet.Amount
}
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.User_ReduceAmount,
BeforeContent: strconv.FormatInt(wallet.Amount, 10),
AfterContent: strconv.FormatInt(wallet.Amount-p.Amount, 10),
Reason: reason,
UserID: int64(uid),
CreatedUser: manager,
}); err != nil {
return err
}
}
if income < 0 && wallet.Income > 0 {
//walletmod.Debit调用的是扣除方法, 所以这里金额要为正才是减少
p.Income = -income
//余额不足时, 扣为0
if wallet.Income < p.Income {
p.Income = wallet.Income
}
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.User_ReduceIncome,
BeforeContent: strconv.FormatInt(wallet.Income, 10),
AfterContent: strconv.FormatInt(wallet.Income-p.Income, 10),
Reason: reason,
UserID: int64(uid),
CreatedUser: manager,
}); err != nil {
return err
}
}
if p.Amount > 0 || p.Income > 0 {
//walletmod.Debit调用的是扣除方法, 所以这里金额要为正才是减少
if _, err = walletmod.Debit(t, p, uid); err != nil {
return err
}
tl := txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: uid,
Amount: -p.Amount,
ActualAmount: -float64(p.Amount),
TranType: txnmod.AdminDebitAmount.Key(),
TranTypeInt: int64(txnmod.AdminDebitAmount),
Desc: txnmod.AdminDebitAmount.Key() + "-" + reason,
RealAmount: walletmod.GetRealAmount(wallet),
}
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
log.Error(" InsertTransactionLog error", log.E(err))
return err
}
}
return nil
})
}
// SetAdvertiser 批量修改用户是否为打广告用户
func SetAdvertiser(in *usermod.SetAdvertiseCond) error {
doc := usermod.UserSelector{IsAdvertiser: in.Enable}
if _, err := usermod.UpdateMany(in.UId, doc); err != nil {
return err
}
filter := bson.M{"userID": bson.M{"$in": in.UId}}
cond := bson.M{"$set": bson.M{"isAdvertiser": in.Enable}}
data, err := cmtmod.GetCmtListByCond(filter)
if err != nil {
return err
}
if len(data) > 0 {
err := cmtmod.UpdateMany(filter, cond)
if err != nil {
return err
}
}
return nil
}
// GenerateQRCode 生成用户二维码
func GenerateQRCode(uid uint64) (string, error) {
user, err := usermod.FindUserByUIDForNoCache(uid)
if err != nil {
return "", err
}
content := ysqr.Content{
UID: uid,
T: ysqr.Login,
LoginClaims: ysqr.LoginClaims{
DevID: user.DevID,
},
}
return content.String(webg.Conf.Base.JwtKey), nil
}
func UpdateLotteryTimes(param *usermod.UpdateLotteryTimesCond, manager string) error {
user, err := usermod.FindUserByUID(param.UID)
if err != nil {
return errors.New("query user by id is err")
}
if user.ID.IsZero() {
return errors.New("user is null")
}
if *param.LotteryTimes == 0 {
return errors.New("抽奖次数不能为0")
}
if param.Mark == nil {
return errors.New("描述不能为空")
}
wallet, err := walletmod.GetWallet(param.UID)
if err != nil {
return err
}
if (*param.LotteryTimes < 0 && wallet.LotteryTimes <= 0) || (*param.LotteryTimes < 0 && wallet.LotteryTimes < -*param.LotteryTimes) {
return errors.New("用户下抽奖次数不足, 回收失败")
}
tranErr := webg.VideoDB.Trans(func(t *db.MongoTool) error {
p := &walletmod.CreditPlan{}
var tl txnmod.TransactionLog
if *param.LotteryTimes < 0 {
d := -*param.LotteryTimes
p.LotteryTimes = &d
tl = txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: param.UID,
Amount: -*param.LotteryTimes,
ActualAmount: float64(*param.LotteryTimes),
TranType: txnmod.AdminDebitLotteryTimesCount.Key(),
TranTypeInt: int64(txnmod.AdminDebitLotteryTimesCount),
Desc: fmt.Sprintf("官方减去[%v次]-%s", -*param.LotteryTimes, *param.Mark),
RealAmount: walletmod.GetRealAmount(wallet),
}
}
if *param.LotteryTimes > 0 {
d := -*param.LotteryTimes
p.LotteryTimes = &d
tl = txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: param.UID,
Amount: *param.LotteryTimes,
ActualAmount: float64(*param.LotteryTimes),
TranType: txnmod.AdminAddLotteryTimesCount.Key(),
TranTypeInt: int64(txnmod.AdminAddLotteryTimesCount),
Desc: fmt.Sprintf("官方增加[%v次]-%s", *param.LotteryTimes, *param.Mark),
RealAmount: walletmod.GetRealAmount(wallet),
}
}
w, err := walletmod.DebitLotteryTimes(t, *p.LotteryTimes, param.UID)
if err != nil {
log.Error(fmt.Sprintf("UpdateFreeTimes walletmod.DebitDownloadCounts error%v,manager%s", err, manager))
return err
}
if w == nil {
return errors.New("UpdateFreeTimes walletmod.DebitDownloadCounts is null")
}
err = txnmod.InsertTransactionLog(t, &tl)
if err != nil {
log.Error(fmt.Sprintf("UpdateDownloadCounts xnmod.InsertTransactionLog error%v,manager%s", err, manager))
return err
}
return nil
})
if tranErr != nil {
log.Error(fmt.Sprintf("UpdateDownloadCounts Trans error%v,manager%s", tranErr, manager))
return tranErr
}
return nil
}
func UpdateFreeTimes(param *usermod.UpdateFreeTimesCond, manager string) error {
user, err := usermod.FindUserByUID(param.UID)
if err != nil {
return errors.New("query user by id is err")
}
if user.ID.IsZero() {
return errors.New("user is null")
}
if *param.FreeTimes == 0 {
return errors.New("免费次数不能为0")
}
if param.Mark == nil {
return errors.New("描述不能为空")
}
wallet, err := walletmod.GetWallet(param.UID)
if err != nil {
return err
}
if (*param.FreeTimes < 0 && wallet.AiUndressFreeTimes <= 0) || (*param.FreeTimes < 0 && wallet.AiUndressFreeTimes < -*param.FreeTimes) {
return errors.New("用户免费次数不足, 回收失败")
}
tranErr := webg.VideoDB.Trans(func(t *db.MongoTool) error {
p := &walletmod.DebitPlan{}
var tl txnmod.TransactionLog
if *param.FreeTimes < 0 {
p.AiUndressFreeTimes = -*param.FreeTimes
tl = txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: param.UID,
Amount: -*param.FreeTimes,
ActualAmount: float64(*param.FreeTimes),
TranType: txnmod.AdminDebitAiUndressFreeTimes.Key(),
TranTypeInt: int64(txnmod.AdminDebitAiUndressFreeTimes),
Desc: fmt.Sprintf("官方减去AI脱衣免费次数[%v次]-%s", -*param.FreeTimes, *param.Mark),
RealAmount: walletmod.GetRealAmount(wallet),
}
}
if *param.FreeTimes > 0 {
p.AiUndressFreeTimes = -*param.FreeTimes
tl = txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: param.UID,
Amount: *param.FreeTimes,
ActualAmount: float64(*param.FreeTimes),
TranType: txnmod.AdminAddAiUndressFreeTimes.Key(),
TranTypeInt: int64(txnmod.AdminAddAiUndressFreeTimes),
Desc: fmt.Sprintf("官方增加AI脱衣免费次数[%v次]-%s", *param.FreeTimes, *param.Mark),
RealAmount: walletmod.GetRealAmount(wallet),
}
}
w, err := walletmod.DebitAiFreeTimes(t, p.AiUndressFreeTimes, param.UID)
if err != nil {
log.Error(fmt.Sprintf("UpdateFreeTimes walletmod.DebitAiFreeTimes error%v,manager%s", err, manager))
return err
}
if w == nil {
return errors.New("UpdateFreeTimes walletmod.DebitAiFreeTimes is null")
}
err = txnmod.InsertTransactionLog(t, &tl)
if err != nil {
log.Error(fmt.Sprintf("UpdateFreeTimes xnmod.InsertTransactionLog error%v,manager%s", err, manager))
return err
}
return nil
})
if tranErr != nil {
log.Error(fmt.Sprintf("UpdateFreeTimes Trans error%v,manager%s", tranErr, manager))
return tranErr
}
return nil
}
func UpdateDownloadCounts(param *usermod.UpdateDownloadCond, manager string) error {
user, err := usermod.FindUserByUID(param.UID)
if err != nil {
return errors.New("query user by id is err")
}
if user.ID.IsZero() {
return errors.New("user is null")
}
if *param.Download == 0 {
return errors.New("下载次数不能为0")
}
if param.Mark == nil {
return errors.New("描述不能为空")
}
wallet, err := walletmod.GetWallet(param.UID)
if err != nil {
return err
}
if (*param.Download < 0 && wallet.DownloadCount <= 0) || (*param.Download < 0 && wallet.DownloadCount < -*param.Download) {
return errors.New("用户下载次数不足, 回收失败")
}
tranErr := webg.VideoDB.Trans(func(t *db.MongoTool) error {
p := &walletmod.DebitPlan{}
var tl txnmod.TransactionLog
if *param.Download < 0 {
p.DownloadCount = -*param.Download
tl = txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: param.UID,
Amount: -*param.Download,
ActualAmount: float64(*param.Download),
TranType: txnmod.AdminDebitDownloadCount.Key(),
TranTypeInt: int64(txnmod.AdminDebitDownloadCount),
Desc: fmt.Sprintf("官方减去[%v次]-%s", -*param.Download, *param.Mark),
RealAmount: walletmod.GetRealAmount(wallet),
}
}
if *param.Download > 0 {
p.DownloadCount = -*param.Download
tl = txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: param.UID,
Amount: *param.Download,
ActualAmount: float64(*param.Download),
TranType: txnmod.AdminAddDownloadCount.Key(),
TranTypeInt: int64(txnmod.AdminAddDownloadCount),
Desc: fmt.Sprintf("官方增加[%v次]-%s", *param.Download, *param.Mark),
RealAmount: walletmod.GetRealAmount(wallet),
}
}
w, err := walletmod.DebitDownloadCounts(t, p.DownloadCount, param.UID)
if err != nil {
log.Error(fmt.Sprintf("UpdateFreeTimes walletmod.DebitDownloadCounts error%v,manager%s", err, manager))
return err
}
if w == nil {
return errors.New("UpdateFreeTimes walletmod.DebitDownloadCounts is null")
}
err = txnmod.InsertTransactionLog(t, &tl)
if err != nil {
log.Error(fmt.Sprintf("UpdateDownloadCounts xnmod.InsertTransactionLog error%v,manager%s", err, manager))
return err
}
return nil
})
if tranErr != nil {
log.Error(fmt.Sprintf("UpdateDownloadCounts Trans error%v,manager%s", tranErr, manager))
return tranErr
}
return nil
}
func DeleteComment(param *usermod.DeleteCommentCond, manager string) error {
user, err := usermod.FindUserByUID(param.UID)
if err != nil {
return errors.New("query user by id is err")
}
if user.ID.IsZero() {
return errors.New("user is null")
}
err = cmtmod.DelCommentsByUID(param.UID)
if err != nil {
return err
}
return nil
}
func QueryGameCode(param *usermod.QueryGameCodeCond) (*game.QueryData, error) {
user, err := usermod.FindUserByUID(param.UID)
if err != nil {
return nil, errors.New("query user by id is err")
}
if user.ID.IsZero() {
return nil, errors.New("user is null")
}
gameCode, err := game.QueryTripartiteGameCode(user.UID)
if err != nil {
return nil, err
}
return gameCode, nil
}
// 重置uid的ai余额为aiMateBalance
func SetAiMateBalance(uid uint64, aiMateBalance float64, reason string) error {
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
if err := walletmod.SetAiMateBalance(t, uid, aiMateBalance); err != nil {
return err
}
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: uid,
TranType: txnmod.AdminAiMateSet.Key(),
TranTypeInt: int64(txnmod.AdminAiMateSet),
Desc: txnmod.AdminAiMateSet.Key() + "-ai伴侣余额设置为" + strconv.FormatFloat(aiMateBalance, 'f', -1, 64) + "-" + reason,
AiMatePoint: aiMateBalance,
RealAiMatePoint: aiMateBalance,
})
})
}
type UserCommentBanned struct {
UIDList []uint64 `form:"uidList" json:"uidList" binding:"required"`
Enable *bool `form:"enable" json:"enable" binding:"required"`
Reason string `form:"reason" json:"reason"`
}
func (req UserCommentBanned) Banned() stderr.Code {
if len(req.UIDList) == 0 || req.Enable == nil {
return stderr.Success
}
now := time.Now()
doc := usermod.UserSelector{HasBanned: req.Enable}
if *req.Enable {
bannedTime := now.Add(time.Hour)
doc.BannedTime = &bannedTime
} else {
doc.BannedTime = &now // 服务端那边使用BannedTime去验证是否解禁,所以这里直接设置为当前时间,表示立即解禁
}
if _, err := usermod.UpdateMany(req.UIDList, doc); err != nil {
return stderr.ErrServerUnavailable
}
if *req.Enable {
common.Go(func() {
var cids []primitive.ObjectID
var pids []primitive.ObjectID
filter := bson.M{"userID": bson.M{"$in": req.UIDList}}
comments, err := cmtmod.FindList(filter, options.Find().SetLimit(1000).SetSkip(0))
if err != nil {
return
}
if len(comments) == 0 {
return
}
mData := map[cmtmod.OType][]primitive.ObjectID{}
// 玩家评论过的帖子 重新统计条数
for _, comment := range comments {
if comment == nil {
continue
}
if _, ok := mData[comment.ObjType]; !ok {
mData[comment.ObjType] = []primitive.ObjectID{comment.ObjID}
} else {
mData[comment.ObjType] = append(mData[comment.ObjType], comment.ObjID)
}
if !comment.CID.IsZero() {
pids = append(pids, comment.CID)
}
cids = append(cids, comment.ID)
}
// 删除评论
_ = cmtmod.DelCommentsByIDS(cids)
for oType, oids := range mData {
for _, oid := range oids {
n, err := cmtmod.GetCount(bson.M{"objType": oType, "objID": oid})
if err != nil {
continue
}
switch oType {
case cmtmod.OTypeVideo:
_ = vidmod.UpdateOne(oid, bson.M{"$set": bson.M{"commentCount": n}})
case cmtmod.OTypeCartoon:
_, _ = mediamod.UpdateByID(nil, oid, bson.M{"countComment": n})
case cmtmod.OTypeAiPlaza:
_, _ = aiplazamod.UpdateByID(nil, oid, bson.M{"commentCount": n})
}
}
}
if len(pids) > 0 {
cmtmod.CmtDecreaseCommentCountByID(pids...)
}
})
}
return stderr.Success
}
type LockUserReq struct {
UID uint64 `form:"uid" json:"uid" binding:"required"`
LockImg *string `form:"lockImg" json:"lockImg"` //禁止登录的原因截图
Enable *bool `form:"enable" json:"enable" binding:"required"`
Reason *string `form:"reason" json:"reason"`
LockAt *time.Time `form:"lockAt" json:"lockAt"` //封禁开始时间
DismissVip *bool `form:"dismissVip" json:"dismissVip"` //是否解除vip
}
var (
lockedSummary = ""
lockedPortrait = ""
lockBackground = []string{}
lockVipLevel = 0
lockSuperUser = false
lockOfficialCert = false
lockBirthday = ""
lockRegion = ""
)
func (req LockUserReq) Lock() stderr.Code {
now := time.Now()
lockAt := now
if req.LockAt != nil {
lockAt = *req.LockAt
}
doc := usermod.UserSelector{
HasLocked: req.Enable,
LockReason: req.Reason,
LockImg: req.LockImg,
LockAt: &lockAt,
//Portrait: &lockedPortrait,
//Summary: &lockedSummary,
//Background: &lockBackground,
//Birthday: &lockBirthday,
//SuperUser: &lockSuperUser,
//OfficialCert: &lockOfficialCert,
//Region: &lockRegion,
}
if req.Enable != nil && *req.Enable {
if req.DismissVip != nil && *req.DismissVip {
doc.VipLevel = &lockVipLevel
doc.VipExpireDate = &now
}
if req.LockAt != nil && req.LockAt.IsZero() {
zero := time.Time{}
doc.LockAt = &zero
}
} else {
zero := time.Time{}
doc.LockAt = &zero
}
if _, err := usermod.Update(req.UID, doc); err != nil {
return stderr.ErrDbUpdateError
}
if req.Enable != nil && *req.Enable {
common.Go(func() {
var cids []primitive.ObjectID
var pids []primitive.ObjectID
filter := bson.M{"userID": bson.M{"$in": req.UID}}
comments, err := cmtmod.FindList(filter, options.Find().SetLimit(1000).SetSkip(0))
if err != nil {
return
}
mData := map[cmtmod.OType][]primitive.ObjectID{}
// 玩家评论过的帖子 重新统计条数
for _, comment := range comments {
if comment == nil {
continue
}
if _, ok := mData[comment.ObjType]; !ok {
mData[comment.ObjType] = []primitive.ObjectID{comment.ObjID}
} else {
mData[comment.ObjType] = append(mData[comment.ObjType], comment.ObjID)
}
if !comment.CID.IsZero() {
pids = append(pids, comment.CID)
}
cids = append(cids, comment.ID)
}
// 删除评论
_ = cmtmod.DelCommentsByIDS(cids)
for oType, oids := range mData {
for _, oid := range oids {
n, err := cmtmod.GetCount(bson.M{"objType": oType, "objID": oid})
if err != nil {
continue
}
switch oType {
case cmtmod.OTypeVideo:
_ = vidmod.UpdateOne(oid, bson.M{"$set": bson.M{"commentCount": n}})
case cmtmod.OTypeCartoon:
_, _ = mediamod.UpdateByID(nil, oid, bson.M{"countComment": n})
case cmtmod.OTypeAiPlaza:
_, _ = aiplazamod.UpdateByID(nil, oid, bson.M{"commentCount": n})
}
}
}
if len(pids) > 0 {
cmtmod.CmtDecreaseCommentCountByID(pids...)
}
})
}
return stderr.Success
}
func CreditIntegral(uid uint64, integral int64, manager, reason string) error {
var tranType string
var tranTypeInt int64
var desc string
// var f func(t *db.MongoTool, amt int64, uid uint64) error
if integral > 0 { //上分
tranType = txnmod.AdminCreaditIntegral.Key()
tranTypeInt = int64(txnmod.AdminCreaditIntegral)
desc = txnmod.AdminCreaditIntegral.Key() + strconv.FormatInt(integral, 10)
} else if integral < 0 { //减分
tranType = txnmod.AdminDebitIntegral.Key()
tranTypeInt = int64(txnmod.AdminDebitIntegral)
desc = txnmod.AdminDebitIntegral.Key() + strconv.FormatInt(integral, 10)
} else { //不操作
return nil
}
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
integralOld, err := walletmod.GetIntegral(t, uid)
if err != nil {
return err
}
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.User_INIntegral,
BeforeContent: strconv.FormatInt(integralOld, 10),
AfterContent: strconv.FormatInt(integralOld+integral, 10),
Reason: reason,
UserID: int64(uid),
CreatedUser: manager,
}); err != nil {
return err
}
wallet, err := walletmod.CreditIntegral(t, integral, uid)
if err != nil {
return err
}
return txnmod.InsertTransactionLog(t, &(txnmod.TransactionLog{
UID: uid,
Integral: integral,
ActualAmount: float64(integral),
Tax: 0,
TaxAmount: 0,
TranType: tranType,
TranTypeInt: tranTypeInt,
Desc: desc,
RealIntegral: wallet.RealIntegral(),
}))
})
}