+385
@@ -0,0 +1,385 @@
|
||||
package ai_text_to_novel_ser
|
||||
|
||||
import (
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/middleware/ua"
|
||||
"91porn-server/models/cache/aitexttonoveldata"
|
||||
"91porn-server/models/cache/sysconfdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aitexttonovelmod"
|
||||
"91porn-server/models/v/sysconfmod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type AppQueryListReq struct {
|
||||
Status int `json:"status" form:"status"` // 状态 1、排队中 2、生成成功 3、生成失败
|
||||
UID uint64 `json:"-"` // 用户ID
|
||||
commod.Page
|
||||
}
|
||||
|
||||
func (p *AppQueryListReq) Filter() primitive.M {
|
||||
filter := bson.M{}
|
||||
switch p.Status {
|
||||
case 1:
|
||||
filter["status"] = bson.M{"$in": []aitexttonovelmod.AiTextToNovelStatus{aitexttonovelmod.StatusOrderSuccess, aitexttonovelmod.StatusSubmitted}}
|
||||
case 2:
|
||||
filter["status"] = aitexttonovelmod.StatusGenerationSuccess
|
||||
case 3:
|
||||
filter["status"] = bson.M{"$in": []aitexttonovelmod.AiTextToNovelStatus{aitexttonovelmod.StatusGenerationFailed, aitexttonovelmod.StatusRefunded}}
|
||||
}
|
||||
filter["uid"] = p.UID
|
||||
filter["isHide"] = false
|
||||
return filter
|
||||
}
|
||||
|
||||
type AppListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []*aitexttonovelmod.AiTextToNovel `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (p *AppQueryListReq) GetList() AppListRes {
|
||||
var res AppListRes
|
||||
var err error
|
||||
|
||||
sort := bson.D{{"createdAt", -1}}
|
||||
// 获取列表
|
||||
var data []aitexttonovelmod.AiTextToNovel
|
||||
data, res.Total, res.HasNext, err = aitexttonovelmod.GetList(p.Filter(), int64(p.Skip()), int64(p.Limit()), sort)
|
||||
if err != nil {
|
||||
log.Error("获取AI小说列表列表数据错误", log.Any("Params", *p), log.E(err))
|
||||
return res
|
||||
}
|
||||
res.List = aitexttonoveldata.FormatAppDataList(data)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
type AppQueryInfoReq struct {
|
||||
ID string `json:"id" form:"id"` // id
|
||||
}
|
||||
type AppQueryInfoRes = *aitexttonovelmod.AiTextToNovel
|
||||
|
||||
// GetInfo 获取详情
|
||||
func (p *AppQueryInfoReq) GetInfo() (res AppQueryInfoRes, err error) {
|
||||
oid, err := primitive.ObjectIDFromHex(p.ID)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
var item aitexttonovelmod.AiTextToNovel
|
||||
item, err = aitexttonovelmod.GetInfo(oid)
|
||||
if err != nil {
|
||||
log.Error("获取AI小说列表详情数据错误", log.Any("ID", p.ID), log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
res = aitexttonoveldata.FormatAppData(item)
|
||||
return
|
||||
}
|
||||
|
||||
type GenerateReq struct {
|
||||
Description string `json:"description"` // 剧情描述/故事情节
|
||||
CharacterSetting string `json:"characterSetting"` // 人物设定
|
||||
LocationScene string `json:"locationScene"` // 地点场景
|
||||
Details string `json:"details"` // 细节说明/其他要求
|
||||
ModelType int `json:"modelType"` // 模型 1:AI小艺 2:AI小萌
|
||||
Coin int64 `json:"-"` // 此次脱衣金币个数
|
||||
UID uint64 `json:"-"` // 用户ID
|
||||
}
|
||||
|
||||
func (p *GenerateReq) GenerateInfo(orderId primitive.ObjectID, orderCreatedAt time.Time, debitCoins, debitIncomeCoins int64) aitexttonovelmod.AiTextToNovel {
|
||||
return aitexttonovelmod.AiTextToNovel{
|
||||
ID: orderId,
|
||||
Coin: debitIncomeCoins + debitCoins,
|
||||
DebitAmountCoin: debitCoins,
|
||||
DebitIncomeCoin: debitIncomeCoins,
|
||||
IsFreeTimes: false,
|
||||
Description: p.Description,
|
||||
CharacterSetting: p.CharacterSetting,
|
||||
LocationScene: p.LocationScene,
|
||||
Details: p.Details,
|
||||
ModelType: p.ModelType,
|
||||
UID: p.UID,
|
||||
Status: int(aitexttonovelmod.StatusOrderSuccess),
|
||||
IsHide: false,
|
||||
UpdatedAt: orderCreatedAt,
|
||||
CreatedAt: orderCreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate 生成订单
|
||||
func (p *GenerateReq) Generate(ua ua.UA, ip string) (stderr.Code, error) {
|
||||
// 获取用户信息
|
||||
user, err := usermod.FindUserByUID(p.UID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError, err
|
||||
}
|
||||
if user.ID.IsZero() {
|
||||
return stderr.UserIsNotExists, errors.New("user is null")
|
||||
}
|
||||
configure, _ := sysconfdata.GetAllFromCache()
|
||||
p.Coin = configure.GetInt(sysconfmod.VCodeAiTextToNovelPrice)
|
||||
|
||||
var debitAmountCoins, debitIncomeCoins int64
|
||||
|
||||
if p.Coin > 0 {
|
||||
// 获取用户钱包
|
||||
w, err := walletmod.GetWallet(p.UID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError, err
|
||||
}
|
||||
if w == nil {
|
||||
return stderr.InsufficientBalance, errors.New("Insufficient balance")
|
||||
}
|
||||
|
||||
// 如果收费的部分余额不够支付
|
||||
if p.Coin > (w.Amount + w.Income) {
|
||||
return stderr.InsufficientBalance, errors.New("Insufficient balance")
|
||||
}
|
||||
|
||||
debitAmountCoins, debitIncomeCoins = TotalDebit(p.Coin, *w)
|
||||
}
|
||||
|
||||
// 获取用户是否是复购
|
||||
isRepurchase, err := txnmod.CheckRepurchaseByTransTypes(p.UID, []txnmod.TransType{
|
||||
txnmod.AiTextToNovelDebitGold,
|
||||
txnmod.AiTextToNovelDebitInComeGold,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("txnmod.CheckRepurchaseByTransTypes fail", log.E(err))
|
||||
return stderr.ErrDbQueryError, err
|
||||
}
|
||||
|
||||
var orderId primitive.ObjectID
|
||||
var orderCreatedAt time.Time
|
||||
if err = appg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
var tl []txnmod.TransactionLog
|
||||
if debitIncomeCoins > 0 || debitAmountCoins > 0 {
|
||||
// 扣除钱包余额
|
||||
wallet, wErr := walletmod.DebitAmountAndIncome(tool, debitAmountCoins, debitIncomeCoins, p.UID)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle imagetovideo Generate walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", p.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if debitAmountCoins > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: p.UID,
|
||||
Amount: -debitAmountCoins,
|
||||
ActualAmount: float64(-debitAmountCoins),
|
||||
TranType: txnmod.AiTextToNovelDebitGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToNovelDebitGold),
|
||||
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: p.UID,
|
||||
Amount: -debitIncomeCoins,
|
||||
ActualAmount: float64(-debitIncomeCoins),
|
||||
TranType: txnmod.AiTextToNovelDebitInComeGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToNovelDebitInComeGold),
|
||||
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 imagetovideo Generate txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", p.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
|
||||
// 新增AI图生视频记录
|
||||
orderId = primitive.NewObjectID()
|
||||
orderCreatedAt = time.Now()
|
||||
order := p.GenerateInfo(orderId, orderCreatedAt, debitAmountCoins, debitIncomeCoins)
|
||||
if _, err = aitexttonovelmod.Insert(tool, order); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle imagetovideo Generate InsertOnes error:%+v:", err), log.Any("uid", p.UID))
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle imagetovideo Generate Trans error:%+v;uid:%v;", err, p.UID))
|
||||
return stderr.ErrDbUpdateError, err
|
||||
}
|
||||
common.Go(func() {
|
||||
_ = autoUploadOrder(orderId)
|
||||
})
|
||||
|
||||
return stderr.Success, nil
|
||||
}
|
||||
|
||||
type AutoUploadReq struct {
|
||||
AppID int `json:"appId"` // 应用ID
|
||||
UID uint64 `json:"uid"` // 用户ID
|
||||
Description string `json:"description"` // 剧情描述/故事情节
|
||||
CharacterSetting string `json:"characterSetting"` // 绘图风格
|
||||
LocationScene string `json:"locationScene"` // 人物设定
|
||||
Details string `json:"details"` // 地点场景
|
||||
ModelType int `json:"modelType"` // AI模型
|
||||
AppOrderNum string `json:"appOrderNum"` // 应用订单号
|
||||
NotifyURL string `json:"notifyUrl"` // 通知 URL
|
||||
}
|
||||
|
||||
func autoUploadOrder(id primitive.ObjectID) error {
|
||||
item, err := aitexttonovelmod.GetInfo(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if item.ID.IsZero() {
|
||||
return errors.New(" ai text to novel order is null")
|
||||
}
|
||||
|
||||
// 将请求参数编码为JSON
|
||||
requestBody := AutoUploadReq{
|
||||
AppID: int(commod.KFK_APPID),
|
||||
UID: item.UID,
|
||||
Description: item.Description,
|
||||
CharacterSetting: item.CharacterSetting,
|
||||
LocationScene: item.LocationScene,
|
||||
Details: item.Details,
|
||||
ModelType: item.ModelType,
|
||||
AppOrderNum: item.ID.Hex(),
|
||||
NotifyURL: fmt.Sprintf("%v/api/web/admin/ai/text_to_novel/callback", appg.Conf.URL.AiImageToVideoCallbackUrl),
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apiURL := getAutoUploadUrl()
|
||||
|
||||
// 创建请求对象
|
||||
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 通过http.Client发送请求
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应内容
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取响应内容失败: %v", err)
|
||||
}
|
||||
|
||||
// 如果返回状态码非200,可以考虑返回错误
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("请求失败,状态码:%d,内容:%s", resp.StatusCode, string(respBytes))
|
||||
}
|
||||
|
||||
// 更新订单
|
||||
updateCond := bson.M{
|
||||
"status": aitexttonovelmod.StatusSubmitted,
|
||||
"updatedAt": time.Now(),
|
||||
}
|
||||
_, err = aitexttonovelmod.UpdateByID(nil, id, updateCond)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAutoUploadUrl() string {
|
||||
return fmt.Sprintf("%v/api/comfyui/prd/create_novel_job", appg.Conf.URL.AiImageToVideoUrl)
|
||||
}
|
||||
|
||||
func TotalDebit(price int64, w walletmod.Wallet) (amountCoins, incomeCoins int64) {
|
||||
var (
|
||||
debitAmountCoins int64
|
||||
debitIncomeCoins int64
|
||||
)
|
||||
|
||||
// 计算当日实际扣减的金额
|
||||
debitCoins := price
|
||||
if w.Amount >= debitCoins {
|
||||
debitAmountCoins = debitCoins
|
||||
} else {
|
||||
debitAmountCoins = w.Amount
|
||||
debitIncomeCoins = debitCoins - w.Amount
|
||||
}
|
||||
return debitAmountCoins, debitIncomeCoins
|
||||
}
|
||||
|
||||
type HideReq struct {
|
||||
ID string `json:"id"` // AI订单ID
|
||||
UID uint64 `json:"-"` // 用户ID
|
||||
}
|
||||
|
||||
// Hide 删除订单
|
||||
func (p *HideReq) Hide() error {
|
||||
oid, err := primitive.ObjectIDFromHex(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
item, err := aitexttonovelmod.GetInfo(oid)
|
||||
if err != nil {
|
||||
log.Error("获取AI绘图列表详情数据错误", log.Any("ID", p.ID), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
if item.ID.IsZero() {
|
||||
return errors.New("AI订单不存在")
|
||||
}
|
||||
|
||||
if item.UID != p.UID {
|
||||
return errors.New("只能删除自己的订单")
|
||||
}
|
||||
|
||||
if err = validateHideStatus(item.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = aitexttonovelmod.Hide(p.UID, oid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateHideStatus(status int) error {
|
||||
if status == int(aitexttonovelmod.StatusOrderSuccess) ||
|
||||
status == int(aitexttonovelmod.StatusSubmitted) {
|
||||
return stderr.AiGenningDelForbidden
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user