Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
@@ -0,0 +1,329 @@
package ai_changeface_img_ser
import (
"91porn-server/app/appg"
"91porn-server/app/service/ai_undress_server"
"91porn-server/app/service/aiautoser"
"91porn-server/common"
"91porn-server/common/aiService"
"91porn-server/common/constant/redisconst"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/middleware/ua"
"91porn-server/models/commod"
"91porn-server/models/v/advanceordermod"
"91porn-server/models/v/aichangefaceimgmod"
"91porn-server/models/v/txnmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/walletmod"
"fmt"
"time"
"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 *aichangefaceimgmod.ListRequest) (interface{}, stderr.Code) {
var data = map[string]interface{}{
"list": []interface{}{},
"count": 0,
}
count, err := aichangefaceimgmod.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{"createdAt": -1})
list, err := aichangefaceimgmod.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 *aichangefaceimgmod.GenerateRequest, ua ua.UA, ip string) stderr.Code {
// 获取AI模版图
id, err := primitive.ObjectIDFromHex(req.MId)
if err != nil {
return stderr.ErrParamError
}
mod, err := aiService.NewAiService(
aiService.AppId(int(commod.KFK_APPID)),
aiService.Url(appg.Conf.URL.AiServer),
aiService.Redis(appg.Redis),
).GetTemplate(id)
if mod.ID.IsZero() {
return stderr.CodeEmptyData
}
req.Coin = int64(mod.Coin)
user, err := usermod.FindUserByUID(uid)
if err != nil {
return stderr.ErrDbQueryError
}
if user.IsVIP(time.Now()) {
req.Coin = int64(mod.VipCoin)
}
// 获取用户是否是复购
isRepurchase, err := txnmod.CheckRepurchaseByTransTypes(uid, []txnmod.TransType{
txnmod.AiChangeFaceImgDebitFreeTimes,
txnmod.AiChangeFaceImgDebitGold,
txnmod.AiChangeFaceImgDebitIncomeGold,
})
if err != nil {
log.Error("txnmod.CheckRepurchaseByTransTypes fail", log.E(err))
return stderr.ErrDbQueryError
}
privilegeFreeCount := int64(0)
picCount := int64(1)
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.AiChangeFaceImgDebitFreeTimes.Key(),
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimes),
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 {
return stderr.InsufficientBalance
}
debitFreeCount, debitAmountCoins, debitIncomeCoins = ai_undress_server.TotalDebit(req.Coin, picCount, *w)
if debitFreeCount > 0 {
req.IsFreeTimes = true
}
// 如果收费的部分余额不够支付
if req.Coin*(picCount-debitFreeCount) > (w.Amount + w.Income) {
return stderr.InsufficientBalance
}
}
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())
// 累加次数(跟ai脱衣共用这个次数)
appg.Redis.IncrBy(redisconst.AiFreeUndressTodayUseTimesKey(uid), debitFreeCount, expire)
// 扣除钱剩余次数
wallet, wErr := walletmod.DebitAiFreeTimes(tool, debitFreeCount, uid)
if wErr != nil {
log.Error(fmt.Sprintf("Handle AIChangeFaceImg 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.AiChangeFaceImgDebitFreeTimes.Key(),
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimes),
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.AiChangeFaceImgDebitGold.Key(),
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitGold),
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.AiChangeFaceImgDebitIncomeGold.Key(),
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitIncomeGold),
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 = aichangefaceimgmod.InsertOne(tool, req.Generate(orderId, orderCreatedAt, uid, mod.Title, mod.Cover, privilegeFreeCount, debitFreeCount, debitAmountCoins, debitIncomeCoins)); err != nil {
log.Error(fmt.Sprintf("Handle Aichangefaceimg Generate InsertOnes error:%+v:", err), log.Any("uid", uid))
return stderr.ErrDbInsertError
}
return nil
}); err != nil {
log.Error(fmt.Sprintf("Handle Aichangefaceimg Generate Trans error:%+v;uid:%v;", err, uid))
return stderr.ErrDbUpdateError
}
common.Go(func() {
if err := aiautoser.SubmitImage(orderId); err != nil {
log.Error("AI图片换脸自动处理失败", log.Any("id", orderId.Hex()), log.E(err))
}
})
return stderr.Success
}
func AiChangeFaceImgHide(uid uint64, req *aichangefaceimgmod.DelRequest) stderr.Code {
acf, err := aichangefaceimgmod.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 == aichangefaceimgmod.Processing || acf.Status == aichangefaceimgmod.StatusSubmit {
return stderr.AiGenningDelForbidden
}
if err = aichangefaceimgmod.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
)
if w.AiUndressFreeTimes > 0 && w.AiUndressFreeTimes >= count {
debitFreeTimes = count
}
if w.AiUndressFreeTimes > 0 && w.AiUndressFreeTimes < count {
debitFreeTimes = w.AiUndressFreeTimes
debitCoins := (count - debitFreeTimes) * price
if w.Amount >= debitCoins {
debitAmountCoins = debitCoins
}
if w.Amount < debitCoins {
debitAmountCoins = w.Amount
debitIncomeCoins = debitCoins - w.Amount
}
}
if w.AiUndressFreeTimes <= 0 {
debitCoins := price * count
if w.Amount >= debitCoins {
debitAmountCoins = debitCoins
}
if w.Amount < debitCoins {
debitAmountCoins = w.Amount
debitIncomeCoins = debitCoins - w.Amount
}
}
return debitFreeTimes, debitAmountCoins, debitIncomeCoins
}
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
}