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 }