Files
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

331 lines
11 KiB
Go

package ai_undress_server
import (
"91porn-server/app/appg"
"91porn-server/app/service/aiautoser"
"91porn-server/common"
"91porn-server/common/constant/redisconst"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/middleware/ua"
"91porn-server/models/cache/sysconfdata"
"91porn-server/models/v/advanceordermod"
"91porn-server/models/v/aiUnDressmod"
"91porn-server/models/v/sysconfmod"
"91porn-server/models/v/txnmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/walletmod"
"fmt"
"strconv"
"time"
"github.com/go-redis/redis"
carbon "github.com/golang-module/carbon/v2"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
func List(uid uint64, req *aiUnDressmod.ListRequest) (interface{}, stderr.Code) {
var data = map[string]interface{}{
"list": []interface{}{},
"count": 0,
}
count, err := aiUnDressmod.CountDocument(req.Filter(uid))
if err != nil {
return nil, stderr.ErrDbQueryError
}
if count == 0 {
return data, stderr.Success
}
opts := options.Find().SetSkip(int64(req.Skip())).SetLimit(int64(req.Limit() + 1)).SetSort(bson.M{"updatedAt": -1})
list, err := aiUnDressmod.QueryAllDocument(req.Filter(uid), opts)
if err != nil {
return data, stderr.ErrDbQueryError
}
hasNext := false
if len(list) > int(req.Limit()) {
list = list[:req.Limit()]
hasNext = true
}
data["count"] = count // 兼容旧版本
data["hasNext"] = hasNext
data["list"] = list
return data, stderr.Success
}
func Generate(uid uint64, req *aiUnDressmod.GenerateRequest, ua ua.UA, ip string) stderr.Code {
if req.OriginPic == nil || len(req.OriginPic) == 0 {
return stderr.ErrParamError
}
// 获取用户信息
user, err := usermod.FindUserByUID(uid)
if err != nil {
return stderr.ErrDbQueryError
}
if user.ID.IsZero() {
return stderr.UserIsNotExists
}
configure, _ := sysconfdata.GetAllFromCache()
req.Coin = configure.GetInt(sysconfmod.VCodeAiUndressPrice)
picCount := int64(len(req.OriginPic))
privilegeFreeCount := int64(0)
// 获取用户是否是复购
isRepurchase, err := txnmod.CheckRepurchaseByTransTypes(uid, []txnmod.TransType{
txnmod.AiUndressDebitFreeTimes,
txnmod.AiUndressDebitGold,
txnmod.AiUndressDebitIncomeGold,
})
if err != nil {
log.Error("txnmod.CheckRepurchaseByTransTypes fail", log.E(err))
return stderr.ErrDbQueryError
}
hasPrivilege, advance := advanceordermod.GetUserValidOrder(uid)
if hasPrivilege {
// 获取可用免费次数
validCount := advance.PrepaidPrivilege.AiUndressLimitPerDay - advance.TodayUse.AiUndressCount
if validCount >= picCount {
// 扣除免费次数
debitPlan := advanceordermod.DebitPlan{}
debitPlan.AiUndressCount = &picCount
if err := advanceordermod.Debit(nil, uid, debitPlan); err != nil {
return stderr.ErrDbUpdateError
}
privilegeFreeCount = picCount
picCount = 0
} else if validCount > 0 {
// 扣除免费次数
debitPlan := advanceordermod.DebitPlan{}
debitPlan.AiUndressCount = &validCount
if err := advanceordermod.Debit(nil, uid, debitPlan); err != nil {
return stderr.ErrDbUpdateError
}
privilegeFreeCount = validCount
picCount -= validCount
}
if validCount > 0 {
l := &txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: uid,
Amount: 1,
ActualAmount: float64(-1),
TranType: txnmod.AiUndressDebitFreeTimes.Key(),
TranTypeInt: int64(txnmod.AiUndressDebitFreeTimes),
Desc: fmt.Sprintf("生成AI脱衣扣除免费次数[%d次]", privilegeFreeCount),
RealAmount: decimal.Decimal{},
SysType: user.SysType,
IsRepurchase: isRepurchase,
}
txnErr := txnmod.InsertTransactionLog(nil, l)
if txnErr != nil {
log.Error(fmt.Sprintf("Handle AiUnDress Generate txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
return stderr.ErrDbQueryError
}
}
}
var debitFreeCount, debitAmountCoins, debitIncomeCoins int64
if picCount > 0 {
// 获取用户钱包
w, err := walletmod.GetWallet(uid)
if err != nil {
return stderr.ErrDbQueryError
}
// 校验用户余额
if w == nil || !CheckBalance(req.Coin, picCount, *w) {
return stderr.InsufficientBalance
}
debitFreeCount, debitAmountCoins, debitIncomeCoins = TotalDebit(req.Coin, picCount, *w)
if w.AiUndressFreeTimes > 0 {
req.IsFreeTimes = true
}
}
var orderId primitive.ObjectID
var orderCreatedAt time.Time
if err = appg.VideoDB.Trans(func(tool *db.MongoTool) error {
var tl []txnmod.TransactionLog
if debitFreeCount > 0 {
expire := carbon.Tomorrow().StartOfDay().StdTime().Sub(time.Now())
// 累加次数
appg.Redis.IncrBy(redisconst.AiFreeUndressTodayUseTimesKey(uid), debitFreeCount, expire)
// 扣除钱剩余次数
wallet, wErr := walletmod.DebitAiFreeTimes(tool, debitFreeCount, uid)
if wErr != nil {
log.Error(fmt.Sprintf("Handle AiUnDress Generate walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
return stderr.ErrDbQueryError
}
tl = append(tl, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: uid,
Amount: 1,
ActualAmount: float64(-1),
TranType: txnmod.AiUndressDebitFreeTimes.Key(),
TranTypeInt: int64(txnmod.AiUndressDebitFreeTimes),
Desc: fmt.Sprintf("生成AI脱衣扣除免费次数[%d次]", debitFreeCount),
RealAmount: walletmod.GetRealAmount(wallet),
SysType: user.SysType,
IsRepurchase: isRepurchase,
})
}
if debitIncomeCoins > 0 || debitAmountCoins > 0 {
// 扣除钱包余额
wallet, wErr := walletmod.DebitAmountAndIncome(tool, debitAmountCoins, debitIncomeCoins, uid)
if wErr != nil {
log.Error(fmt.Sprintf("Handle AiUnDress Generate walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
return stderr.ErrDbQueryError
}
if debitAmountCoins > 0 {
tl = append(tl, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: uid,
Amount: -debitAmountCoins,
ActualAmount: float64(-debitAmountCoins),
TranType: txnmod.AiUndressDebitGold.Key(),
TranTypeInt: int64(txnmod.AiUndressDebitGold),
Desc: fmt.Sprintf("生成AI脱衣金币-%d", debitAmountCoins),
RealAmount: walletmod.GetRealAmount(wallet),
SysType: user.SysType,
IsRepurchase: isRepurchase,
})
}
if debitIncomeCoins > 0 {
tl = append(tl, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: uid,
Amount: -debitIncomeCoins,
ActualAmount: float64(-debitIncomeCoins),
TranType: txnmod.AiUndressDebitIncomeGold.Key(),
TranTypeInt: int64(txnmod.AiUndressDebitIncomeGold),
Desc: fmt.Sprintf("生成AI脱衣收益金币-%d", debitIncomeCoins),
RealAmount: walletmod.GetRealAmount(wallet),
SysType: user.SysType,
IsRepurchase: isRepurchase,
})
}
}
if len(tl) > 0 {
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
if txnErr != nil {
log.Error(fmt.Sprintf("Handle AiUnDress Generate txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
return stderr.ErrDbQueryError
}
}
// 新增AI脱衣记录
orderId = primitive.NewObjectID()
orderCreatedAt = time.Now()
if err := aiUnDressmod.InsertMany(tool, req.GenerateMany(orderId, orderCreatedAt, uid, privilegeFreeCount, debitFreeCount, debitAmountCoins, debitIncomeCoins, req.Coin, req.ShareTitle, req.ShareStatus)); err != nil {
log.Error(fmt.Sprintf("Handle AiUnDress Generate InsertOnes error:%+v:", err), log.Any("uid", uid))
return stderr.ErrDbInsertError
}
return nil
}); err != nil {
log.Error(fmt.Sprintf("Handle AiUnDress Generate Trans error:%+v;uid:%v;", err, uid))
return stderr.ErrDbUpdateError
}
common.Go(func() {
if err := aiautoser.SubmitUndress(orderId); err != nil {
log.Error("AI脱衣自动处理失败", log.Any("id", orderId.Hex()), log.E(err))
}
})
return stderr.Success
}
func AiUndressHide(uid uint64, req *aiUnDressmod.DelRequest) stderr.Code {
acf, err := aiUnDressmod.FindByID(nil, req.ID)
if err != nil {
return stderr.ErrDbQueryError
}
if acf.ID.IsZero() {
return stderr.CodeEmptyData
}
if acf.UID != uid {
return stderr.Failure
}
if acf.Status == aiUnDressmod.Processing || acf.Status == aiUnDressmod.SubmitOrder {
return stderr.AiGenningDelForbidden
}
if err = aiUnDressmod.Edit(nil, req.Filter(), req.Update()); err != nil {
return stderr.ErrDbQueryError
}
return stderr.Success
}
func TotalDebit(price int64, count int64, w walletmod.Wallet) (fCount, amountCoins, incomeCoins int64) {
var (
debitFreeTimes int64
debitAmountCoins int64
debitIncomeCoins int64
)
// 当日剩余ai免费脱衣次数
todayAiUndressFreeTimes := GetTodayRemainingAiFreeUndressTimes(w)
// 实际使用的免费脱衣次数
if todayAiUndressFreeTimes >= count {
return count, 0, 0
}
debitFreeTimes = todayAiUndressFreeTimes
// 计算当日实际扣减的金额
debitCoins := (count - debitFreeTimes) * price
if w.Amount >= debitCoins {
debitAmountCoins = debitCoins
} else {
debitAmountCoins = w.Amount
debitIncomeCoins = debitCoins - w.Amount
}
return debitFreeTimes, debitAmountCoins, debitIncomeCoins
}
// GetTodayRemainingAiFreeUndressTimes 获取当日剩余的免费ai脱衣的次数
func GetTodayRemainingAiFreeUndressTimes(w walletmod.Wallet) (num int64) {
var todayUseAiUndressFreeTimes int // 当日已使用的免费ai脱衣次数
//获取当日已经使用的ai免费脱衣次数
str, err := appg.Redis.Get(redisconst.AiFreeUndressTodayUseTimesKey(w.UID))
if err != nil && err != redis.Nil {
log.Error(fmt.Sprintf("get AiUndressTodayUseTimes err%v", err))
// redis报错返回已经当日最大限制次数,避免被刷
todayUseAiUndressFreeTimes = 0
} else if str != nil {
todayUseAiUndressFreeTimes, _ = strconv.Atoi(*str)
}
num = int64(appg.Conf.AiFreeUnDressDailyLimit - todayUseAiUndressFreeTimes)
// 如果用户剩余的免费次数不足,以剩余次数为准
if num >= w.AiUndressFreeTimes {
num = w.AiUndressFreeTimes
}
if num < 0 {
num = 0
}
return
}
func CheckBalance(price int64, count int64, w walletmod.Wallet) bool {
var checkStats bool
var totalCoins = w.Amount
if w.Income != 0 {
totalCoins += w.Income
}
if w.AiUndressFreeTimes > 0 && w.AiUndressFreeTimes >= count {
checkStats = true
}
if w.AiUndressFreeTimes > 0 && w.AiUndressFreeTimes < count && totalCoins >= price*(count-w.AiUndressFreeTimes) {
checkStats = true
}
if w.AiUndressFreeTimes <= 0 && totalCoins >= price*count {
checkStats = true
}
return checkStats
}