385 lines
11 KiB
Go
385 lines
11 KiB
Go
package ai_changeface_ser
|
|
|
|
import (
|
|
"91porn-server/app/appg"
|
|
"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/aichangefacemod"
|
|
"91porn-server/models/v/aichangefacevidmod"
|
|
"91porn-server/models/v/backpackmod"
|
|
"91porn-server/models/v/txnmod"
|
|
"91porn-server/models/v/usermod"
|
|
"91porn-server/models/v/walletmod"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
func List(uid uint64, status *aichangefacemod.AiChangeFaceStatus, skip, limit int) ([]aichangefacemod.AiChangeFace, bool, error) {
|
|
acfs, err := aichangefacemod.ListByUid(uid, status, skip, limit+1)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
hasNext := false
|
|
if len(acfs) > limit {
|
|
acfs = acfs[:limit]
|
|
hasNext = true
|
|
}
|
|
return acfs, hasNext, nil
|
|
}
|
|
|
|
func Generate(uid uint64, pics []string, vidModId primitive.ObjectID, discount []primitive.ObjectID, shareTitle string, shareStatus int, ua ua.UA, ip string) stderr.Code {
|
|
vidMod, err := aiService.NewAiService(
|
|
aiService.AppId(int(commod.KFK_APPID)),
|
|
aiService.Url(appg.Conf.URL.AiServer),
|
|
aiService.Redis(appg.Redis),
|
|
).GetTemplate(vidModId)
|
|
if vidMod.ID.IsZero() {
|
|
return stderr.Failure
|
|
}
|
|
var discountValue int64
|
|
now := time.Now()
|
|
var dsId []primitive.ObjectID
|
|
for _, d := range discount {
|
|
dsId = append(dsId, d)
|
|
bp, err := backpackmod.GetByID(nil, d)
|
|
if err != nil {
|
|
return stderr.ErrDbQueryError
|
|
}
|
|
if bp == nil { // 抵扣券不存在
|
|
return stderr.Failure
|
|
}
|
|
if bp.GoodsType != backpackmod.AiChangeFaceDiscount { // 抵扣券类型不对
|
|
return stderr.Failure
|
|
}
|
|
if bp.Status == backpackmod.Used { // 抵扣券已被使用
|
|
return stderr.Failure
|
|
}
|
|
if bp.ExpiredTime.Before(now) { // 抵扣券已过期
|
|
return stderr.Failure
|
|
}
|
|
discountValue += bp.GoodsValue
|
|
}
|
|
originPrice := vidMod.Coin
|
|
|
|
user, err := usermod.FindUserByUID(uid)
|
|
if err != nil {
|
|
return stderr.ErrDbQueryError
|
|
}
|
|
|
|
if user.IsVIP(time.Now()) {
|
|
originPrice = vidMod.VipCoin
|
|
}
|
|
|
|
price := int64(originPrice) - discountValue
|
|
if price < 0 {
|
|
price = 0
|
|
}
|
|
|
|
// 获取钱包信息
|
|
wlt, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return stderr.ErrDbQueryError
|
|
}
|
|
|
|
if wlt == nil || wlt.Amount+wlt.Income < price {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
|
|
// 扣除的普通金币和收益金币
|
|
debitAmt, debitIncome := TotalDebit(price, wlt)
|
|
// 获取用户是否是复购
|
|
isRepurchase, err := txnmod.CheckRepurchaseByTransTypes(uid, []txnmod.TransType{
|
|
txnmod.AiChangefaceDebitGold,
|
|
txnmod.AiChangefaceDebitInComeGold,
|
|
})
|
|
if err != nil {
|
|
log.Error("txnmod.CheckRepurchaseByTransTypes fail", log.E(err))
|
|
return stderr.ErrDbQueryError
|
|
}
|
|
var orderId primitive.ObjectID
|
|
var orderCreatedAt time.Time
|
|
if err = appg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
|
var tl []txnmod.TransactionLog
|
|
if debitAmt > 0 || debitIncome > 0 {
|
|
// 扣除钱包余额
|
|
wallet, err := walletmod.DebitAmountAndIncome(tool, debitAmt, debitIncome, uid)
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("Handle AiChangeface Generate walletmod.DebitAmount error:%+v:", err), log.Any("uid", uid))
|
|
if err.Error() == "no enough balance" {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
return stderr.ErrDbUpdateError
|
|
}
|
|
|
|
if debitAmt > 0 {
|
|
tl = append(tl, txnmod.TransactionLog{
|
|
TransNo: primitive.NewObjectID(),
|
|
UID: uid,
|
|
Amount: -debitAmt,
|
|
ActualAmount: -float64(debitAmt),
|
|
TranType: txnmod.AiChangefaceDebitGold.Key(),
|
|
TranTypeInt: int64(txnmod.AiChangefaceDebitGold),
|
|
Desc: fmt.Sprintf("生成AI换脸金币-%d", debitAmt),
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
SysType: user.SysType,
|
|
IsRepurchase: isRepurchase,
|
|
})
|
|
}
|
|
|
|
if debitIncome > 0 {
|
|
tl = append(tl, txnmod.TransactionLog{
|
|
TransNo: primitive.NewObjectID(),
|
|
UID: uid,
|
|
Amount: -debitIncome,
|
|
ActualAmount: -float64(debitIncome),
|
|
TranType: txnmod.AiChangefaceDebitGold.Key(),
|
|
TranTypeInt: int64(txnmod.AiChangefaceDebitGold),
|
|
Desc: fmt.Sprintf("生成AI换脸收益金币-%d", debitIncome),
|
|
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 AiChangeface Generate txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
|
return stderr.ErrDbQueryError
|
|
}
|
|
}
|
|
|
|
// 抵扣券使用
|
|
if len(dsId) > 0 {
|
|
modified, err := backpackmod.UseManyGoods(tool, dsId)
|
|
if err != nil {
|
|
return stderr.ErrDbUpdateError
|
|
}
|
|
if modified != int64(len(dsId)) {
|
|
return stderr.Failure
|
|
}
|
|
}
|
|
|
|
// 新增AI脱衣记录
|
|
orderId = primitive.NewObjectID()
|
|
orderCreatedAt = time.Now()
|
|
if err = aichangefacemod.AddAiChangeFace(tool, orderId, orderCreatedAt, uid, pics, vidMod, debitAmt, debitIncome, discount, shareTitle, shareStatus); err != nil {
|
|
log.Error(fmt.Sprintf("Handle AiChangeface Generate InsertOnes error:%+v:", err), log.Any("uid", uid))
|
|
return stderr.ErrDbInsertError
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
if e, ok := err.(stderr.Code); ok {
|
|
return e
|
|
}
|
|
return stderr.ErrDbQueryError
|
|
}
|
|
common.Go(func() {
|
|
if err := aiautoser.SubmitVideo(orderId); err != nil {
|
|
log.Error("AI视频换脸自动处理失败", log.Any("id", orderId.Hex()), log.E(err))
|
|
}
|
|
})
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func TotalDebit(coins int64, w *walletmod.Wallet) (amtDebit, income int64) {
|
|
var (
|
|
deAmt int64
|
|
deIncome int64
|
|
)
|
|
if w.Amount >= coins {
|
|
deAmt = coins
|
|
}
|
|
if w.Amount < coins && w.Amount+w.Income >= coins {
|
|
deAmt = w.Amount
|
|
deIncome = coins - w.Amount
|
|
}
|
|
return deAmt, deIncome
|
|
}
|
|
|
|
// ModList 获取模版列表
|
|
func ModList(uid uint64) (data *aichangefacevidmod.AppResponse, err error) {
|
|
var ret aichangefacevidmod.AppResponse
|
|
key := redisconst.AIModCache
|
|
str, err := appg.Redis.Get(key)
|
|
if err != nil {
|
|
log.Warn(fmt.Sprintf("用户ID:%d;缓存获取AI模版列表信息异常:%v", uid, err))
|
|
}
|
|
|
|
if str != nil {
|
|
if err = json.Unmarshal([]byte(*str), &ret); err == nil {
|
|
return &ret, nil
|
|
}
|
|
log.Warn(fmt.Sprintf("用户ID:%d;解析缓存数据异常:%v", uid, err))
|
|
}
|
|
|
|
mods, err := aichangefacevidmod.GetAllMods()
|
|
if err != nil {
|
|
return &ret, err
|
|
}
|
|
|
|
if len(mods) > 0 {
|
|
for _, m := range mods {
|
|
if m.ModuleType == aichangefacevidmod.AIUndress {
|
|
ret.AiUndressMod = append(ret.AiUndressMod, aichangefacevidmod.AiUndressMod{
|
|
ID: m.ID,
|
|
Cover: m.Cover,
|
|
})
|
|
}
|
|
if m.ModuleType == aichangefacevidmod.AIImgToVideo {
|
|
ret.AiImgToVideoMod = append(ret.AiImgToVideoMod, aichangefacevidmod.AiImgToVideoMod{
|
|
ID: m.ID,
|
|
Title: m.Title,
|
|
SceneType: m.SceneType,
|
|
Cover: m.Cover,
|
|
NewUrl: m.NewUrl,
|
|
})
|
|
}
|
|
if m.ModuleType == aichangefacevidmod.AITextToImage {
|
|
ret.AiTextToImgMod = append(ret.AiTextToImgMod, aichangefacevidmod.AiTextToImgMod{
|
|
ID: m.ID,
|
|
Cover: m.Cover,
|
|
Title: m.Title,
|
|
StyleType: m.HotValue,
|
|
})
|
|
}
|
|
//if m.ModuleType == aichangefacevidmod.AIVideoChangeFace {
|
|
// ret.AiChangeFaceVideoMod = append(ret.AiChangeFaceVideoMod, aichangefacevidmod.AiChangeMod{
|
|
// ID: m.ID,
|
|
// Title: m.Title,
|
|
// SourceURL: m.SourceURL,
|
|
// Cover: m.Cover,
|
|
// PlayTime: m.PlayTime,
|
|
// Type: m.Type,
|
|
// HotMark: m.HotMark,
|
|
// HotValue: m.HotValue,
|
|
// Coin: m.Coin,
|
|
// VipCoin: m.VipCoin,
|
|
// })
|
|
//}
|
|
//if m.ModuleType == aichangefacevidmod.AIImgChangeFace {
|
|
// ret.AiChangeFaceMod = append(ret.AiChangeFaceMod, aichangefacevidmod.AiChangeFaceMod{
|
|
// Title: m.Title,
|
|
// ID: m.ID,
|
|
// Cover: m.Cover,
|
|
// HotMark: m.HotMark,
|
|
// HotValue: m.HotValue,
|
|
// Coin: m.Coin,
|
|
// VipCoin: m.VipCoin,
|
|
// })
|
|
//}
|
|
}
|
|
}
|
|
common.Go(func() {
|
|
d, err := json.Marshal(ret)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if err = appg.Redis.Set(key, d, 5*time.Minute); err != nil {
|
|
log.Warn(fmt.Sprintf("用户ID:%d;保存缓存数据异常:%v", uid, err))
|
|
}
|
|
})
|
|
return &ret, nil
|
|
}
|
|
|
|
type ModListV2Req struct {
|
|
Type int `json:"type" form:"type"` // 0-图片换脸 1-视频换脸
|
|
CategoryId string `json:"categoryId" form:"categoryId"` // 模版分类id,如果为空,则默认第一个模版分类
|
|
}
|
|
|
|
type ModListV2Resp struct {
|
|
CategoryList []*aiService.Category `json:"categoryList"`
|
|
TemplateList []*aiService.Template `json:"templateList"`
|
|
CategoryId string `json:"categoryId"` // 当前返回的分类id下的模版
|
|
}
|
|
|
|
// ModListV2 获取模版列表
|
|
func ModListV2(req ModListV2Req) (resp ModListV2Resp, err error) {
|
|
ai := aiService.NewAiService(
|
|
aiService.AppId(int(commod.KFK_APPID)),
|
|
aiService.Url(appg.Conf.URL.AiServer),
|
|
aiService.Redis(appg.Redis),
|
|
)
|
|
resp.CategoryList, resp.CategoryId, resp.TemplateList, err = ai.GetTemplateList(req.Type, req.CategoryId)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
type ModInfoReq struct {
|
|
Id string `json:"id" form:"id"`
|
|
Type int `json:"type" form:"type"` // 1-ai图片换脸 2-ai视频换脸 3-ai脱衣 4-ai图生视频 5-ai绘画
|
|
}
|
|
|
|
type ModInfoResp struct {
|
|
AiUndressMod aichangefacevidmod.AiUndressMod `json:"aiUndressMod" bson:"aiUndressMod"` // AI脱衣模版
|
|
AiTextToImgMod aichangefacevidmod.AiTextToImgMod `json:"aiTextToImgMod" bson:"aiTextToImgMod"` // AI绘图模型
|
|
AiImgToVideoMod aichangefacevidmod.AiImgToVideoMod `json:"aiImgToVideoMod" bson:"aiImgToVideoMod"` // AI图生视频模型
|
|
AiChangeFaceMod aiService.Template `json:"aiChangeFaceMod" bson:"aiChangeFaceMod"` // Ai视频/图片换脸模型
|
|
}
|
|
|
|
func (p *ModInfoReq) GetInfo() (resp ModInfoResp, err error) {
|
|
id, _ := primitive.ObjectIDFromHex(p.Id)
|
|
if id.IsZero() {
|
|
return resp, errors.New("id无效")
|
|
}
|
|
if p.Type > 2 {
|
|
// 从本地的表里获取
|
|
m, err := aichangefacevidmod.GetModById(id)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
if m.ModuleType == aichangefacevidmod.AIUndress {
|
|
resp.AiUndressMod = aichangefacevidmod.AiUndressMod{
|
|
ID: m.ID,
|
|
Cover: m.Cover,
|
|
}
|
|
}
|
|
if m.ModuleType == aichangefacevidmod.AIImgToVideo {
|
|
resp.AiImgToVideoMod = aichangefacevidmod.AiImgToVideoMod{
|
|
ID: m.ID,
|
|
Cover: m.Cover,
|
|
NewUrl: m.NewUrl,
|
|
}
|
|
}
|
|
if m.ModuleType == aichangefacevidmod.AITextToImage {
|
|
resp.AiTextToImgMod = aichangefacevidmod.AiTextToImgMod{
|
|
ID: m.ID,
|
|
Cover: m.Cover,
|
|
Title: m.Title,
|
|
StyleType: m.HotValue,
|
|
}
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// 获取视频/图片换脸模版
|
|
ai := aiService.NewAiService(
|
|
aiService.AppId(int(commod.KFK_APPID)),
|
|
aiService.Url(appg.Conf.URL.AiServer),
|
|
aiService.Redis(appg.Redis),
|
|
)
|
|
template, err := ai.GetTemplate(id)
|
|
if err != nil {
|
|
log.Error("ai.GetTemplate fail", log.Any("id", id), log.E(err))
|
|
return
|
|
}
|
|
resp.AiChangeFaceMod = template
|
|
return
|
|
}
|