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
+396
View File
@@ -0,0 +1,396 @@
package aiService
import (
"91porn-server/common"
"91porn-server/common/httputil"
"91porn-server/common/log"
"91porn-server/common/redis"
"encoding/json"
"errors"
"reflect"
"strings"
"time"
"github.com/fatih/structs"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type AiService struct {
c *Conf
}
type Conf struct {
AppId int
Url string
Redis *redis.Client
}
type Option func(c *Conf)
func NewAiService(opts ...Option) *AiService {
c := &Conf{}
for _, opt := range opts {
opt(c)
}
return &AiService{
c: c,
}
}
func Redis(r *redis.Client) Option {
return func(c *Conf) {
c.Redis = r
}
}
func AppId(appId int) Option {
return func(c *Conf) {
c.AppId = appId
}
}
func Url(url string) Option {
return func(c *Conf) {
c.Url = url
}
}
type msg struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
func (m *AiService) getParams(p interface{}) (params map[string]interface{}) {
req := structs.New(p)
fields := req.Fields()
params = make(map[string]interface{})
for _, field := range fields {
jsonTag := field.Tag("json")
tagList := strings.Split(jsonTag, ",")
key := field.Name()
omitempty := ""
if len(tagList) > 0 {
key = tagList[0]
}
if len(tagList) > 1 {
omitempty = tagList[1]
}
kind := reflect.TypeOf(field.Value()).Kind()
// 如果是0值忽略或者空指针,不需要传
if field.IsZero() && (kind == reflect.Ptr || omitempty == "omitempty") {
continue
}
if kind == reflect.Ptr {
// 通过指针取值
params[key] = reflect.ValueOf(field.Value()).Elem().Interface()
} else {
params[key] = field.Value()
}
}
params["appId"] = m.c.AppId
return
}
func (s *AiService) getTemplateList(p *TemplateListReq) (result TemplateListResp, err error) {
var retMsg msg
path := "/api/ai/template/all"
params := s.getParams(p)
code, err := httputil.DefaultClientGetWithResp(&retMsg, common.BindUrl(s.c.Url, path), nil, params)
if err != nil {
log.Error("AiService GetTemplateList fail", log.Any("params", params), log.E(err))
err = errors.New("AiService GetTemplateList fail")
return
}
if code != 200 {
log.Error("AiService GetTemplateList fail", log.Any("params", params), log.Any("code", code))
err = errors.New("AiService GetTemplateList fail")
return
}
if retMsg.Code != 200 {
log.Error("AiService GetTemplateList fail", log.Any("params", params), log.Any("retMsg", retMsg))
err = errors.New("AiService GetTemplateList fail")
return
}
b, err := json.Marshal(retMsg.Data)
if err != nil {
log.Error("AiService GetTemplateList json.Marshal fail", log.Any("params", params), log.E(err))
err = errors.New("AiService GetTemplateList json.Marshal fail")
return
}
if err = json.Unmarshal(b, &result); err != nil {
log.Error("AiService GetTemplateList json.Unmarshal fail", log.Any("params", params), log.Any("data", string(b)), log.E(err))
err = errors.New("AiService GetTemplateList json.Unmarshal fail")
return
}
return
}
type TemplateDetailReq struct {
Ids []primitive.ObjectID `json:"ids" binding:"required"`
}
func (s *AiService) getTemplateDetail(p *TemplateDetailReq) (result TemplateDetailResp, err error) {
var retMsg msg
path := "/api/ai/template/detail"
params := s.getParams(p)
code, err := httputil.DefaultClientPostJsonWithResp(&retMsg, common.BindUrl(s.c.Url, path), nil, params)
if err != nil {
log.Error("AiService GetTemplateDetail fail", log.Any("params", params), log.E(err))
err = errors.New("AiService GetTemplateDetail fail")
return
}
if code != 200 {
log.Error("AiService GetTemplateDetail fail", log.Any("params", params), log.Any("code", code))
err = errors.New("AiService GetTemplateDetail fail")
return
}
if retMsg.Code != 200 {
log.Error("AiService GetTemplateDetail fail", log.Any("params", params), log.Any("retMsg", retMsg))
err = errors.New("AiService GetTemplateDetail fail")
return
}
b, err := json.Marshal(retMsg.Data)
if err != nil {
log.Error("AiService GetTemplateDetail json.Marshal fail", log.Any("params", params), log.E(err))
err = errors.New("AiService GetTemplateDetail json.Marshal fail")
return
}
if err = json.Unmarshal(b, &result); err != nil {
log.Error("AiService GetTemplateDetail json.Unmarshal fail", log.Any("params", params), log.Any("data", string(b)), log.E(err))
err = errors.New("AiService GetTemplateDetail json.Unmarshal fail")
return
}
return
}
func (s *AiService) Sync() (err error) {
if s.c.Redis == nil {
return errors.New("redis is nil")
}
resp, err := s.getTemplateList(&TemplateListReq{})
if err != nil {
log.Error("AI Template sync fail", log.E(err))
return
}
for _, template := range resp.TemplateList {
b, err := json.Marshal(template)
if err != nil {
continue
}
// 将模版数据写入redis
s.c.Redis.Set(AiTemplateKey(template.ID), string(b), time.Hour*24*7)
}
var imageCategoryList []Category
var videoCategoryList []Category
for _, v := range resp.CategoryList {
if v.Type == 0 {
imageCategoryList = append(imageCategoryList, v)
} else {
videoCategoryList = append(videoCategoryList, v)
}
}
// 将数据写入写入redis
b0, err := json.Marshal(imageCategoryList)
if err != nil {
log.Error("AI Template json.Marshal(imageCategoryList) fail", log.E(err))
return
}
b1, err := json.Marshal(videoCategoryList)
if err != nil {
log.Error("AI Template json.Marshal(videoCategoryList) fail", log.E(err))
return
}
err = s.c.Redis.Set(AiCategoryKey(0), string(b0), time.Hour*24*7)
if err != nil {
log.Error("AI Template Redis.Set(AiCategoryKey(0) fail", log.E(err))
return
}
err = s.c.Redis.Set(AiCategoryKey(1), string(b1), time.Hour*24*7)
if err != nil {
log.Error("AI Template Redis.Set(AiCategoryKey(1) fail", log.E(err))
return
}
return nil
}
func (s *AiService) GetTemplateList(types int, categoryId string) (categoryList []*Category, queryCategoryId string, templateList []*Template, err error) {
if s.c.Redis == nil {
err = errors.New("redis nil")
return
}
// 获取分类列表
val, err := s.c.Redis.Get(AiCategoryKey(types))
if err != nil {
return
}
if val == nil {
return
}
err = json.Unmarshal([]byte(*val), &categoryList)
if err != nil {
return
}
if len(categoryList) == 0 {
return
}
if categoryId == "" {
queryCategoryId = categoryList[0].ID.Hex()
} else {
queryCategoryId = categoryId
}
templateIds := []primitive.ObjectID{}
for _, v := range categoryList {
if v.ID.Hex() == queryCategoryId {
templateIds = v.TemplateIds
break
}
}
keys := []string{}
for _, templateId := range templateIds {
keys = append(keys, AiTemplateKey(templateId))
}
if len(keys) == 0 {
return
}
// 通过id查找这个模版
res, err := s.c.Redis.MGet(keys...)
if err != nil {
return
}
for _, v := range res {
templateVal, ok := v.(string)
if !ok || templateVal == "" {
continue
}
item := Template{}
err = json.Unmarshal([]byte(templateVal), &item)
if err != nil {
continue
}
if item.ModuleType != types {
continue
}
templateList = append(templateList, &item)
}
return categoryList, queryCategoryId, templateList, nil
}
func (s *AiService) GetTemplate(id primitive.ObjectID) (template Template, err error) {
if id.IsZero() {
return
}
if s.c.Redis == nil {
err = errors.New("redis nil")
return
}
// 从缓存中获取数据
val, err := s.c.Redis.Get(AiTemplateKey(id))
if err != nil {
return
}
if val != nil && *val != "" {
err = json.Unmarshal([]byte(*val), &template)
if err == nil {
return template, nil
}
}
// 如果获取不到,尝试从ai服务获取
res, err := s.getTemplateDetail(&TemplateDetailReq{
Ids: []primitive.ObjectID{id},
})
if err != nil {
return
}
if len(res.List) == 0 {
return
}
template = res.List[0]
common.Go(func() {
b, err := json.Marshal(template)
if err != nil {
return
}
// 将模版数据写入redis
s.c.Redis.Set(AiTemplateKey(template.ID), string(b), time.Hour*48)
})
return template, nil
}
func (s *AiService) GetTemplateByIds(ids []primitive.ObjectID) (templateList []Template, err error) {
if len(ids) == 0 {
return
}
if s.c.Redis == nil {
err = errors.New("redis nil")
return
}
keys := []string{}
for _, id := range ids {
keys = append(keys, AiTemplateKey(id))
}
// 从缓存中获取数据
list, err := s.c.Redis.MGet(keys...)
if err != nil {
return
}
noFoundKey := []int{}
for k, v := range list {
if v == nil {
noFoundKey = append(noFoundKey, k)
continue
}
val, ok := v.(string)
if !ok || val == "" {
noFoundKey = append(noFoundKey, k)
continue
}
item := Template{}
err = json.Unmarshal([]byte(val), &item)
if err != nil {
noFoundKey = append(noFoundKey, k)
continue
}
templateList = append(templateList, item)
}
noFoundIds := []primitive.ObjectID{}
for _, k := range noFoundKey {
noFoundIds = append(noFoundIds, ids[k])
}
if len(noFoundIds) == 0 {
return
}
// 如果获取不到,尝试从ai服务获取
res, err := s.getTemplateDetail(&TemplateDetailReq{
Ids: noFoundIds,
})
if err != nil {
return
}
if len(res.List) == 0 {
return
}
templateList = append(templateList, res.List...)
common.Go(func() {
// 将查到的写入redis
for _, template := range res.List {
b, err := json.Marshal(template)
if err != nil {
return
}
// 将模版数据写入redis
s.c.Redis.Set(AiTemplateKey(template.ID), string(b), time.Hour*48)
}
})
return templateList, nil
}
+20
View File
@@ -0,0 +1,20 @@
package aiService
import (
"fmt"
"go.mongodb.org/mongo-driver/bson/primitive"
)
const (
aiCategoryKey = "aiCategory:%v"
aiTemplateKey = "aiTemplate:%v"
aiCategoryTemplateKey = "aiCategoryTemplate:%v"
)
func AiTemplateKey(id primitive.ObjectID) string {
return fmt.Sprintf(aiTemplateKey, id.Hex())
}
func AiCategoryKey(types int) string {
return fmt.Sprintf(aiCategoryKey, types)
}
+46
View File
@@ -0,0 +1,46 @@
package aiService
import (
"go.mongodb.org/mongo-driver/bson/primitive"
"time"
)
// 获取模版列表
type TemplateListReq struct {
}
type TemplateListResp struct {
CategoryList []Category `json:"categoryList"` // 分类列表
TemplateList []*Template `json:"templateList"` // 模版列表
}
type TemplateDetailResp struct {
List []Template `json:"list"`
}
type Template struct {
ID primitive.ObjectID `json:"id" bson:"id"`
Title string `json:"title" bson:"title"`
Cover string `json:"cover" bson:"cover"` //封面
M3u8Url string `json:"m3u8Url" bson:"m3u8_url"` // m3u8 地址
Coin uint `json:"coin" bson:"coin"` // 价格(金币)
ModuleType int `json:"moduleType" bson:"module_type"` //换脸模版类型 0 图片 1 视频
VipCoin uint `json:"vipCoin" bson:"vipCoin"`
CategoryId primitive.ObjectID `json:"categoryId" bson:"categoryId"` // 分类id
CreatedAt time.Time `json:"createdAt,omitempty" bson:"createdAt"` //创建时间
UsedCount uint64 `json:"usedCount" bson:"usedCount"` // 模版使用次数
}
// Category 模版分类表
type Category struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Name string `json:"name" bson:"name"` // 分类名
Type int `json:"type" bson:"type"` // 0 图片 1 视频
SortCode int `json:"sortCode" bson:"sortCode"` // 排序号
AppId int `json:"appId" bson:"appId"` // 绑定appId
Status int `json:"status" bson:"status"` // 0-不可用 1-可用
UpdateTime time.Time `json:"updatedAt" bson:"updatedAt"` // 修改时间
CreateTime time.Time `json:"createdAt" bson:"createdAt"` // 创建时间
TemplateIds []primitive.ObjectID `json:"templateIds"`
}