Files
huangguo_server/app/service/aiautoser/automation.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

330 lines
11 KiB
Go

package aiautoser
import (
"errors"
"fmt"
"strconv"
"91porn-server/app/appg"
"91porn-server/common"
"91porn-server/common/db"
"91porn-server/common/httputil"
"91porn-server/common/log"
"91porn-server/models/commod"
"91porn-server/models/v/aiUnDressmod"
"91porn-server/models/v/aichangefaceimgmod"
"91porn-server/models/v/aichangefacemod"
"91porn-server/models/v/backpackmod"
"91porn-server/models/v/txnmod"
"91porn-server/models/v/walletmod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
const autoOperator = "AI_AUTO"
type changeFaceRequest struct {
AppID int `json:"app_id"`
OrderNumApp string `json:"order_num_app"`
Title string `json:"title"`
SourceImageURL string `json:"source_image_url"`
NotifyURL string `json:"notify_url"`
}
type changeFaceResponse struct {
Code int `json:"code"`
}
// SubmitVideo automatically approves and submits an AI video face-swap order.
// Submission failures are handled with the same refund policy as the existing
// Web callback flow.
func SubmitVideo(id primitive.ObjectID) error {
order, err := aichangefacemod.FindByID(nil, id)
if err != nil {
return err
}
transitioned, err := aichangefacemod.TransitionStatus(nil, id, aichangefacemod.StatusGenning, aichangefacemod.StatusSubmit, bson.M{
"operator": autoOperator,
"remark": "已自动提交",
})
if err != nil || !transitioned {
return err
}
notifyURL, notifyErr := appg.AICallbackURL("/api/web/admin/ai/changeface/callback")
if notifyErr != nil || len(order.Pic) == 0 || order.Pic[0] == "" || appg.Conf.URL.AiServer == "" {
return refundVideo(id, "自动提交参数不完整")
}
req := changeFaceRequest{
AppID: int(commod.KFK_APPID),
OrderNumApp: id.Hex(),
Title: order.ModTitle,
SourceImageURL: order.Pic[0],
NotifyURL: notifyURL,
}
if err := postChangeFace(req); err != nil {
log.Error("AI视频换脸自动提交失败", log.Any("id", id.Hex()), log.E(err))
return refundVideo(id, err.Error())
}
return nil
}
// SubmitImage automatically approves and submits an AI image face-swap order.
func SubmitImage(id primitive.ObjectID) error {
order, err := aichangefaceimgmod.FindByID(nil, id)
if err != nil {
return err
}
transitioned, err := aichangefaceimgmod.TransitionStatus(nil, id, aichangefaceimgmod.Processing, aichangefaceimgmod.StatusSubmit, bson.M{
"updateAct": autoOperator,
"remark": "已自动提交",
})
if err != nil || !transitioned {
return err
}
notifyURL, notifyErr := appg.AICallbackURL("/api/web/admin/ai/change_face_img/callback")
if notifyErr != nil || order.OriginPic == "" || appg.Conf.URL.AiServer == "" {
return refundImage(id, "自动提交参数不完整")
}
req := changeFaceRequest{
AppID: int(commod.KFK_APPID),
OrderNumApp: id.Hex(),
Title: order.ModTitle,
SourceImageURL: order.OriginPic,
NotifyURL: notifyURL,
}
if err := postChangeFace(req); err != nil {
log.Error("AI图片换脸自动提交失败", log.Any("id", id.Hex()), log.E(err))
return refundImage(id, err.Error())
}
return nil
}
// SubmitUndress automatically approves and submits an AI undress order.
func SubmitUndress(id primitive.ObjectID) error {
order, err := aiUnDressmod.FindByID(nil, id)
if err != nil {
return err
}
transitioned, err := aiUnDressmod.TransitionStatus(nil, id, aiUnDressmod.Processing, aiUnDressmod.SubmitOrder, bson.M{
"updateAct": autoOperator,
"remark": "已自动提交",
})
if err != nil || !transitioned {
return err
}
notifyURL, notifyErr := appg.AICallbackURL("/api/web/admin/ai/undress/callback")
if notifyErr != nil || len(order.OriginPics) == 0 || appg.Conf.URL.AiUndressServer == "" {
return refundUndress(id, "自动提交参数不完整")
}
req := aiUnDressmod.AiUndressOrderReq{
AppId: int(commod.KFK_APPID),
FileUrl: order.OriginPics,
UserId: strconv.FormatUint(order.UID, 10),
AppOrderNum: id.Hex(),
NotifyUrl: notifyURL,
}
var resp aiUnDressmod.AiUndressOrderResp
statusCode, err := httputil.DefaultClientPostJsonWithResp(
&resp,
common.BindUrl(appg.Conf.URL.AiUndressServer, "/api/undress/prd/create_job"),
nil,
req,
)
if err != nil || statusCode != 200 {
if err == nil {
err = fmt.Errorf("third-party HTTP status %d", statusCode)
}
log.Error("AI脱衣自动提交失败", log.Any("id", id.Hex()), log.E(err))
return refundUndress(id, err.Error())
}
return nil
}
func postChangeFace(req changeFaceRequest) error {
var resp changeFaceResponse
statusCode, err := httputil.DefaultClientPostJsonWithResp(
&resp,
common.BindUrl(appg.Conf.URL.AiServer, "/api/changeface/prd/changeFaceByVideo"),
nil,
req,
)
if err != nil {
return err
}
if statusCode != 200 {
return fmt.Errorf("third-party HTTP status %d", statusCode)
}
if resp.Code == 40000 {
return errors.New("third-party template is unavailable")
}
return nil
}
func refundVideo(id primitive.ObjectID, remark string) error {
return appg.VideoDB.Trans(func(tool *db.MongoTool) error {
order, err := aichangefacemod.FindByID(tool, id)
if err != nil {
return err
}
transitioned, err := aichangefacemod.TransitionStatus(tool, id, aichangefacemod.StatusSubmit, aichangefacemod.StatusRefund, bson.M{
"operator": autoOperator,
"remark": remark,
})
if err != nil || !transitioned {
return err
}
if len(order.Discount) > 0 {
if _, err = backpackmod.UnuseManyGoods(tool, order.Discount); err != nil {
return err
}
}
if order.Coin == 0 && order.IncomeCoin == 0 {
return nil
}
wallet, err := walletmod.ReturnAmountAndIncome(tool, -order.Coin, -order.IncomeCoin, order.Uid)
if err != nil {
return err
}
totalCoin := order.Coin + order.IncomeCoin
return txnmod.InsertTransactionLog(tool, &txnmod.TransactionLog{
TransNo: primitive.NewObjectID(),
UID: order.Uid,
Amount: totalCoin,
ActualAmount: float64(totalCoin),
TranType: txnmod.AiChangefaceDebitGoldReturn.Key(),
TranTypeInt: int64(txnmod.AiChangefaceDebitGoldReturn),
Desc: fmt.Sprintf("AI换脸金币退返-%d", totalCoin),
RealAmount: walletmod.GetRealAmount(wallet),
})
})
}
func refundImage(id primitive.ObjectID, remark string) error {
return appg.VideoDB.Trans(func(tool *db.MongoTool) error {
order, err := aichangefaceimgmod.FindByID(tool, id)
if err != nil {
return err
}
transitioned, err := aichangefaceimgmod.TransitionStatus(tool, id, aichangefaceimgmod.StatusSubmit, aichangefaceimgmod.REFUND, bson.M{
"updateAct": autoOperator,
"remark": remark,
})
if err != nil || !transitioned {
return err
}
return refundImageBalance(tool, order)
})
}
func refundImageBalance(tool *db.MongoTool, order *aichangefaceimgmod.AiChangeFaceImg) error {
logs := make([]txnmod.TransactionLog, 0, 3)
if order.Count > 0 || order.IsFreeTimes {
count := order.Count
if count == 0 {
count = 1
}
wallet, err := walletmod.DebitAiFreeTimes(tool, -count, order.UID)
if err != nil {
return err
}
logs = append(logs, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(), UID: order.UID, Amount: count, ActualAmount: float64(count),
TranType: txnmod.AiChangeFaceImgDebitFreeTimesReturn.Key(), TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimesReturn),
Desc: fmt.Sprintf("AI图片换脸免费次数退返[%d次]", count), RealAmount: walletmod.GetRealAmount(wallet),
})
}
if order.DebitAmountCoin > 0 || order.DebitIncomeCoin > 0 {
wallet, err := walletmod.ReturnAmountAndIncome(tool, -order.DebitAmountCoin, -order.DebitIncomeCoin, order.UID)
if err != nil {
return err
}
if order.DebitIncomeCoin > 0 {
logs = append(logs, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(), UID: order.UID, Amount: order.DebitIncomeCoin, ActualAmount: float64(order.DebitIncomeCoin),
TranType: txnmod.AiChangeFaceImgReturnIncomeGold.Key(), TranTypeInt: int64(txnmod.AiChangeFaceImgReturnIncomeGold),
Desc: fmt.Sprintf("AI图片换脸收益金币退返-%d", order.DebitIncomeCoin), RealAmount: walletmod.GetRealAmount(wallet),
})
}
if order.DebitAmountCoin > 0 {
logs = append(logs, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(), UID: order.UID, Amount: order.DebitAmountCoin, ActualAmount: float64(order.DebitAmountCoin),
TranType: txnmod.AiChangeFaceImgReturnGold.Key(), TranTypeInt: int64(txnmod.AiChangeFaceImgReturnGold),
Desc: fmt.Sprintf("AI图片换脸金币退返-%d", order.DebitAmountCoin), RealAmount: walletmod.GetRealAmount(wallet),
})
}
}
if len(logs) == 0 {
return nil
}
return txnmod.InsertManyTransactionLog(tool, logs)
}
func refundUndress(id primitive.ObjectID, remark string) error {
return appg.VideoDB.Trans(func(tool *db.MongoTool) error {
order, err := aiUnDressmod.FindByID(tool, id)
if err != nil {
return err
}
transitioned, err := aiUnDressmod.TransitionStatus(tool, id, aiUnDressmod.SubmitOrder, aiUnDressmod.REFUND, bson.M{
"updateAct": autoOperator,
"remark": remark,
})
if err != nil || !transitioned {
return err
}
return refundUndressBalance(tool, order)
})
}
func refundUndressBalance(tool *db.MongoTool, order *aiUnDressmod.AiUnDress) error {
logs := make([]txnmod.TransactionLog, 0, 3)
if order.Count > 0 || order.IsFreeTimes {
count := order.Count
if count == 0 {
count = 1
}
wallet, err := walletmod.DebitAiFreeTimes(tool, -count, order.UID)
if err != nil {
return err
}
logs = append(logs, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(), UID: order.UID, Amount: count, ActualAmount: float64(count),
TranType: txnmod.AiUndressDebitFreeTimesReturn.Key(), TranTypeInt: int64(txnmod.AiUndressDebitFreeTimesReturn),
Desc: fmt.Sprintf("AI脱衣免费次数退返[%d次]", count), RealAmount: walletmod.GetRealAmount(wallet),
})
}
debitAmount := order.DebitAmountCoin
debitIncome := order.DebitIncomeCoin
if !order.IsFreeTimes && debitAmount == 0 && debitIncome == 0 {
debitAmount = order.Coin
}
if debitAmount > 0 || debitIncome > 0 {
wallet, err := walletmod.ReturnAmountAndIncome(tool, -debitAmount, -debitIncome, order.UID)
if err != nil {
return err
}
if debitIncome > 0 {
logs = append(logs, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(), UID: order.UID, Amount: debitIncome, ActualAmount: float64(debitIncome),
TranType: txnmod.AiUndressDebitIncomeGoldReturn.Key(), TranTypeInt: int64(txnmod.AiUndressDebitIncomeGoldReturn),
Desc: fmt.Sprintf("AI脱衣收益金币退返-%d", debitIncome), RealAmount: walletmod.GetRealAmount(wallet),
})
}
if debitAmount > 0 {
logs = append(logs, txnmod.TransactionLog{
TransNo: primitive.NewObjectID(), UID: order.UID, Amount: debitAmount, ActualAmount: float64(debitAmount),
TranType: txnmod.AiUndressDebitGoldReturn.Key(), TranTypeInt: int64(txnmod.AiUndressDebitGoldReturn),
Desc: fmt.Sprintf("AI脱衣金币退返-%d", debitAmount), RealAmount: walletmod.GetRealAmount(wallet),
})
}
}
if len(logs) == 0 {
return nil
}
return txnmod.InsertManyTransactionLog(tool, logs)
}