@@ -0,0 +1,109 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/activitymod"
|
||||
"91porn-server/web/webg"
|
||||
)
|
||||
|
||||
// 新增活动
|
||||
func Add(in activitymod.AddCond) stderr.Code {
|
||||
result, err := activitymod.Add(in)
|
||||
if err != nil {
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
log.Info(fmt.Sprintf("新增活动[%v]成功", result))
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 修改活动
|
||||
func Modify(in *activitymod.ModifyCond) stderr.Code {
|
||||
var jobStatus bool
|
||||
now := time.Now()
|
||||
// 获取活动信息
|
||||
activityDetail, err := activitymod.Get(in.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
// 活动开启限制
|
||||
if in.Status != nil {
|
||||
// 时间限制
|
||||
if *in.Status && (in.StartTime.After(now) || !in.EndTime.After(now)) {
|
||||
return stderr.ActivityDateError
|
||||
}
|
||||
// 开启/关闭任务
|
||||
if (*in.Status && !activityDetail.Status) || (!*in.Status && activityDetail.Status) {
|
||||
jobStatus = true
|
||||
}
|
||||
}
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if err = activitymod.Modify(t, in.Cond(), in.Bson()); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
if jobStatus {
|
||||
if err = OpenBotDraw(in.ID, *in.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return stderr.ErrDbTransError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 查询列表
|
||||
func QueryAll(in *activitymod.QueryAllCond) (interface{}, stderr.Code) {
|
||||
var data map[string]interface{} = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := activitymod.Count(in.Query())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := activitymod.QueryAll(in.Query(), in.Options())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func QueryDropDownBox() (interface{}, stderr.Code) {
|
||||
list, err := activitymod.QueryDropDownBox()
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
data := make([]map[string]interface{}, len(list))
|
||||
for i, v := range list {
|
||||
data[i] = map[string]interface{}{
|
||||
"label": v.Title,
|
||||
"value": v.ID.Hex(),
|
||||
}
|
||||
}
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func LoadingJob() error {
|
||||
// 获取所有开启中活动
|
||||
activityList, err := activitymod.QueryDropDownBox()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range activityList {
|
||||
if err = OpenBotDraw(v.ID, v.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/activitymod"
|
||||
"91porn-server/models/v/prizemod"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
var job JobCron
|
||||
|
||||
type JobCron struct {
|
||||
cron *cron.Cron
|
||||
cache map[primitive.ObjectID]cron.EntryID
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
job = JobCron{
|
||||
cron: cron.New(cron.WithSeconds()),
|
||||
cache: make(map[primitive.ObjectID]cron.EntryID, 1),
|
||||
}
|
||||
job.cron.Start()
|
||||
}
|
||||
|
||||
type BotDraw struct {
|
||||
ActivityID primitive.ObjectID
|
||||
}
|
||||
|
||||
func (b BotDraw) Run() {
|
||||
var now time.Time = time.Now()
|
||||
// 随机种子
|
||||
randInt := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
// 随机获取n个马甲号
|
||||
names := GetFullName(randInt)
|
||||
// 获取大奖列表
|
||||
prizes, err := prizemod.GetHighPrizeListByActivityID(b.ActivityID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("获取大奖列表失败 [%v]", err))
|
||||
return
|
||||
}
|
||||
// 获取奖池信息
|
||||
prizePool, err := activitymod.GetPrizePool(b.ActivityID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("获取奖池信息失败 [%v]", err))
|
||||
return
|
||||
}
|
||||
// 增加抽奖记录
|
||||
records := make([]activitymod.UserRecord, len(names))
|
||||
for i, v := range names {
|
||||
p := prizes[randInt.Intn(len(prizes))]
|
||||
records[i] = activitymod.UserRecord{
|
||||
ActivityID: b.ActivityID,
|
||||
VIPCardID: p.VIPCardID,
|
||||
UserName: v,
|
||||
Name: p.Name,
|
||||
Type: p.Type,
|
||||
Price: p.Price,
|
||||
Count: p.Count,
|
||||
Value: p.Value,
|
||||
Level: p.Level,
|
||||
DrawPrice: prizePool.Price,
|
||||
CreateTime: now.Add(-time.Duration(randInt.Int63n(3600)) * time.Second),
|
||||
}
|
||||
}
|
||||
// 插入中奖信息
|
||||
if err := activitymod.AddMany(records); err != nil {
|
||||
log.Error(fmt.Sprintf("插入中奖信息失败 [%v]", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetFullName(randInt *rand.Rand) []string {
|
||||
var lastName = []string{
|
||||
"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋",
|
||||
"沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏",
|
||||
"陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭",
|
||||
"郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "任", "袁", "柳", "鲍", "史", "唐", "费", "薛",
|
||||
"雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "安", "常", "傅", "卞", "齐", "元", "顾", "孟",
|
||||
"平", "黄", "穆", "萧", "尹", "姚", "邵", "湛", "汪", "祁", "毛", "狄", "米", "伏", "成", "戴", "谈",
|
||||
"宋", "茅", "庞", "熊", "纪", "舒", "屈", "项", "祝", "董", "梁", "杜", "阮", "蓝", "闵", "季", "贾",
|
||||
"路", "娄", "江", "童", "颜", "郭", "梅", "盛", "林", "钟", "徐", "邱", "骆", "高", "夏", "蔡", "田",
|
||||
"樊", "胡", "凌", "霍", "虞", "万", "支", "柯", "管", "卢", "莫", "柯", "房", "裘", "缪", "解", "应",
|
||||
"宗", "丁", "宣", "邓", "单", "杭", "洪", "包", "诸", "左", "石", "崔", "吉", "龚", "程", "嵇", "邢",
|
||||
"裴", "陆", "荣", "翁", "荀", "于", "惠", "甄", "曲", "封", "储", "仲", "伊", "宁", "仇", "甘", "武",
|
||||
"符", "刘", "景", "詹", "龙", "叶", "幸", "司", "黎", "溥", "印", "怀", "蒲", "邰", "从", "索", "赖",
|
||||
"卓", "屠", "池", "乔", "胥", "闻", "莘", "党", "翟", "谭", "贡", "劳", "逄", "姬", "申", "扶", "堵",
|
||||
"冉", "宰", "雍", "桑", "寿", "通", "燕", "浦", "尚", "农", "温", "别", "庄", "晏", "柴", "瞿", "阎",
|
||||
"连", "习", "容", "向", "古", "易", "廖", "庾", "终", "步", "都", "耿", "满", "弘", "匡", "国", "文",
|
||||
"寇", "广", "禄", "阙", "东", "欧", "利", "师", "巩", "聂", "关", "荆", "司马", "上官", "欧阳", "夏侯",
|
||||
"诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊", "澹台", "公冶", "宗政", "濮阳", "淳于", "单于",
|
||||
"太叔", "申屠", "公孙", "仲孙", "轩辕", "令狐", "徐离", "宇文", "长孙", "慕容", "司徒", "司空"}
|
||||
var firstName = []string{
|
||||
"伟", "刚", "勇", "毅", "俊", "峰", "强", "军", "平", "保", "东", "文", "辉", "力", "明", "永", "健", "世", "广", "志", "义",
|
||||
"兴", "良", "海", "山", "仁", "波", "宁", "贵", "福", "生", "龙", "元", "全", "国", "胜", "学", "祥", "才", "发", "武", "新",
|
||||
"利", "清", "飞", "彬", "富", "顺", "信", "子", "杰", "涛", "昌", "成", "康", "星", "光", "天", "达", "安", "岩", "中", "茂",
|
||||
"进", "林", "有", "坚", "和", "彪", "博", "诚", "先", "敬", "震", "振", "壮", "会", "思", "群", "豪", "心", "邦", "承", "乐",
|
||||
"绍", "功", "松", "善", "厚", "庆", "磊", "民", "友", "裕", "河", "哲", "江", "超", "浩", "亮", "政", "谦", "亨", "奇", "固",
|
||||
"之", "轮", "翰", "朗", "伯", "宏", "言", "若", "鸣", "朋", "斌", "梁", "栋", "维", "启", "克", "伦", "翔", "旭", "鹏", "泽",
|
||||
"晨", "辰", "士", "以", "建", "家", "致", "树", "炎", "德", "行", "时", "泰", "盛", "雄", "琛", "钧", "冠", "策", "腾", "楠",
|
||||
"榕", "风", "航", "弘", "秀", "娟", "英", "华", "慧", "巧", "美", "娜", "静", "淑", "惠", "珠", "翠", "雅", "芝", "玉", "萍",
|
||||
"红", "娥", "玲", "芬", "芳", "燕", "彩", "春", "菊", "兰", "凤", "洁", "梅", "琳", "素", "云", "莲", "真", "环", "雪", "荣",
|
||||
"爱", "妹", "霞", "香", "月", "莺", "媛", "艳", "瑞", "凡", "佳", "嘉", "琼", "勤", "珍", "贞", "莉", "桂", "娣", "叶", "璧",
|
||||
"璐", "娅", "琦", "晶", "妍", "茜", "秋", "珊", "莎", "锦", "黛", "青", "倩", "婷", "姣", "婉", "娴", "瑾", "颖", "露", "瑶",
|
||||
"怡", "婵", "雁", "蓓", "纨", "仪", "荷", "丹", "蓉", "眉", "君", "琴", "蕊", "薇", "菁", "梦", "岚", "苑", "婕", "馨", "瑗",
|
||||
"琰", "韵", "融", "园", "艺", "咏", "卿", "聪", "澜", "纯", "毓", "悦", "昭", "冰", "爽", "琬", "茗", "羽", "希", "欣", "飘",
|
||||
"育", "滢", "馥", "筠", "柔", "竹", "霭", "凝", "晓", "欢", "霄", "枫", "芸", "菲", "寒", "伊", "亚", "宜", "可", "姬", "舒",
|
||||
"影", "荔", "枝", "丽", "阳", "妮", "宝", "贝", "初", "程", "梵", "罡", "恒", "鸿", "桦", "骅", "剑", "娇", "纪", "宽", "苛",
|
||||
"灵", "玛", "媚", "琪", "晴", "容", "睿", "烁", "堂", "唯", "威", "韦", "雯", "苇", "萱", "阅", "彦", "宇", "雨", "洋", "忠",
|
||||
"宗", "曼", "紫", "逸", "贤", "蝶", "菡", "绿", "蓝", "儿", "翠", "烟", "小", "轩"}
|
||||
var lastNameLen = len(lastName)
|
||||
var firstNameLen = len(firstName)
|
||||
total := randInt.Intn(10)
|
||||
names := make([]string, total+1)
|
||||
// 随机产生1-10位
|
||||
for i := 0; i <= total; i++ {
|
||||
var first string
|
||||
for i := 0; i <= rand.Intn(2); i++ { //随机产生2位或者3位的名
|
||||
first = fmt.Sprint(firstName[rand.Intn(firstNameLen-1)])
|
||||
}
|
||||
names[i] = fmt.Sprintf("%s%s", fmt.Sprint(lastName[rand.Intn(lastNameLen-1)]), first)
|
||||
}
|
||||
return names
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/prizemod"
|
||||
"91porn-server/models/v/productmod"
|
||||
)
|
||||
|
||||
// 新增活动
|
||||
func AddPrize(in prizemod.AddPrizeCond) stderr.Code {
|
||||
result, err := prizemod.AddPrize(in)
|
||||
if err != nil {
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
log.Info(fmt.Sprintf("新增活动[%v]成功", result))
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 修改活动
|
||||
func ModifyPrize(in *prizemod.ModifyPrizeCond) stderr.Code {
|
||||
if err := prizemod.ModifyPrize(in.Cond(), in.Bson()); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 查询列表
|
||||
func QueryAllPrize(in *prizemod.QueryAllPrizeCond) (interface{}, stderr.Code) {
|
||||
var data map[string]interface{} = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := prizemod.CountPrize(in.Query())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := prizemod.QueryAllPrize(in.Query(), in.Options())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func QueryVipDropDownBox() (interface{}, stderr.Code) {
|
||||
list, err := productmod.FindByProductType(commod.VIP)
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
data := make([]map[string]interface{}, len(list))
|
||||
for i, v := range list {
|
||||
data[i] = map[string]interface{}{
|
||||
"label": v.Name,
|
||||
"value": v.ID.Hex(),
|
||||
}
|
||||
}
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func QueryPrizeDropDownBox() (interface{}, stderr.Code) {
|
||||
list, err := prizemod.QueryDropDownBox()
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
data := make([]map[string]interface{}, len(list))
|
||||
for i, v := range list {
|
||||
data[i] = map[string]interface{}{
|
||||
"label": v.Name + fmt.Sprintf("[数量:%d]", v.Count),
|
||||
"value": v.ID.Hex(),
|
||||
}
|
||||
}
|
||||
return data, stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/activitymod"
|
||||
)
|
||||
|
||||
// 新增活动奖池
|
||||
func AddPrizePool(in activitymod.AddPrizePoolCond) stderr.Code {
|
||||
result, err := activitymod.AddPrizePool(in)
|
||||
if err != nil {
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
log.Info(fmt.Sprintf("新增活动奖池[%v]成功", result))
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 修改活动奖池
|
||||
func ModifyPrizePool(in *activitymod.ModifyPrizePoolCond) stderr.Code {
|
||||
if err := activitymod.ModifyPrizePool(in.Cond(), in.Bson()); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 查询活动奖池列表
|
||||
func QueryAllPrizePool(in *activitymod.QueryAllPrizePoolCond) (interface{}, stderr.Code) {
|
||||
var data map[string]interface{} = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := activitymod.CountPrizePool(in.Query())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := activitymod.QueryAllPrizePool(in.Query(), in.Options())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/activitymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func QueryAllRecord(in *activitymod.QueryAllRecordCond) (interface{}, stderr.Code) {
|
||||
var data map[string]interface{} = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := activitymod.CountRecord(in.Query())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := activitymod.QueryAllRecord(in.Query(), in.Options())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
// 开启机器人抽奖
|
||||
func OpenBotDraw(activityID primitive.ObjectID, status bool) error {
|
||||
job.mutex.Lock()
|
||||
defer job.mutex.Unlock()
|
||||
if entryID, ok := job.cache[activityID]; ok {
|
||||
if !status {
|
||||
// 移除任务
|
||||
job.cron.Remove(entryID)
|
||||
delete(job.cache, activityID)
|
||||
return nil
|
||||
}
|
||||
return errors.New("任务已开启")
|
||||
}
|
||||
if status {
|
||||
// 加入任务(每小时执行一次)
|
||||
entryID, err := job.cron.AddJob("@hourly", BotDraw{ActivityID: activityID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
job.cache[activityID] = entryID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package actvser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/v/actmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
func GetActities(req GetActitiesRequest) (int64, []actmod.Activity, bool, error) {
|
||||
skip := (req.PageNum - 1) * req.PageSize
|
||||
count, err := actmod.GetActivitiesCount()
|
||||
if err != nil {
|
||||
return 0, nil, false, err
|
||||
}
|
||||
if uint64(count) <= skip {
|
||||
return 0, nil, false, err
|
||||
}
|
||||
limit := req.PageSize + 1
|
||||
actvs, err := actmod.GetActivities(skip, limit)
|
||||
if err != nil {
|
||||
return count, nil, false, err
|
||||
}
|
||||
hasNext := false
|
||||
if uint64(len(actvs)) > req.PageSize {
|
||||
hasNext = true
|
||||
actvs = actvs[:req.PageSize]
|
||||
}
|
||||
return count, actvs, hasNext, nil
|
||||
}
|
||||
|
||||
func AddActivity(req AddActivityRequest) error {
|
||||
now := time.Now()
|
||||
act := actmod.Activity{
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if req.ActivityName != nil {
|
||||
act.ActivityName = *req.ActivityName
|
||||
}
|
||||
if req.Desc != nil {
|
||||
act.Desc = *req.Desc
|
||||
}
|
||||
if req.Img != nil {
|
||||
act.Img = *req.Img
|
||||
}
|
||||
if req.Content != nil {
|
||||
act.Content = *req.Content
|
||||
}
|
||||
if req.Link != nil {
|
||||
act.Link = *req.Link
|
||||
}
|
||||
if req.Sort != nil {
|
||||
act.Sort = *req.Sort
|
||||
}
|
||||
if req.Status != nil {
|
||||
act.Status = *req.Status
|
||||
}
|
||||
if req.ExpiredIn != nil {
|
||||
act.ExpiredIn = *req.ExpiredIn
|
||||
}
|
||||
return actmod.AddACtivity(act)
|
||||
}
|
||||
|
||||
func UpdateActivity(req UpdateActivityRequest) error {
|
||||
b := make(bson.M)
|
||||
if req.ActivityName != nil {
|
||||
b["activityName"] = *req.ActivityName
|
||||
}
|
||||
if req.Desc != nil {
|
||||
b["desc"] = *req.Desc
|
||||
}
|
||||
if req.Img != nil {
|
||||
b["img"] = *req.Img
|
||||
}
|
||||
if req.Content != nil {
|
||||
b["content"] = *req.Content
|
||||
}
|
||||
if req.Link != nil {
|
||||
b["link"] = *req.Link
|
||||
}
|
||||
if req.Sort != nil {
|
||||
b["sort"] = *req.Sort
|
||||
}
|
||||
if req.Status != nil {
|
||||
b["status"] = *req.Status
|
||||
}
|
||||
if req.ExpiredIn != nil {
|
||||
b["expiredIn"] = *req.ExpiredIn
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
now := time.Now()
|
||||
b["createdAt"] = now
|
||||
b["updatedAt"] = now
|
||||
return actmod.EidtActivity(req.ActvID, b)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package actvser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type GetActitiesRequest struct {
|
||||
PageNum uint64 `form:"pageNum" json:"pageNum" binding:"required,min=1"` // 页码
|
||||
PageSize uint64 `form:"pageSize" json:"pageSize" binding:"required,min=1,max=20"` // 每页展示数
|
||||
}
|
||||
|
||||
type UpdateActivityRequest struct {
|
||||
ActvID primitive.ObjectID `json:"activeID"` // 被编辑的活动id
|
||||
ActivityName *string `json:"activityName"` // 活动名
|
||||
Desc *string `json:"desc"` // 活动说明
|
||||
Img *string `json:"img"` // 活动图片
|
||||
Content *string `json:"content"` // 富文本内容
|
||||
Link *string `json:"link"` // 链接地址
|
||||
Sort *int `json:"sort"` // 活动排序
|
||||
Status *int `json:"status"` // 状态 1 有效 0 无效
|
||||
ExpiredIn *time.Time `json:"expiredIn"` // 过期时间
|
||||
}
|
||||
|
||||
type AddActivityRequest struct {
|
||||
ActivityName *string `json:"activityName"` // 活动名
|
||||
Desc *string `json:"desc"` // 活动说明
|
||||
Img *string `json:"img"` // 活动图片
|
||||
Content *string `json:"content"` // 富文本内容
|
||||
Link *string `json:"link"` // 链接地址
|
||||
Sort *int `json:"sort"` // 活动排序
|
||||
Status *int `json:"status"` // 状态 1 有效 0 无效
|
||||
ExpiredIn *time.Time `json:"expiredIn"` // 过期时间
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package addwelfareser
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/l/welfarelgmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/web/service/userser"
|
||||
|
||||
"github.com/jinzhu/now"
|
||||
)
|
||||
|
||||
func AddWelfare(userId string) (stderr.Code, error) {
|
||||
uid, err := strconv.ParseUint(userId, 10, 64)
|
||||
if err != nil {
|
||||
return stderr.Failure, err
|
||||
}
|
||||
user, err := usermod.FindUserByUIDForNoCache(uid)
|
||||
if err != nil || user == nil {
|
||||
return stderr.Failure, err
|
||||
}
|
||||
if err = welfarelgmod.InsertWelFareLog(welfarelgmod.WelfareLog{
|
||||
Uid: uid,
|
||||
FareType: welfarelgmod.Fare_Vip,
|
||||
FareNum: 2,
|
||||
DailyDate: now.BeginningOfDay(),
|
||||
CreatedAt: time.Now(),
|
||||
}); err != nil {
|
||||
if strings.Contains(err.Error(), "E11000 duplicate key error") {
|
||||
return stderr.ErrDbInputExist, err
|
||||
}
|
||||
return stderr.Failure, err
|
||||
}
|
||||
vipExpire, vipLevel := userser.CheckVipRenew(user, 3, 0)
|
||||
if _, err := usermod.Update(user.UID, usermod.UserSelector{VipExpireDate: &vipExpire, VipLevel: &vipLevel}); err != nil {
|
||||
return stderr.Failure, err
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package adminser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/pageopt"
|
||||
"91porn-server/models/v/adminmod"
|
||||
"91porn-server/models/v/authoritymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type M = bson.M
|
||||
|
||||
// Record Record
|
||||
type Record struct {
|
||||
ID primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
|
||||
Name *string `json:"name,omitempty" bson:"name,omitempty"` //账号
|
||||
Password *string `json:"password,omitempty" bson:"password,omitempty"` //密码
|
||||
Nickname *string `json:"nickname,omitempty" bson:"nickname,omitempty"` //昵称
|
||||
Email *string `json:"email,omitempty" bson:"email,omitempty"` //邮箱
|
||||
PrivilegeID *primitive.ObjectID `json:"privilegeID,omitempty" bson:"privilegeID,omitempty"` //权限ID
|
||||
Role *string `json:"role,omitempty" bson:"role,omitempty"` //角色
|
||||
HasLocked *bool `json:"hasLocked,omitempty" bson:"hasLocked,omitempty"` //已禁止登陆
|
||||
LockReason *string `json:"lockReason,omitempty" bson:"lockReason,omitempty"` //禁止登陆的原因
|
||||
CreatedAt *time.Time `json:"createdAt,omitempty" bson:"createdAt,omitempty"`
|
||||
LastLoginAt *time.Time `json:"lastLoginAt,omitempty" bson:"lastLoginAt,omitempty"`
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Total int64 `json:"total" bson:"total"`
|
||||
List []Record `json:"list" bson:"list"`
|
||||
}
|
||||
|
||||
// NameMatch
|
||||
type NameMatch struct {
|
||||
Name *string
|
||||
}
|
||||
|
||||
func (n *NameMatch) New() pageopt.Matcher {
|
||||
return pageopt.NewAssignMatch("name", n.Name)
|
||||
}
|
||||
|
||||
// NotSuperAdminMatch
|
||||
type NotSuperAdminMatch struct {
|
||||
}
|
||||
|
||||
func (n *NotSuperAdminMatch) New() pageopt.Matcher {
|
||||
return pageopt.NewAssignMatch("name", M{"$ne": "superAdmin"})
|
||||
}
|
||||
|
||||
// AdminPages AdminPages
|
||||
func AdminPages(skip, limit int64, name *string) (Page, error) {
|
||||
matList := []pageopt.Matcher{(&NotSuperAdminMatch{}).New()}
|
||||
if name != nil {
|
||||
matList = []pageopt.Matcher{(&NameMatch{name}).New()}
|
||||
}
|
||||
adminList, err := adminmod.List(skip, limit, matList...)
|
||||
if err != nil {
|
||||
return Page{}, err
|
||||
}
|
||||
roles := make([]string, len(adminList))
|
||||
for i, admin := range adminList {
|
||||
roles[i] = admin.Role
|
||||
}
|
||||
roleIDMap, err := authoritymod.IDMapByRoles(roles)
|
||||
if err != nil {
|
||||
return Page{}, err
|
||||
}
|
||||
recordList := make([]Record, len(adminList))
|
||||
for i, admin := range adminList {
|
||||
_admin := admin
|
||||
privilegeID := roleIDMap[_admin.Role]
|
||||
recordList[i] = Record{
|
||||
ID: _admin.ID,
|
||||
Name: &_admin.Name,
|
||||
Password: &_admin.Password,
|
||||
Nickname: &_admin.Nickname,
|
||||
Email: &_admin.Email,
|
||||
PrivilegeID: &privilegeID,
|
||||
Role: &_admin.Role,
|
||||
HasLocked: &_admin.HasLocked,
|
||||
LockReason: &_admin.LockReason,
|
||||
CreatedAt: &_admin.CreatedAt,
|
||||
LastLoginAt: &_admin.LastLoginAt,
|
||||
}
|
||||
}
|
||||
count, err := adminmod.Count(matList...)
|
||||
if err != nil {
|
||||
return Page{}, err
|
||||
}
|
||||
return Page{
|
||||
Total: count,
|
||||
List: recordList,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package adser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common"
|
||||
"91porn-server/models/v/adsmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type AdRecord struct {
|
||||
ID primitive.ObjectID `json:"id"` //广告id
|
||||
AdsType adsmod.AdsType `json:"adsType"` //广告类型
|
||||
Cover string `json:"cover"` //广告封面图
|
||||
Size int `json:"size"` //尺寸 B
|
||||
Height int `json:"height"` //高
|
||||
Width int `json:"width"` //宽
|
||||
Title string `json:"title"` //广告标题
|
||||
HrefType adsmod.URLJumpType `json:"hrefType"` //广告链接类型
|
||||
Href string `json:"href"` //广告跳转地址
|
||||
Position adsmod.AdPosition `json:"position"` //广告位置
|
||||
SortCode int `json:"sortCode"` //排序号
|
||||
Active bool `json:"active"` //激活状态 true:激活
|
||||
Click int `json:"click"` //点击次数
|
||||
Remark string `json:"remark"` //备注
|
||||
ReviewID primitive.ObjectID `json:"reviewID,omitempty"` //审核id
|
||||
DistrictCode string `json:"districtCode"` //商区码
|
||||
CreatedAt time.Time `json:"createdAt"` //创建时间
|
||||
Start time.Time `json:"start"` //开始时间
|
||||
End time.Time `json:"end"` //结束时间
|
||||
}
|
||||
|
||||
type AdPage struct {
|
||||
List []AdRecord `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// AdPages 获取广告列表
|
||||
func AdPages(skip, limit int64, idMatch adsmod.IDMatch, adsType adsmod.AdsTypeMatch, adsPositionMatch adsmod.AdPositionMatch, districtCodeMatch adsmod.DistrictCodeMatch) (AdPage, error) {
|
||||
mats := []adsmod.Matcher{
|
||||
idMatch.New(),
|
||||
adsType.New(),
|
||||
adsPositionMatch.New(),
|
||||
districtCodeMatch.New(),
|
||||
}
|
||||
list, _ := adsmod.List(adsmod.Sort_createdAt_N1, &skip, &limit, mats...)
|
||||
recordList := make([]AdRecord, len(list))
|
||||
for i, v := range list {
|
||||
recordList[i] = AdRecord{
|
||||
ID: v.ID,
|
||||
AdsType: v.AdsType,
|
||||
Cover: v.Cover,
|
||||
Size: v.Size,
|
||||
Height: v.Height,
|
||||
Width: v.Width,
|
||||
Title: v.Title,
|
||||
HrefType: v.HrefType,
|
||||
Href: v.Href,
|
||||
Position: v.Position,
|
||||
SortCode: v.SortCode,
|
||||
Active: v.Active,
|
||||
Click: v.Click,
|
||||
Remark: v.Remark,
|
||||
ReviewID: v.ReviewID,
|
||||
DistrictCode: v.DistrictCode,
|
||||
CreatedAt: v.CreatedAt,
|
||||
Start: v.Start,
|
||||
End: v.End,
|
||||
}
|
||||
}
|
||||
count, _ := adsmod.Count(mats...)
|
||||
return AdPage{
|
||||
recordList,
|
||||
count,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func IsValidAdPicture(pos adsmod.AdPosition, pictureProp common.PictureProp) error {
|
||||
switch pos {
|
||||
case adsmod.StartupPage: //启动页广告
|
||||
if pictureProp.Height > 1560 || pictureProp.Width > 720 {
|
||||
return ErrImgScale{}
|
||||
}
|
||||
case adsmod.HomeRecoSmall: //首页-推荐小广告
|
||||
if pictureProp.Height > 620 || pictureProp.Width > 560 {
|
||||
return ErrImgScale{}
|
||||
}
|
||||
case adsmod.HomePopupAnnou: //首页-弹窗公告
|
||||
if pictureProp.Height > 1300 || pictureProp.Width > 656 {
|
||||
return ErrImgScale{}
|
||||
}
|
||||
case adsmod.NotifyPage: //消息-界面广告
|
||||
if pictureProp.Height > 260 || pictureProp.Width > 720 {
|
||||
return ErrImgScale{}
|
||||
}
|
||||
case adsmod.LiaoCarousel: //撩吧-轮播广告
|
||||
if pictureProp.Height > 500 || pictureProp.Width > 720 {
|
||||
return ErrImgScale{}
|
||||
}
|
||||
case adsmod.WalletPage: //钱包-轮播广告
|
||||
if pictureProp.Height > 120 || pictureProp.Width > 720 {
|
||||
return ErrImgScale{}
|
||||
}
|
||||
case adsmod.SearchPage: //搜索-轮播广告
|
||||
if pictureProp.Height > 360 || pictureProp.Width > 720 {
|
||||
return ErrImgScale{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package adser
|
||||
|
||||
type ErrImgURLInvalid struct {
|
||||
}
|
||||
|
||||
func (ErrImgURLInvalid) Error() string {
|
||||
return "picture url is invalid!"
|
||||
}
|
||||
|
||||
type ErrImgTooBig struct {
|
||||
}
|
||||
|
||||
func (ErrImgTooBig) Error() string {
|
||||
return "picture is too big!"
|
||||
}
|
||||
|
||||
type ErrImgScale struct {
|
||||
}
|
||||
|
||||
func (ErrImgScale) Error() string {
|
||||
return "picture cale is invalid!"
|
||||
}
|
||||
|
||||
type ErrImgContent struct {
|
||||
}
|
||||
|
||||
func (ErrImgContent) Error() string {
|
||||
return "picture is invalid!"
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package adser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/models/v/adreviewmod"
|
||||
"91porn-server/models/v/adsmod"
|
||||
"91porn-server/web/webg"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ReviewRecord struct {
|
||||
ID primitive.ObjectID `json:"id"` //id
|
||||
DistrictCode string `json:"districtCode"` //商区码
|
||||
OptType adreviewmod.OptType `json:"optType"` //操作类型
|
||||
ApplyAt time.Time `json:"applyAt"` //申请时间
|
||||
PassAt time.Time `json:"passAt"` //通过时间
|
||||
RefussAt time.Time `json:"refussAt"` //拒绝时间
|
||||
Reviewer string `json:"reviewer"` //审核人
|
||||
ReviewStatus adreviewmod.ReviewStatus `json:"reviewStatus"` //审核状态
|
||||
Remark string `json:"remark"` //备注
|
||||
ReceiptInfo string `json:"receiptInfo"` //回执信息
|
||||
ADID primitive.ObjectID `json:"adID"` //广告id
|
||||
Title string `json:"title"` //广告标题
|
||||
Cover string `json:"cover"` //广告封面图
|
||||
Size int `json:"size"` //尺寸 单位:B
|
||||
Height int `json:"height"`
|
||||
Width int `json:"width"`
|
||||
HrefType adsmod.URLJumpType `json:"hrefType"` //广告链接类型
|
||||
Href string `json:"href"` //广告跳转地址
|
||||
Position adsmod.AdPosition `json:"position"` //广告位置
|
||||
SortCode int `json:"sortCode"` //排序号
|
||||
}
|
||||
|
||||
type ReviewPage struct {
|
||||
List []ReviewRecord `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// ReviewPages 获取广告列表
|
||||
func ReviewPages(skip, limit int64, idMatch adreviewmod.IDMatch, reviewStatusMatch adreviewmod.ReviewStatusMatch, districtCodeMatch adreviewmod.DistrictCodeMatch) (ReviewPage, error) {
|
||||
mats := []adreviewmod.Matcher{
|
||||
idMatch.New(),
|
||||
reviewStatusMatch.New(),
|
||||
districtCodeMatch.New(),
|
||||
}
|
||||
list, err := adreviewmod.List(adreviewmod.Sort_createdAt_N1, &skip, &limit, mats...)
|
||||
if err != nil {
|
||||
return ReviewPage{}, nil
|
||||
}
|
||||
recordList := make([]ReviewRecord, len(list))
|
||||
for i, v := range list {
|
||||
recordList[i] = ReviewRecord{
|
||||
ID: v.ID,
|
||||
DistrictCode: v.DistrictCode,
|
||||
OptType: v.OptType,
|
||||
PassAt: v.PassAt,
|
||||
RefussAt: v.RefussAt,
|
||||
Reviewer: v.Reviewer,
|
||||
ReviewStatus: v.ReviewStatus,
|
||||
ADID: v.ADID,
|
||||
Cover: v.Cover,
|
||||
Size: v.Size,
|
||||
Height: v.Height,
|
||||
Width: v.Width,
|
||||
Title: v.Title,
|
||||
HrefType: v.HrefType,
|
||||
Href: v.Href,
|
||||
Position: adsmod.AdPosition(v.Position),
|
||||
SortCode: v.SortCode,
|
||||
Remark: v.Remark,
|
||||
ReceiptInfo: v.ReceiptInfo,
|
||||
ApplyAt: v.ApplyAt,
|
||||
}
|
||||
}
|
||||
count, err := adreviewmod.Count(mats...)
|
||||
if err != nil {
|
||||
return ReviewPage{}, nil
|
||||
}
|
||||
return ReviewPage{
|
||||
recordList,
|
||||
count,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func CheckRefuse(id primitive.ObjectID, reviewer string, receiptInfo string) error {
|
||||
return adreviewmod.UpdateReviewByID(id, adreviewmod.Processong, adreviewmod.Refuse, nil, reviewer, receiptInfo, time.Now())
|
||||
}
|
||||
|
||||
func CheckPass(id primitive.ObjectID, adsType adsmod.AdsType, reviewer string, receiptInfo string, startTime, endTime time.Time) error {
|
||||
adR, err := adreviewmod.FindOneByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
position := adsmod.AdPosition(adR.Position)
|
||||
reviewInfo := adsmod.ReviewInfo{
|
||||
ReviewID: adR.ID,
|
||||
ReviewAt: &now,
|
||||
Reviewer: &reviewer,
|
||||
}
|
||||
insDoc := adsmod.InsertDoc{
|
||||
AdsType: adsType,
|
||||
Cover: adR.Cover,
|
||||
Title: adR.Title,
|
||||
HrefType: adR.HrefType,
|
||||
Href: adR.Href,
|
||||
Position: position,
|
||||
SortCode: adR.SortCode,
|
||||
Active: true,
|
||||
Remark: receiptInfo,
|
||||
DistrictCode: adR.DistrictCode,
|
||||
ReviewInfo: reviewInfo,
|
||||
CreatedAt: now,
|
||||
CoverProp: adR.CoverProp,
|
||||
Start: startTime,
|
||||
End: endTime,
|
||||
}
|
||||
t := true
|
||||
upDoc := adsmod.UpdateDoc{
|
||||
Title: &adR.Title,
|
||||
Cover: &adR.Cover,
|
||||
HrefType: &adR.HrefType,
|
||||
Href: &adR.Href,
|
||||
Position: &position,
|
||||
SortCode: &adR.SortCode,
|
||||
Active: &t,
|
||||
Remark: &receiptInfo,
|
||||
ReviewInfo: reviewInfo,
|
||||
UpdatedAt: now,
|
||||
CoverProp: adR.CoverProp,
|
||||
Start: &startTime,
|
||||
End: &endTime,
|
||||
}
|
||||
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if adR.ADID.IsZero() {
|
||||
adID, err := adsmod.InsertOne(t, insDoc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return adreviewmod.UpdateReviewByID(id, adreviewmod.Processong, adreviewmod.Pass, &adID, reviewer, receiptInfo, now)
|
||||
}
|
||||
if err := adsmod.UpdateOneByID(t, adR.ADID, upDoc); err != nil {
|
||||
return err
|
||||
}
|
||||
return adreviewmod.UpdateReviewByID(id, adreviewmod.Processong, adreviewmod.Pass, nil, reviewer, receiptInfo, now)
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteAd(adID, arID *primitive.ObjectID) error {
|
||||
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if adID != nil {
|
||||
if err := adsmod.DeleteOne(t, *adID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if arID != nil {
|
||||
if err := adreviewmod.DeleteOne(t, *arID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package advance_config_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/advanceconfigmod"
|
||||
)
|
||||
|
||||
func QueryAll(in *advanceconfigmod.QueryAllCond) (*advanceconfigmod.QueryAllRes, error) {
|
||||
var out = &advanceconfigmod.QueryAllRes{}
|
||||
count, err := advanceconfigmod.CountDocument(in.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*advanceconfigmod.AdvanceConfig{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = advanceconfigmod.QueryAllDocument(in.Filter(), in.Options())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func Create(in *advanceconfigmod.WebCreateReq, manager string) stderr.Code {
|
||||
err := advanceconfigmod.InsertOne(nil, in.Create(manager))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
func Update(in *advanceconfigmod.WebUpdateReq, manager string) stderr.Code {
|
||||
err := advanceconfigmod.Update(nil, in.Filter(), in.Update(manager))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
|
||||
}
|
||||
|
||||
func Del(in *advanceconfigmod.WebDeleteReq, manager string) stderr.Code {
|
||||
err := advanceconfigmod.Update(nil, in.Filter(), in.Del(manager))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package advance_order_ser
|
||||
|
||||
import (
|
||||
"91porn-server/models/v/advanceordermod"
|
||||
)
|
||||
|
||||
func QueryAll(in *advanceordermod.QueryAllCond) (*advanceordermod.QueryAllRes, error) {
|
||||
var out = &advanceordermod.QueryAllRes{}
|
||||
count, err := advanceordermod.CountDocument(in.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*advanceordermod.AdvanceOrder{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = advanceordermod.QueryAllDocument(in.Filter(), in.Options())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package advgroupser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/s/dailyretentionmod"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type AdStatType int
|
||||
|
||||
const (
|
||||
AdStatTypeUserRetained AdStatType = 1 // 用户留存数
|
||||
AdStatTypeUserRates AdStatType = 2 // 用户留存率
|
||||
AdStatTypeAdClickAcc AdStatType = 3 // 广告点击累计
|
||||
AdStatTypePayTotalAcc AdStatType = 4 // 充值总额累计
|
||||
AdStatTypePayAndAdClickTotalAcc AdStatType = 5 // 充值总额+广告点击
|
||||
)
|
||||
|
||||
type QueryReq struct {
|
||||
StartDate *time.Time `json:"startDate" form:"startDate"` // 开始日期
|
||||
EndDate *time.Time `json:"endDate" form:"endDate"` // 结束日期
|
||||
commod.Page
|
||||
}
|
||||
|
||||
type AdGroupStatReq struct {
|
||||
Type AdStatType `json:"type" form:"type"` // 类型 1-用户留存数 2-用户留存率 3-广告点击累计 4-充值总额累计 5-充值总额+广告点击
|
||||
QueryReq
|
||||
}
|
||||
|
||||
type QueryRes struct {
|
||||
Total int64 `json:"total"` // 总数
|
||||
List []Item `json:"list"` // 列表
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"id"` // ID
|
||||
Date time.Time `json:"date" bson:"date"` // 日期
|
||||
GroupA []string `json:"groupA" bson:"groupA"` // A组数据(0-30天)
|
||||
GroupB []string `json:"groupB" bson:"groupB"` // B组数据(0-30天)
|
||||
GroupC []string `json:"groupC" bson:"groupC"` // C组数据(0-30天)
|
||||
}
|
||||
|
||||
func GetAdGroupStatList(ctx context.Context, req *AdGroupStatReq) (*QueryRes, error) {
|
||||
var err error
|
||||
var res *QueryRes
|
||||
switch req.Type {
|
||||
case AdStatTypeUserRetained:
|
||||
res, err = QueryUserRetained(ctx, &req.QueryReq)
|
||||
case AdStatTypeUserRates:
|
||||
res, err = QueryUserRates(ctx, &req.QueryReq)
|
||||
case AdStatTypeAdClickAcc:
|
||||
res, err = QueryTotalClickAcc(ctx, &req.QueryReq)
|
||||
case AdStatTypePayTotalAcc:
|
||||
res, err = QueryPayTotalAcc(ctx, &req.QueryReq)
|
||||
case AdStatTypePayAndAdClickTotalAcc:
|
||||
res, err = QueryTotalPayAndClickAcc(ctx, &req.QueryReq)
|
||||
default:
|
||||
return nil, errors.New("未知的类型")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//res, _ = GetAdGroupStatListMose(ctx, req) // 使用模拟数据替代真实查询
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type dataExtractor func(item *dailyretentionmod.DailyRetention) []string
|
||||
|
||||
// queryRetention 通用查询函数
|
||||
func queryRetention(ctx context.Context, req *QueryReq, extractor dataExtractor) (*QueryRes, error) {
|
||||
startDate := time.Time{}
|
||||
endDate := time.Now()
|
||||
if req.StartDate != nil {
|
||||
startDate = time.Date(req.StartDate.Year(), req.StartDate.Month(), req.StartDate.Day(), 0, 0, 0, 0, req.StartDate.Location())
|
||||
}
|
||||
if req.EndDate != nil {
|
||||
endDate = time.Date(req.EndDate.Year(), req.EndDate.Month(), req.EndDate.Day(), 0, 0, 0, 0, req.EndDate.Location())
|
||||
}
|
||||
|
||||
filter := bson.M{
|
||||
"date": bson.M{
|
||||
"$gte": startDate,
|
||||
"$lte": endDate,
|
||||
},
|
||||
}
|
||||
|
||||
// 查询总数(按日期去重)
|
||||
total, err := countDistinctDates(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("count failed: %w", err)
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
skip := int64((req.PageNumber - 1) * req.PageSize)
|
||||
limit := int64(req.PageSize)
|
||||
|
||||
// 先查询日期范围内的所有记录
|
||||
opts := options.Find().SetSort(bson.D{{Key: "date", Value: -1}})
|
||||
items, err := dailyretentionmod.QueryAllList(filter, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query failed: %w", err)
|
||||
}
|
||||
|
||||
// 按日期分组
|
||||
dateMap := make(map[time.Time]map[commod.AdGroup]*dailyretentionmod.DailyRetention)
|
||||
for _, item := range items {
|
||||
if dateMap[item.Date] == nil {
|
||||
dateMap[item.Date] = make(map[commod.AdGroup]*dailyretentionmod.DailyRetention)
|
||||
}
|
||||
dateMap[item.Date][item.AdGroup] = item
|
||||
}
|
||||
|
||||
// 收集所有日期并排序
|
||||
dates := make([]time.Time, 0, len(dateMap))
|
||||
for date := range dateMap {
|
||||
dates = append(dates, date)
|
||||
}
|
||||
sortDatesDesc(dates)
|
||||
|
||||
// 分页处理
|
||||
start := int(skip)
|
||||
end := start + int(limit)
|
||||
if start > len(dates) {
|
||||
start = len(dates)
|
||||
}
|
||||
if end > len(dates) {
|
||||
end = len(dates)
|
||||
}
|
||||
pagedDates := dates[start:end]
|
||||
|
||||
// 构建结果
|
||||
list := make([]Item, 0, len(pagedDates))
|
||||
for _, date := range pagedDates {
|
||||
groupMap := dateMap[date]
|
||||
item := Item{
|
||||
Date: common.NormalizeDate(date),
|
||||
GroupA: extractGroupData(groupMap[commod.AdGroupA], extractor),
|
||||
GroupB: extractGroupData(groupMap[commod.AdGroupB], extractor),
|
||||
GroupC: extractGroupData(groupMap[commod.AdGroupC], extractor),
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return &QueryRes{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// countDistinctDates 统计日期数量
|
||||
func countDistinctDates(ctx context.Context, filter bson.M) (int64, error) {
|
||||
dates, err := dailyretentionmod.Distinct(filter, "date")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(len(dates)), nil
|
||||
}
|
||||
|
||||
// sortDatesDesc 日期降序排序
|
||||
func sortDatesDesc(dates []time.Time) {
|
||||
for i := 0; i < len(dates)-1; i++ {
|
||||
for j := i + 1; j < len(dates); j++ {
|
||||
if dates[i].Before(dates[j]) {
|
||||
dates[i], dates[j] = dates[j], dates[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extractGroupData 提取单个广告组的数据
|
||||
func extractGroupData(item *dailyretentionmod.DailyRetention, extractor dataExtractor) []string {
|
||||
if item == nil {
|
||||
// 返回 31 个空字符串
|
||||
result := make([]string, dailyretentionmod.MaxRetentionDays+1)
|
||||
for i := range result {
|
||||
result[i] = ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
return extractor(item)
|
||||
}
|
||||
|
||||
// QueryUserRetained 查询用户留存人数
|
||||
func QueryUserRetained(ctx context.Context, req *QueryReq) (*QueryRes, error) {
|
||||
return queryRetention(ctx, req, func(item *dailyretentionmod.DailyRetention) []string {
|
||||
result := make([]string, dailyretentionmod.MaxRetentionDays+1)
|
||||
for i := 0; i <= dailyretentionmod.MaxRetentionDays; i++ {
|
||||
if item.UserRetained != nil {
|
||||
result[i] = fmt.Sprintf("%d", item.UserRetained[i])
|
||||
} else {
|
||||
result[i] = "0"
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// QueryUserRates 查询用户留存率
|
||||
func QueryUserRates(ctx context.Context, req *QueryReq) (*QueryRes, error) {
|
||||
return queryRetention(ctx, req, func(item *dailyretentionmod.DailyRetention) []string {
|
||||
result := make([]string, dailyretentionmod.MaxRetentionDays+1)
|
||||
for i := 0; i <= dailyretentionmod.MaxRetentionDays; i++ {
|
||||
if item.UserRates != nil {
|
||||
result[i] = formatRatePercentage(item.UserRates[i])
|
||||
} else {
|
||||
result[i] = "100%"
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// QueryPayTotalAcc 查询充值累计金额
|
||||
func QueryPayTotalAcc(ctx context.Context, req *QueryReq) (*QueryRes, error) {
|
||||
return queryRetention(ctx, req, func(item *dailyretentionmod.DailyRetention) []string {
|
||||
result := make([]string, dailyretentionmod.MaxRetentionDays+1)
|
||||
for i := 0; i <= dailyretentionmod.MaxRetentionDays; i++ {
|
||||
if item.PayTotalAcc != nil {
|
||||
result[i] = fmt.Sprintf("%d", item.PayTotalAcc[i]/100) // 金额以分为单位,转换为元
|
||||
} else {
|
||||
result[i] = "0"
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// QueryTotalClickAcc 查询点击累计次数
|
||||
func QueryTotalClickAcc(ctx context.Context, req *QueryReq) (*QueryRes, error) {
|
||||
return queryRetention(ctx, req, func(item *dailyretentionmod.DailyRetention) []string {
|
||||
result := make([]string, dailyretentionmod.MaxRetentionDays+1)
|
||||
for i := 0; i <= dailyretentionmod.MaxRetentionDays; i++ {
|
||||
if item.TotalClickAcc != nil {
|
||||
result[i] = fmt.Sprintf("%d", item.TotalClickAcc[i])
|
||||
} else {
|
||||
result[i] = "0"
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// QueryTotalPayAndClickAcc 查询点击累计次数
|
||||
func QueryTotalPayAndClickAcc(ctx context.Context, req *QueryReq) (*QueryRes, error) {
|
||||
return queryRetention(ctx, req, func(item *dailyretentionmod.DailyRetention) []string {
|
||||
result := make([]string, dailyretentionmod.MaxRetentionDays+1)
|
||||
for i := 0; i <= dailyretentionmod.MaxRetentionDays; i++ {
|
||||
if item.TotalClickAcc != nil {
|
||||
result[i] = fmt.Sprintf("%d", item.TotalClickAcc[i]+item.PayTotalAcc[i]/100)
|
||||
} else {
|
||||
result[i] = "0"
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
func formatRatePercentage(rate float64) string {
|
||||
return fmt.Sprintf("%.2f%%", rate*100)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package advgroupser
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFormatRatePercentage(t *testing.T) {
|
||||
t.Run("formats decimal rate as percentage", func(t *testing.T) {
|
||||
got := formatRatePercentage(0.1234)
|
||||
want := "12.340%"
|
||||
if got != want {
|
||||
t.Fatalf("expected %s, got %s", want, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("formats zero as zero percentage", func(t *testing.T) {
|
||||
got := formatRatePercentage(0)
|
||||
want := "0.000%"
|
||||
if got != want {
|
||||
t.Fatalf("expected %s, got %s", want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
package ai_changeface_img_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/httputil"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aichangefaceimgmod"
|
||||
"91porn-server/models/v/aichangefacevidmod"
|
||||
"91porn-server/models/v/aiplazamod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"91porn-server/web/proto"
|
||||
"91porn-server/web/webg"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func List(req *aichangefaceimgmod.WebListRequest) (interface{}, stderr.Code) {
|
||||
var data = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := aichangefaceimgmod.CountDocument(req.Filter())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := aichangefaceimgmod.QueryAllDocument(req.Filter(), req.Options())
|
||||
if err != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func Update(act string, req *aichangefaceimgmod.EditCond) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aichangefaceimgmod.FindDataByID(req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return stderr.CodeEmptyData
|
||||
}
|
||||
if (*req.Status == aichangefaceimgmod.FAILURE || *req.Status == aichangefaceimgmod.REFUND) &&
|
||||
(data.Status == aichangefaceimgmod.FAILURE || data.Status == aichangefaceimgmod.REFUND) {
|
||||
return stderr.Success
|
||||
}
|
||||
// 根据状态判断
|
||||
switch *req.Status {
|
||||
case aichangefaceimgmod.Processing:
|
||||
if data.Status != aichangefaceimgmod.SUCCESS {
|
||||
return stderr.AiChangeFaceImgStatusIsErr
|
||||
}
|
||||
err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.SUCCESS:
|
||||
if err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act)); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.FAILURE, aichangefaceimgmod.REFUND:
|
||||
// 失败处理 退金币
|
||||
deAmt := -data.DebitAmountCoin
|
||||
deIncome := -data.DebitIncomeCoin
|
||||
uid := data.UID
|
||||
if err = webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
updateSet, _ := req.Update(act)["$set"].(bson.M)
|
||||
transitioned, transitionErr := aichangefaceimgmod.TransitionStatus(tool, data.ID, data.Status, *req.Status, updateSet)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
// 根据是否是免费次数退回
|
||||
if data.Count > 0 || data.IsFreeTimes {
|
||||
deCount := data.Count
|
||||
if data.Count == 0 {
|
||||
deCount = 1
|
||||
}
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -deCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: deCount,
|
||||
ActualAmount: float64(deCount),
|
||||
TranType: txnmod.AiChangeFaceImgDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI图片换脸免费次数退返[%d次]", deCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if data.DebitAmountCoin > 0 || data.DebitIncomeCoin > 0 {
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiChangeFaceImgReturnIncomeGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnIncomeGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiChangeFaceImgReturnGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
func UpdateNew(act string, req *aichangefaceimgmod.EditCond) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aichangefaceimgmod.FindDataByID(req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.ID.IsZero() {
|
||||
return stderr.CodeEmptyData
|
||||
}
|
||||
if *req.Status == aichangefaceimgmod.StatusSubmit && data.Status == aichangefaceimgmod.StatusSubmit {
|
||||
return stderr.Success
|
||||
}
|
||||
if (*req.Status == aichangefaceimgmod.FAILURE || *req.Status == aichangefaceimgmod.REFUND) &&
|
||||
(data.Status == aichangefaceimgmod.FAILURE || data.Status == aichangefaceimgmod.REFUND) {
|
||||
return stderr.Success
|
||||
}
|
||||
// 根据状态判断
|
||||
switch *req.Status {
|
||||
case aichangefaceimgmod.Processing:
|
||||
err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.StatusSubmit:
|
||||
//提交订单到第三方
|
||||
notifyURL, notifyErr := webg.AICallbackURL("/api/web/admin/ai/change_face_img/callback")
|
||||
if notifyErr != nil {
|
||||
log.Error("AI图片换脸回调地址配置异常", log.E(notifyErr))
|
||||
return stderr.Failure
|
||||
}
|
||||
httpReq := proto.AiChangeFaceReq{
|
||||
AppId: int(commod.KFK_APPID),
|
||||
OrderNumAPP: data.ID.Hex(),
|
||||
Title: data.ModTitle,
|
||||
SourceImageUrl: data.OriginPic,
|
||||
NotifyUrl: notifyURL,
|
||||
}
|
||||
url := webg.Conf.URL.AiServer + "/api/changeface/prd/changeFaceByVideo"
|
||||
//第三方请求地址
|
||||
var resp proto.AiChangeFaceResp
|
||||
codeJ, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, httpReq)
|
||||
if err != nil {
|
||||
log.Error("CreateChangeFace httputil.DefaultClientPostJsonWithResp err", log.E(err))
|
||||
return stderr.Failure
|
||||
}
|
||||
if codeJ != 200 {
|
||||
log.Error("CreateChangeFace req http code error", log.Any("code", codeJ))
|
||||
return stderr.Failure
|
||||
}
|
||||
if resp.Code == 40000 {
|
||||
return stderr.ChangeFaceNoModule
|
||||
}
|
||||
if err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act)); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.FAILURE, aichangefaceimgmod.REFUND:
|
||||
// 失败处理 退金币
|
||||
deAmt := -data.DebitAmountCoin
|
||||
deIncome := -data.DebitIncomeCoin
|
||||
uid := data.UID
|
||||
if err = webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
updateSet, _ := req.Update(act)["$set"].(bson.M)
|
||||
transitioned, transitionErr := aichangefaceimgmod.TransitionStatus(tool, data.ID, data.Status, *req.Status, updateSet)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
// 根据是否是免费次数退回
|
||||
if data.Count > 0 || data.IsFreeTimes {
|
||||
deCount := data.Count
|
||||
if data.Count == 0 {
|
||||
deCount = 1
|
||||
}
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -deCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: deCount,
|
||||
ActualAmount: float64(deCount),
|
||||
TranType: txnmod.AiChangeFaceImgDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI图片换脸免费次数退返[%d次]", deCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if data.DebitAmountCoin > 0 || data.DebitIncomeCoin > 0 {
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiChangeFaceImgReturnIncomeGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnIncomeGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiChangeFaceImgReturnGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
func CallBackOrder(req *aichangefacevidmod.CallbackReq) stderr.Code {
|
||||
//根据订单号更新ai换脸订单
|
||||
id, _ := primitive.ObjectIDFromHex(req.AppOrderNum)
|
||||
//查询该笔订单是否存在
|
||||
aichangeFaceInfo, err := aichangefaceimgmod.FindByID(nil, id)
|
||||
if aichangeFaceInfo == nil || err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
oldOrderTime := time.Date(2025, 9, 15, 0, 0, 0, 0, time.Local)
|
||||
if aichangeFaceInfo.CreatedAt.Before(oldOrderTime) &&
|
||||
(aichangeFaceInfo.Status == aichangefaceimgmod.SUCCESS || aichangeFaceInfo.Status == aichangefaceimgmod.FAILURE || aichangeFaceInfo.Status == aichangefaceimgmod.REFUND) {
|
||||
// 以前的单子如果处理过了就新下资源和状态
|
||||
set := bson.M{"newPic": []string{strings.TrimSpace(req.ImgUrl)}}
|
||||
if req.Msg == "操作成功" {
|
||||
set["status"] = aichangefaceimgmod.SUCCESS
|
||||
} else {
|
||||
set["remark"] = req.Msg
|
||||
set["status"] = aichangefaceimgmod.REFUND
|
||||
}
|
||||
_ = aichangefaceimgmod.Update(nil, id, set)
|
||||
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
if aichangeFaceInfo.Status != aichangefaceimgmod.StatusSubmit {
|
||||
log.Error("AI图片换脸-重复回调或者状态不正确", log.Any("id", aichangeFaceInfo.ID.Hex()), log.Any("status", aichangeFaceInfo.Status))
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
var status = aichangefaceimgmod.SUCCESS
|
||||
//处理金币退款以及后续操作
|
||||
set := bson.M{}
|
||||
if req.ImgUrl != "" {
|
||||
set["newPic"] = append([]string{}, strings.TrimSpace(req.ImgUrl))
|
||||
}
|
||||
if req.Msg != "操作成功" {
|
||||
set["remark"] = req.Msg
|
||||
status = aichangefaceimgmod.REFUND
|
||||
}
|
||||
set["status"] = status
|
||||
switch status {
|
||||
case aichangefaceimgmod.SUCCESS:
|
||||
transitioned, updateErr := aichangefaceimgmod.TransitionStatus(nil, id, aichangefaceimgmod.StatusSubmit, aichangefaceimgmod.SUCCESS, set)
|
||||
if updateErr != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
if !transitioned {
|
||||
return stderr.Success
|
||||
}
|
||||
common.Go(func() {
|
||||
if aichangeFaceInfo.ShareStatus != 1 {
|
||||
return
|
||||
}
|
||||
// 新增一个分享
|
||||
alPlaza := aiplazamod.AiPlaza{
|
||||
Type: 1,
|
||||
OrderId: aichangeFaceInfo.ID,
|
||||
Template: aichangeFaceInfo.MId,
|
||||
Uid: aichangeFaceInfo.UID,
|
||||
Title: aichangeFaceInfo.ShareTitle,
|
||||
OriginalImage: aichangeFaceInfo.ModPic,
|
||||
OriginalVideo: "",
|
||||
OriginContent: "",
|
||||
GenerateImage: req.ImgUrl,
|
||||
GenerateVideo: "",
|
||||
Status: aiplazamod.DefaultStatus,
|
||||
}
|
||||
_, err = aiplazamod.Insert(nil, alPlaza)
|
||||
if err != nil {
|
||||
log.Error("新增ai广场帖子失败", log.Any("alPlaza", alPlaza), log.E(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
return stderr.Success
|
||||
case aichangefaceimgmod.REFUND:
|
||||
//换脸失败,进行退款操作
|
||||
// 失败处理 退金币
|
||||
deAmt := -aichangeFaceInfo.DebitAmountCoin
|
||||
deIncome := -aichangeFaceInfo.DebitIncomeCoin
|
||||
uid := aichangeFaceInfo.UID
|
||||
if err = webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
transitioned, transitionErr := aichangefaceimgmod.TransitionStatus(tool, id, aichangefaceimgmod.StatusSubmit, aichangefaceimgmod.REFUND, set)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
// 根据是否是免费次数退回
|
||||
if aichangeFaceInfo.Count > 0 || aichangeFaceInfo.IsFreeTimes {
|
||||
deCount := aichangeFaceInfo.Count
|
||||
if aichangeFaceInfo.Count == 0 {
|
||||
deCount = 1
|
||||
}
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -deCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: deCount,
|
||||
ActualAmount: float64(deCount),
|
||||
TranType: txnmod.AiChangeFaceImgDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI图片换脸免费次数退返[%d次]", deCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if aichangeFaceInfo.DebitAmountCoin > 0 || aichangeFaceInfo.DebitIncomeCoin > 0 {
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if aichangeFaceInfo.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiChangeFaceImgReturnIncomeGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnIncomeGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if aichangeFaceInfo.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiChangeFaceImgReturnGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
package ai_changeface_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/httputil"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aichangefacemod"
|
||||
"91porn-server/models/v/aichangefacevidmod"
|
||||
"91porn-server/models/v/aiplazamod"
|
||||
"91porn-server/models/v/backpackmod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"91porn-server/web/proto"
|
||||
"91porn-server/web/webg"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func List(id *primitive.ObjectID, req *aichangefacemod.ListRequest) ([]aichangefacemod.AiChangeFaceWebResp, int64, bool, error) {
|
||||
limit := req.Limit()
|
||||
acfs, count, err := aichangefacemod.WebList(id, req.Uid, req.Status, req.VidID, int(req.Skip()), int(limit+1))
|
||||
if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
hasNext := false
|
||||
if len(acfs) > int(limit) {
|
||||
acfs = acfs[:limit]
|
||||
hasNext = true
|
||||
}
|
||||
acfsLen := len(acfs)
|
||||
acfwrs := make([]aichangefacemod.AiChangeFaceWebResp, acfsLen)
|
||||
for i, v := range acfs {
|
||||
acfwrs[i] = aichangefacemod.AiChangeFaceWebResp{
|
||||
ID: v.ID,
|
||||
Uid: v.Uid,
|
||||
Pic: v.Pic,
|
||||
ModId: v.VidID,
|
||||
ModUrl: v.ModVideo,
|
||||
ModTitle: v.ModTitle,
|
||||
Coin: v.Coin,
|
||||
IncomeCoin: v.IncomeCoin,
|
||||
Status: v.Status,
|
||||
Cover: v.Cover,
|
||||
Url: v.Url,
|
||||
Remark: v.Remark,
|
||||
Operator: v.Operator,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
}
|
||||
return acfwrs, count, hasNext, nil
|
||||
}
|
||||
|
||||
func Update(act string, req *aichangefacemod.EditAiChangeFace) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aichangefacemod.FindByID(nil, req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
// 如果已经退款,直接返回,避免重复扣款
|
||||
if *req.Status == aichangefacemod.StatusRefund && data.Status == aichangefacemod.StatusRefund {
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
switch *req.Status {
|
||||
case aichangefacemod.StatusGenning: // 未完成或完成
|
||||
if err = aichangefacemod.Update(nil, req.ID, req.Update(act)); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
case aichangefacemod.StatusComplete:
|
||||
if req.Url == nil || *req.Url == "" {
|
||||
return stderr.ChangeFaceSuccessWithoutUrl
|
||||
}
|
||||
if err = aichangefacemod.Update(nil, req.ID, req.Update(act)); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
case aichangefacemod.StatusRefund: // 退款
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
var tl []txnmod.TransactionLog
|
||||
updateSet, _ := req.Update(act)["$set"].(bson.M)
|
||||
transitioned, transitionErr := aichangefacemod.TransitionStatus(t, req.ID, data.Status, aichangefacemod.StatusRefund, updateSet)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 折扣券返还
|
||||
if len(data.Discount) > 0 {
|
||||
if _, err = backpackmod.UnuseManyGoods(t, data.Discount); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if data.Coin > 0 || data.IncomeCoin > 0 {
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -data.Coin, -data.IncomeCoin, data.Uid)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", data.Uid))
|
||||
return err
|
||||
}
|
||||
|
||||
if data.Coin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.Uid,
|
||||
Amount: data.Coin,
|
||||
ActualAmount: float64(data.Coin),
|
||||
TranType: txnmod.AiChangefaceDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangefaceDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("AI换脸金币退返-%d", data.Coin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if data.IncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.Uid,
|
||||
Amount: data.IncomeCoin,
|
||||
ActualAmount: float64(data.IncomeCoin),
|
||||
TranType: txnmod.AiChangefaceDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangefaceDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("AI换收益金币退返-%d", data.IncomeCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(t, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeface Generate txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", data.Uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
return stderr.Success
|
||||
default:
|
||||
return stderr.ErrParamError
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateNew(act string, req *aichangefacemod.EditAiChangeFace) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aichangefacemod.FindByID(nil, req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if *req.Status == aichangefacemod.StatusSubmit && data.Status == aichangefacemod.StatusSubmit {
|
||||
return stderr.Success
|
||||
}
|
||||
if *req.Status == aichangefacemod.StatusRefund && data.Status == aichangefacemod.StatusRefund {
|
||||
return stderr.Success
|
||||
}
|
||||
set := bson.M{}
|
||||
set["status"] = req.Status
|
||||
if req.Cover != nil {
|
||||
set["cover"] = strings.TrimSpace(*req.Cover)
|
||||
}
|
||||
if req.Url != nil {
|
||||
set["url"] = strings.TrimSpace(*req.Url)
|
||||
}
|
||||
if req.Remark != nil {
|
||||
set["remark"] = *req.Remark
|
||||
}
|
||||
if len(set) == 0 { // 没有做任何修改
|
||||
return stderr.Success
|
||||
}
|
||||
set["operator"] = act // 记录操作员
|
||||
set["updatedAt"] = time.Now()
|
||||
switch *req.Status {
|
||||
case aichangefacemod.StatusGenning: // 未完成或完成
|
||||
if err = aichangefacemod.Update(nil, req.ID, bson.M{"$set": set}); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefacemod.StatusSubmit:
|
||||
//提交订单到第三方
|
||||
notifyURL, notifyErr := webg.AICallbackURL("/api/web/admin/ai/changeface/callback")
|
||||
if notifyErr != nil {
|
||||
log.Error("AI视频换脸回调地址配置异常", log.E(notifyErr))
|
||||
return stderr.Failure
|
||||
}
|
||||
httpReq := proto.AiChangeFaceReq{
|
||||
AppId: int(commod.KFK_APPID),
|
||||
OrderNumAPP: data.ID.Hex(),
|
||||
Title: data.ModTitle,
|
||||
SourceImageUrl: data.Pic[0],
|
||||
NotifyUrl: notifyURL,
|
||||
}
|
||||
url := webg.Conf.URL.AiServer + "/api/changeface/prd/changeFaceByVideo"
|
||||
//第三方请求地址
|
||||
var resp proto.AiChangeFaceResp
|
||||
codeJ, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, httpReq)
|
||||
if err != nil {
|
||||
log.Error("CreateChangeFace httputil.DefaultClientPostJsonWithResp err", log.E(err))
|
||||
return stderr.Failure
|
||||
}
|
||||
if codeJ != 200 {
|
||||
log.Error("CreateChangeFace req http code error", log.Any("code", codeJ))
|
||||
return stderr.Failure
|
||||
}
|
||||
if resp.Code == 40000 {
|
||||
return stderr.ChangeFaceNoModule
|
||||
}
|
||||
if err = aichangefacemod.Update(nil, req.ID, bson.M{"$set": set}); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefacemod.StatusRefund: // 退款
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
transitioned, transitionErr := aichangefacemod.TransitionStatus(t, req.ID, data.Status, aichangefacemod.StatusRefund, set)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
// 折扣券返还
|
||||
if len(data.Discount) > 0 {
|
||||
if _, err = backpackmod.UnuseManyGoods(t, data.Discount); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 退款
|
||||
if data.Coin == 0 && data.IncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -data.Coin, -data.IncomeCoin, data.Uid)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", data.Uid))
|
||||
return err
|
||||
}
|
||||
// 添加退款记录
|
||||
totalCoin := data.Coin + data.IncomeCoin
|
||||
if err = txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.Uid,
|
||||
Amount: totalCoin,
|
||||
ActualAmount: float64(totalCoin),
|
||||
TranType: txnmod.AiChangefaceDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangefaceDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("AI换脸金币退返-%d", totalCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeface Edit txnmod.InsertTransactionLog error:%+v:", err), log.Any("uid", data.Uid))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
default:
|
||||
return stderr.ErrParamError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
func AddMod(acfv aichangefacevidmod.AddAiChangeFaceVidReq) error {
|
||||
now := time.Now()
|
||||
return aichangefacevidmod.AddAiChangeFaceVid(aichangefacevidmod.AiChangeFaceVidMod{
|
||||
Title: acfv.Title,
|
||||
SourceURL: acfv.SourceURL,
|
||||
Status: acfv.Status,
|
||||
PlayTime: acfv.PlayTime,
|
||||
Cover: acfv.Cover,
|
||||
Type: acfv.Type,
|
||||
NewUrl: acfv.NewUrl,
|
||||
Coin: acfv.Coin,
|
||||
VipCoin: acfv.VipCoin,
|
||||
StyleType: acfv.StyleType,
|
||||
ModuleType: acfv.ModuleType,
|
||||
HotMark: acfv.HotMark,
|
||||
HotValue: acfv.HotValue,
|
||||
Sort: acfv.Sort,
|
||||
SceneType: acfv.SceneType,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateMod(eacfv aichangefacevidmod.EditAiChangeFaceVid) error {
|
||||
set := bson.M{}
|
||||
if eacfv.Title != nil {
|
||||
set["title"] = *eacfv.Title
|
||||
}
|
||||
if eacfv.SourceURL != nil {
|
||||
set["sourceURL"] = *eacfv.SourceURL
|
||||
}
|
||||
if eacfv.Status != nil {
|
||||
set["status"] = *eacfv.Status
|
||||
}
|
||||
if eacfv.NewUrl != nil {
|
||||
set["newUrl"] = *eacfv.NewUrl
|
||||
}
|
||||
if eacfv.PlayTime != nil {
|
||||
set["playTime"] = *eacfv.PlayTime
|
||||
}
|
||||
if eacfv.Cover != nil {
|
||||
set["cover"] = *eacfv.Cover
|
||||
}
|
||||
if eacfv.Type != nil {
|
||||
set["type"] = *eacfv.Type
|
||||
}
|
||||
if eacfv.Coin != nil {
|
||||
set["coin"] = *eacfv.Coin
|
||||
}
|
||||
if eacfv.VipCoin != nil {
|
||||
set["vipCoin"] = *eacfv.VipCoin
|
||||
}
|
||||
if eacfv.ModuleType != nil {
|
||||
set["moduleType"] = *eacfv.ModuleType
|
||||
}
|
||||
if eacfv.HotValue != nil {
|
||||
set["hotValue"] = *eacfv.HotValue
|
||||
}
|
||||
if eacfv.HotMark != nil {
|
||||
set["hotMark"] = *eacfv.HotMark
|
||||
}
|
||||
if eacfv.Sort != nil {
|
||||
set["sort"] = *eacfv.Sort
|
||||
}
|
||||
if eacfv.SceneType != nil {
|
||||
set["sceneType"] = *eacfv.SceneType
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil
|
||||
}
|
||||
set["updatedAt"] = time.Now()
|
||||
return aichangefacevidmod.UpdateAiChangeFaceVid(eacfv.ID, bson.M{"$set": set})
|
||||
}
|
||||
|
||||
func ModList(req *aichangefacevidmod.WebListRequest) (interface{}, stderr.Code) {
|
||||
var (
|
||||
data = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
err error
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, stderr.ErrParamError
|
||||
}
|
||||
count, err := aichangefacevidmod.CountDocument(req.Filter())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := aichangefacevidmod.QueryAllDocument(req.Filter(), req.Options())
|
||||
if err != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func CallBackOrder(req *aichangefacevidmod.CallbackReq) stderr.Code {
|
||||
//根据订单号更新ai换脸订单
|
||||
id, _ := primitive.ObjectIDFromHex(req.AppOrderNum)
|
||||
//查询该笔订单是否存在
|
||||
aichangeFaceInfo, err := aichangefacemod.FindByID(nil, id)
|
||||
if aichangeFaceInfo == nil || err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
oldOrderTime := time.Date(2025, 9, 15, 0, 0, 0, 0, time.Local)
|
||||
if aichangeFaceInfo.CreatedAt.Before(oldOrderTime) &&
|
||||
(aichangeFaceInfo.Status == aichangefacemod.StatusComplete || aichangeFaceInfo.Status == aichangefacemod.StatusRefund) {
|
||||
// 以前的单子如果处理过了就新下资源和状态
|
||||
set := bson.M{"url": strings.TrimSpace(req.ImgUrl)}
|
||||
if req.Msg == "操作成功" {
|
||||
set["status"] = aichangefacemod.StatusComplete
|
||||
} else {
|
||||
set["remark"] = req.Msg
|
||||
set["status"] = aichangefacemod.StatusRefund
|
||||
}
|
||||
_ = aichangefacemod.Update(nil, id, bson.M{"$set": set})
|
||||
|
||||
return stderr.Success
|
||||
}
|
||||
if aichangeFaceInfo.Status != aichangefacemod.StatusSubmit {
|
||||
log.Error("AI视频换脸-重复回调或者状态不正确", log.Any("id", aichangeFaceInfo.ID.Hex()), log.Any("status", aichangeFaceInfo.Status))
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
var status = aichangefacemod.StatusComplete
|
||||
//处理金币退款以及后续操作
|
||||
set := bson.M{}
|
||||
if req.ImgUrl != "" {
|
||||
set["url"] = strings.TrimSpace(req.ImgUrl)
|
||||
}
|
||||
if req.Msg != "操作成功" {
|
||||
set["remark"] = req.Msg
|
||||
status = aichangefacemod.StatusRefund
|
||||
}
|
||||
set["status"] = status
|
||||
set["updatedAt"] = time.Now()
|
||||
switch status {
|
||||
case aichangefacemod.StatusComplete:
|
||||
transitioned, updateErr := aichangefacemod.TransitionStatus(nil, id, aichangefacemod.StatusSubmit, aichangefacemod.StatusComplete, set)
|
||||
if updateErr != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
if !transitioned {
|
||||
return stderr.Success
|
||||
}
|
||||
common.Go(func() {
|
||||
if aichangeFaceInfo.ShareStatus != 1 {
|
||||
return
|
||||
}
|
||||
// 新增一个分享
|
||||
alPlaza := aiplazamod.AiPlaza{
|
||||
Type: 2,
|
||||
OrderId: aichangeFaceInfo.ID,
|
||||
Template: aichangeFaceInfo.VidID.Hex(),
|
||||
Uid: aichangeFaceInfo.Uid,
|
||||
Title: aichangeFaceInfo.ShareTitle,
|
||||
OriginalImage: "",
|
||||
OriginalVideo: aichangeFaceInfo.ModVideo,
|
||||
OriginalVideoCover: aichangeFaceInfo.ModCover,
|
||||
OriginContent: "",
|
||||
GenerateVideo: req.ImgUrl,
|
||||
GenerateVideoCover: aichangeFaceInfo.Pic[0],
|
||||
Status: aiplazamod.DefaultStatus,
|
||||
}
|
||||
if len(aichangeFaceInfo.Pic) > 0 {
|
||||
alPlaza.GenerateVideoCover = aichangeFaceInfo.Pic[0]
|
||||
}
|
||||
_, err = aiplazamod.Insert(nil, alPlaza)
|
||||
if err != nil {
|
||||
log.Error("新增ai广场帖子失败", log.Any("alPlaza", alPlaza), log.E(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
return stderr.Success
|
||||
case aichangefacemod.StatusRefund: // 退款
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
transitioned, transitionErr := aichangefacemod.TransitionStatus(t, id, aichangefacemod.StatusSubmit, aichangefacemod.StatusRefund, set)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
// 折扣券返还
|
||||
if len(aichangeFaceInfo.Discount) > 0 {
|
||||
if _, err = backpackmod.UnuseManyGoods(t, aichangeFaceInfo.Discount); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 退款
|
||||
if aichangeFaceInfo.Coin == 0 && aichangeFaceInfo.IncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -aichangeFaceInfo.Coin, -aichangeFaceInfo.IncomeCoin, aichangeFaceInfo.Uid)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", aichangeFaceInfo.Uid))
|
||||
return err
|
||||
}
|
||||
// 添加退款记录
|
||||
totalCoin := aichangeFaceInfo.Coin + aichangeFaceInfo.IncomeCoin
|
||||
if err = txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: aichangeFaceInfo.Uid,
|
||||
Amount: totalCoin,
|
||||
ActualAmount: float64(totalCoin),
|
||||
TranType: txnmod.AiChangefaceDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangefaceDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("AI换脸金币退返-%d", totalCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeface Edit txnmod.InsertTransactionLog error:%+v:", err), log.Any("uid", aichangeFaceInfo.Uid))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
return stderr.Success
|
||||
default:
|
||||
return stderr.ErrParamError
|
||||
}
|
||||
}
|
||||
|
||||
type BatchUpdateReq struct {
|
||||
IDS []string `json:"ids" bson:"ids"` // AI模版IDS
|
||||
MID string `json:"mid" bson:"mid"` // 模块ID
|
||||
}
|
||||
|
||||
func (q *BatchUpdateReq) Filter() primitive.M {
|
||||
filter := bson.M{}
|
||||
var ids []primitive.ObjectID
|
||||
for _, id := range q.IDS {
|
||||
objectID, _ := primitive.ObjectIDFromHex(id)
|
||||
ids = append(ids, objectID)
|
||||
}
|
||||
|
||||
if len(ids) > 0 {
|
||||
filter["_id"] = bson.M{"$in": ids}
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
func (q *BatchUpdateReq) UpdateCond() primitive.M {
|
||||
cond := bson.M{}
|
||||
cond["mid"] = q.MID
|
||||
cond["updatedAt"] = time.Now()
|
||||
return cond
|
||||
}
|
||||
|
||||
func (q *BatchUpdateReq) BatchUpdateMod() error {
|
||||
return aichangefacevidmod.UpdateManyByCond(q.Filter(), bson.M{"$set": q.UpdateCond()})
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ai_changeface_ser
|
||||
|
||||
import "91porn-server/models/v/aiimagetovideomod"
|
||||
|
||||
type AllSceneListReq struct {
|
||||
}
|
||||
|
||||
type AllSceneResp struct {
|
||||
Name string `json:"name" bson:"name"` // 名称
|
||||
Value int `json:"value" bson:"value"` // 值
|
||||
}
|
||||
|
||||
func (in *AllSceneListReq) All() (data []AllSceneResp, err error) {
|
||||
var ret []AllSceneResp
|
||||
ret = append(ret, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneTagLiftShirt.String(),
|
||||
Value: int(aiimagetovideomod.SceneTagLiftShirt),
|
||||
}, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneTagTearClothes.String(),
|
||||
Value: int(aiimagetovideomod.SceneTagTearClothes),
|
||||
}, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneTagCumOnFace.String(),
|
||||
Value: int(aiimagetovideomod.SceneTagCumOnFace),
|
||||
}, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneOFace.String(),
|
||||
Value: int(aiimagetovideomod.SceneOFace),
|
||||
}, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneTagKiss.String(),
|
||||
Value: int(aiimagetovideomod.SceneTagKiss),
|
||||
}, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneTagSuckBreasts.String(),
|
||||
Value: int(aiimagetovideomod.SceneTagSuckBreasts),
|
||||
}, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneRevealed.String(),
|
||||
Value: int(aiimagetovideomod.SceneRevealed),
|
||||
}, AllSceneResp{
|
||||
Name: aiimagetovideomod.SceneLargerChest.String(),
|
||||
Value: int(aiimagetovideomod.SceneLargerChest),
|
||||
})
|
||||
return ret, nil
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
package ai_image_to_video_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aiimagetovideomod"
|
||||
"91porn-server/models/v/aiplazamod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"91porn-server/web/webg"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
ID *string `json:"id" form:"id"` // ID
|
||||
UID *uint64 `json:"uid" form:"uid"` // 用户ID
|
||||
Status *int `json:"status" form:"status"` // 状态
|
||||
SceneType *int `json:"sceneType" form:"sceneType"` // 模版场景类型
|
||||
commod.Page
|
||||
}
|
||||
|
||||
func (q *WebListReq) Filter() primitive.M {
|
||||
filter := bson.M{}
|
||||
if q.ID != nil {
|
||||
id, _ := primitive.ObjectIDFromHex(*q.ID)
|
||||
filter["_id"] = id
|
||||
}
|
||||
if q.Status != nil {
|
||||
filter["status"] = q.Status
|
||||
}
|
||||
if q.UID != nil {
|
||||
filter["uid"] = q.UID
|
||||
}
|
||||
if q.SceneType != nil {
|
||||
filter["sceneType"] = q.SceneType
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []aiimagetovideomod.AiImageToVideo `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
res.List, res.Total, res.HasNext, err = aiimagetovideomod.GetList(q.Filter(), int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID string `json:"id" binding:"required"` // ID
|
||||
Status *int `json:"status"` // 状态
|
||||
Remark *string `json:"remark"` // 备注
|
||||
}
|
||||
|
||||
func (p *WebUpdateReq) UpdateCond() primitive.M {
|
||||
data := make(map[string]interface{})
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
if p.Remark != nil {
|
||||
data["remark"] = *p.Remark
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
return data
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
id, _ := primitive.ObjectIDFromHex(p.ID)
|
||||
data, err := aiimagetovideomod.GetInfo(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return errors.New("data is null")
|
||||
}
|
||||
|
||||
if p.Status != nil {
|
||||
if data.Status == int(aiimagetovideomod.StatusRefunded) || data.Status == int(aiimagetovideomod.StatusGenerationFailed) {
|
||||
return errors.New("Please do not refund repeatedly.")
|
||||
}
|
||||
|
||||
switch aiimagetovideomod.AiImageToVideoStatus(*p.Status) {
|
||||
case aiimagetovideomod.StatusRefunded:
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if _, err = aiimagetovideomod.UpdateByID(t, id, p.UpdateCond()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 退款
|
||||
if data.DebitAmountCoin == 0 && data.DebitIncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -data.DebitAmountCoin, -data.DebitIncomeCoin, data.UID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", data.UID))
|
||||
return err
|
||||
}
|
||||
|
||||
var tl []txnmod.TransactionLog
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitAmountCoin,
|
||||
ActualAmount: float64(-data.DebitAmountCoin),
|
||||
TranType: txnmod.AiImageToVideoDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiImageToVideoDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI图生视频金币-%d", data.DebitAmountCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitIncomeCoin,
|
||||
ActualAmount: float64(-data.DebitIncomeCoin),
|
||||
TranType: txnmod.AiImageToVideoDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiImageToVideoDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI图生视频收益金币-%d", data.DebitIncomeCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(t, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle imagetovideo Edit txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", data.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
if _, err = aiimagetovideomod.UpdateByID(nil, id, p.UpdateCond()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
if err := aiimagetovideomod.DeleteByID(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CallbackReq 换脸订单回调请求
|
||||
type CallbackReq struct {
|
||||
ImgUrl string `json:"imgUrl"` // AI图生视频图片地址
|
||||
AppOrderNum string `json:"appOrderNum"` // app订单号
|
||||
Status int `json:"status"` // 订单状态 1:成功,2:失败
|
||||
Msg string `json:"msg"` // 消息
|
||||
}
|
||||
|
||||
func (p *CallbackReq) Callback() error {
|
||||
id, _ := primitive.ObjectIDFromHex(p.AppOrderNum)
|
||||
// 查询该笔订单是否存在
|
||||
data, err := aiimagetovideomod.GetInfo(id)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return errors.New("ai imagetovideo data is null")
|
||||
}
|
||||
|
||||
if data.Status != int(aiimagetovideomod.StatusSubmitted) {
|
||||
log.Error("AI图生视频-重复回调或者状态不正确", log.Any("id", data.ID.Hex()), log.Any("status", data.Status))
|
||||
return nil
|
||||
}
|
||||
|
||||
var status = aiimagetovideomod.StatusGenerationSuccess
|
||||
|
||||
// 处理金币退款以及后续操作
|
||||
set := bson.M{}
|
||||
if p.ImgUrl != "" {
|
||||
set["newImgUrl"] = strings.TrimSpace(p.ImgUrl)
|
||||
}
|
||||
if p.Status != 1 {
|
||||
set["remark"] = p.Msg
|
||||
set["newImgUrl"] = ""
|
||||
status = aiimagetovideomod.StatusGenerationFailed
|
||||
}
|
||||
set["status"] = status
|
||||
set["updatedAt"] = time.Now()
|
||||
|
||||
switch status {
|
||||
case aiimagetovideomod.StatusGenerationSuccess:
|
||||
if _, err = aiimagetovideomod.UpdateByID(nil, id, set); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
if data.ShareStatus == 1 {
|
||||
common.Go(func() {
|
||||
// 新增一个分享
|
||||
alPlaza := aiplazamod.AiPlaza{
|
||||
Type: 4,
|
||||
OrderId: data.ID,
|
||||
Template: "",
|
||||
Uid: data.UID,
|
||||
Title: data.ShareTitle,
|
||||
OriginalImage: data.ImgUrl,
|
||||
OriginalVideo: "",
|
||||
OriginContent: "",
|
||||
GenerateImage: p.ImgUrl,
|
||||
GenerateVideo: "",
|
||||
Status: aiplazamod.DefaultStatus,
|
||||
}
|
||||
_, err = aiplazamod.Insert(nil, alPlaza)
|
||||
if err != nil {
|
||||
log.Error("新增ai广场帖子失败", log.Any("alPlaza", alPlaza), log.E(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
case aiimagetovideomod.StatusGenerationFailed: // 退款
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if _, err = aiimagetovideomod.UpdateByID(t, id, set); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 退款
|
||||
if data.DebitAmountCoin == 0 && data.DebitIncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -data.DebitAmountCoin, -data.DebitIncomeCoin, data.UID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle Callback Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", data.UID))
|
||||
return err
|
||||
}
|
||||
|
||||
var tl []txnmod.TransactionLog
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitAmountCoin,
|
||||
ActualAmount: float64(-data.DebitAmountCoin),
|
||||
TranType: txnmod.AiImageToVideoDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiImageToVideoDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI图生视频金币-%d", data.DebitAmountCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitIncomeCoin,
|
||||
ActualAmount: float64(-data.DebitIncomeCoin),
|
||||
TranType: txnmod.AiImageToVideoDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiImageToVideoDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI图生视频收益金币-%d", data.DebitIncomeCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(t, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle imagetovideo Callback txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", data.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package ai_mate_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type AIMateTransactionInfo struct {
|
||||
OrderId string `json:"orderId"` // 订单Id
|
||||
Credit_delta string `json:"credit_delta"` // 此次操作积分数
|
||||
Post_credit string `json:"post_credit"` // 操作后剩余积分数
|
||||
Created_at string `json:"created_at"` // 操作时间
|
||||
Operation_type string `json:"operation_type"` // 操作类型
|
||||
}
|
||||
|
||||
// GetBalance 获取AI伴侣用户交易记录
|
||||
func GetUserTransactionsList(uid uint64, pageNumber, pageSize uint64) (code stderr.Code, data map[string]interface{}) {
|
||||
code = stderr.Success
|
||||
|
||||
filter := bson.M{"tranTypeInt": bson.M{"$in": []txnmod.TransType{txnmod.AiMateChat, txnmod.AdminAiMateSet, txnmod.AiMateCurrencyExchange}}, "uid": uid}
|
||||
|
||||
list, _, total, err := txnmod.GetCoinLogs(pageNumber, pageSize, filter)
|
||||
if err != nil {
|
||||
log.Error("ai_mate_ser GetCoinLogs", log.Any("err", err), log.Any("uid", uid))
|
||||
return
|
||||
}
|
||||
rsl := make([]AIMateTransactionInfo, 0)
|
||||
for _, v := range list {
|
||||
info := AIMateTransactionInfo{
|
||||
OrderId: v.ID.Hex(),
|
||||
Credit_delta: fmt.Sprintf("%v", v.AiMatePoint),
|
||||
Post_credit: fmt.Sprintf("%v", v.RealAiMatePoint),
|
||||
Created_at: v.CreatedAt.String(),
|
||||
Operation_type: v.TranType,
|
||||
}
|
||||
rsl = append(rsl, info)
|
||||
}
|
||||
data = map[string]interface{}{}
|
||||
data["count"] = total
|
||||
data["list"] = rsl
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package ai_mate_v2_ser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/laosiji_app"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/web/webg"
|
||||
)
|
||||
|
||||
// GetUserOrderLogs 查询老司机AI女友V2第三方消费流水。
|
||||
func GetUserOrderLogs(ctx context.Context, uid, pageNumber, pageSize uint64, startTime, endTime time.Time) (code stderr.Code, data map[string]interface{}) {
|
||||
resp, err := laosiji_app.GetAiMateOrderLogs(ctx, webg.Conf.Base.Env, laosiji_app.AiMateOrderLogsReq{
|
||||
AppID: commod.KFK_APPID,
|
||||
UID: uid,
|
||||
Page: int(pageNumber),
|
||||
PageSize: int(pageSize),
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("ai_mate_v2_ser GetUserOrderLogs", log.Any("err", err), log.Any("uid", uid))
|
||||
return stderr.Failure, nil
|
||||
}
|
||||
|
||||
total, _ := strconv.ParseInt(resp.Total, 10, 64)
|
||||
items := resp.Items
|
||||
if items == nil {
|
||||
items = make([]laosiji_app.AiMateOrderLog, 0)
|
||||
}
|
||||
return stderr.Success, map[string]interface{}{
|
||||
"count": total,
|
||||
"list": items,
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
package ai_template_module_ser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aitemplatemodulemod"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
Type *int `json:"type" form:"type"` // 模块类型
|
||||
Status *int `json:"status" form:"status"` // 状态
|
||||
commod.Page
|
||||
}
|
||||
|
||||
func (q *WebListReq) Filter() primitive.M {
|
||||
filter := bson.M{}
|
||||
if q.Status != nil {
|
||||
filter["status"] = q.Status
|
||||
}
|
||||
if q.Type != nil {
|
||||
filter["type"] = q.Type
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
List []aitemplatemodulemod.AiTemplateModule `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
sort := bson.D{{"sortNum", -1}}
|
||||
res.List, res.Total, _, err = aitemplatemodulemod.GetList(q.Filter(), int64(q.Skip()), int64(q.Limit()), sort)
|
||||
return
|
||||
}
|
||||
|
||||
type WebAllListReq struct {
|
||||
}
|
||||
|
||||
func (q *WebAllListReq) Filter() primitive.M {
|
||||
filter := bson.M{}
|
||||
|
||||
filter["status"] = 1
|
||||
filter["isDelete"] = false
|
||||
return filter
|
||||
}
|
||||
|
||||
type WebAllListRes struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` // 文档id
|
||||
Type int `json:"type" bson:"type"` // 模块类型 1:AI视频换脸,2:AI图片换脸
|
||||
Name string `json:"name" bson:"name"` // 模块名称
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebAllListReq) GetList() (res []WebAllListRes, err error) {
|
||||
var ret []WebAllListRes
|
||||
sort := bson.D{{"sortNum", -1}}
|
||||
data, err := aitemplatemodulemod.GetAll(q.Filter(), sort)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if data != nil && len(data) > 0 {
|
||||
for _, d := range data {
|
||||
ret = append(ret, WebAllListRes{
|
||||
ID: d.ID,
|
||||
Name: d.Name,
|
||||
Type: d.Type,
|
||||
})
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
Type int `json:"type" form:"type" binding:"required"` // 模块类型
|
||||
Name string `json:"name" form:"name" binding:"required"` // 模块名称
|
||||
Status int `json:"status" form:"status" binding:"required"` // 状态
|
||||
SortNum int `json:"sortNum" form:"sortNum" binding:"required"` // 模块排序
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create() error {
|
||||
data := aitemplatemodulemod.AiTemplateModule{
|
||||
Type: p.Type,
|
||||
Name: p.Name,
|
||||
Status: p.Status,
|
||||
SortNum: p.SortNum,
|
||||
IsDelete: false,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := aitemplatemodulemod.Insert(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
Type *int `json:"type"` // 模块类型
|
||||
Name *string `json:"name"` // 模块名称
|
||||
Status *int `json:"status"` // 状态
|
||||
SortNum *int `json:"sortNum"` // 模块排序
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
_, err := aitemplatemodulemod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Type != nil {
|
||||
data["type"] = *p.Type
|
||||
}
|
||||
if p.Name != nil {
|
||||
data["name"] = *p.Name
|
||||
}
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
if p.SortNum != nil {
|
||||
data["sortNum"] = *p.SortNum
|
||||
}
|
||||
|
||||
data["updatedAt"] = time.Now()
|
||||
|
||||
if _, err = aitemplatemodulemod.UpdateByID(nil, p.ID, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
if err := aitemplatemodulemod.DeleteByID(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
package ai_text_to_image_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/aiimagetovideomod"
|
||||
"91porn-server/models/v/aiplazamod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"91porn-server/web/webg"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/aitexttoimagedata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aitexttoimagemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
ID *string `json:"id" form:"id"` // ID
|
||||
UID *uint64 `json:"uid" form:"uid"` // 用户ID
|
||||
Status *int `json:"status" form:"status"` // 状态
|
||||
commod.Page
|
||||
}
|
||||
|
||||
func (q *WebListReq) Filter() primitive.M {
|
||||
filter := bson.M{}
|
||||
if q.ID != nil {
|
||||
id, _ := primitive.ObjectIDFromHex(*q.ID)
|
||||
filter["_id"] = id
|
||||
}
|
||||
if q.Status != nil {
|
||||
filter["status"] = q.Status
|
||||
}
|
||||
if q.UID != nil {
|
||||
filter["uid"] = q.UID
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []aitexttoimagemod.AiTextToImage `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
res.List, res.Total, res.HasNext, err = aitexttoimagemod.GetList(q.Filter(), int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID string `json:"id" binding:"required"` // ID
|
||||
Status *int `json:"status"` // 状态
|
||||
Remark *string `json:"remark"` // 备注
|
||||
}
|
||||
|
||||
func (p *WebUpdateReq) UpdateCond() primitive.M {
|
||||
data := make(map[string]interface{})
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
if p.Remark != nil {
|
||||
data["remark"] = *p.Remark
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
return data
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
id, _ := primitive.ObjectIDFromHex(p.ID)
|
||||
data, err := aitexttoimagemod.GetInfo(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return errors.New("data is null")
|
||||
}
|
||||
|
||||
if p.Status != nil {
|
||||
if data.Status == int(aitexttoimagemod.StatusRefunded) || data.Status == int(aiimagetovideomod.StatusGenerationFailed) {
|
||||
return errors.New("Please do not refund repeatedly.")
|
||||
}
|
||||
|
||||
switch aitexttoimagemod.AiTextToImageStatus(*p.Status) {
|
||||
case aitexttoimagemod.StatusRefunded:
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if _, err = aitexttoimagemod.UpdateByID(t, id, p.UpdateCond()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 退款
|
||||
if data.DebitAmountCoin == 0 && data.DebitIncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -data.DebitAmountCoin, -data.DebitIncomeCoin, data.UID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", data.UID))
|
||||
return err
|
||||
}
|
||||
|
||||
var tl []txnmod.TransactionLog
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitAmountCoin,
|
||||
ActualAmount: float64(-data.DebitAmountCoin),
|
||||
TranType: txnmod.AiTextToImageDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToImageDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI绘图金币-%d", data.DebitAmountCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitIncomeCoin,
|
||||
ActualAmount: float64(-data.DebitIncomeCoin),
|
||||
TranType: txnmod.AiTextToImageDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToImageDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI绘图收益金币-%d", data.DebitIncomeCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(t, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle imagetovideo Edit txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", data.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
if _, err = aitexttoimagemod.UpdateByID(nil, id, p.UpdateCond()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
data, err := aitexttoimagemod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = validateDeleteStatus(data.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := aitexttoimagedata.DeleteData(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDeleteStatus(status int) error {
|
||||
if status == int(aitexttoimagemod.StatusOrderSuccess) ||
|
||||
status == int(aitexttoimagemod.StatusSubmitted) {
|
||||
return stderr.AiGenningDelForbidden
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CallbackReq 换脸订单回调请求
|
||||
type CallbackReq struct {
|
||||
ImgUrl string `json:"imgUrl"` // AI图生视频图片地址
|
||||
AppOrderNum string `json:"appOrderNum"` // app订单号
|
||||
Status int `json:"status"` // 订单状态 1:成功,2:失败
|
||||
Msg string `json:"msg"` // 消息
|
||||
}
|
||||
|
||||
func (p *CallbackReq) Callback() error {
|
||||
id, _ := primitive.ObjectIDFromHex(p.AppOrderNum)
|
||||
// 查询该笔订单是否存在
|
||||
data, err := aitexttoimagemod.GetInfo(id)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return errors.New("ai imagetovideo data is null")
|
||||
}
|
||||
|
||||
if data.Status != int(aitexttoimagemod.StatusSubmitted) {
|
||||
log.Error("AI文生图-重复回调或者状态不正确", log.Any("id", data.ID.Hex()), log.Any("status", data.Status))
|
||||
return nil
|
||||
}
|
||||
|
||||
var status = aitexttoimagemod.StatusGenerationSuccess
|
||||
|
||||
// 处理金币退款以及后续操作
|
||||
set := bson.M{}
|
||||
if p.ImgUrl != "" {
|
||||
set["newImgUrl"] = strings.TrimSpace(p.ImgUrl)
|
||||
}
|
||||
if p.Status != 1 {
|
||||
set["remark"] = p.Msg
|
||||
set["newImgUrl"] = ""
|
||||
status = aitexttoimagemod.StatusGenerationFailed
|
||||
}
|
||||
set["status"] = status
|
||||
set["updatedAt"] = time.Now()
|
||||
|
||||
switch status {
|
||||
case aitexttoimagemod.StatusGenerationSuccess:
|
||||
if _, err = aitexttoimagemod.UpdateByID(nil, id, set); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
common.Go(func() {
|
||||
if data.ShareStatus != 1 {
|
||||
return
|
||||
}
|
||||
// 新增一个分享
|
||||
alPlaza := aiplazamod.AiPlaza{
|
||||
Type: 5,
|
||||
OrderId: data.ID,
|
||||
Template: "",
|
||||
Uid: data.UID,
|
||||
Title: data.ShareTitle,
|
||||
OriginalImage: "",
|
||||
OriginalVideo: "",
|
||||
OriginContent: data.Text,
|
||||
GenerateImage: p.ImgUrl,
|
||||
GenerateVideo: "",
|
||||
Status: aiplazamod.DefaultStatus,
|
||||
}
|
||||
_, err = aiplazamod.Insert(nil, alPlaza)
|
||||
if err != nil {
|
||||
log.Error("新增ai广场帖子失败", log.Any("alPlaza", alPlaza), log.E(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
return nil
|
||||
case aitexttoimagemod.StatusGenerationFailed: // 退款
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if _, err = aitexttoimagemod.UpdateByID(t, id, set); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 退款
|
||||
if data.DebitAmountCoin == 0 && data.DebitIncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -data.DebitAmountCoin, -data.DebitIncomeCoin, data.UID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle Callback Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", data.UID))
|
||||
return err
|
||||
}
|
||||
|
||||
var tl []txnmod.TransactionLog
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitAmountCoin,
|
||||
ActualAmount: float64(-data.DebitAmountCoin),
|
||||
TranType: txnmod.AiTextToImageDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToImageDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI绘图金币-%d", data.DebitAmountCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: -data.DebitIncomeCoin,
|
||||
ActualAmount: float64(-data.DebitIncomeCoin),
|
||||
TranType: txnmod.AiTextToImageDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToImageDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI绘图收益金币-%d", data.DebitIncomeCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(t, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle ai text to image Callback txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", data.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
package ai_text_to_novel_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/cache/aitexttonoveldata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aitexttonovelmod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"91porn-server/web/webg"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
ID *string `json:"id" form:"id"` // ID
|
||||
UID *uint64 `json:"uid" form:"uid"` // 用户ID
|
||||
Status *int `json:"status" form:"status"` // 状态
|
||||
commod.Page
|
||||
}
|
||||
|
||||
func (q *WebListReq) Filter() primitive.M {
|
||||
filter := bson.M{}
|
||||
if q.ID != nil {
|
||||
id, _ := primitive.ObjectIDFromHex(*q.ID)
|
||||
filter["_id"] = id
|
||||
}
|
||||
if q.Status != nil {
|
||||
filter["status"] = q.Status
|
||||
}
|
||||
if q.UID != nil {
|
||||
filter["uid"] = q.UID
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
func (q *WebListReq) Sort() primitive.D {
|
||||
return bson.D{{Key: "createdAt", Value: -1}}
|
||||
}
|
||||
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []aitexttonovelmod.AiTextToNovel `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
res.List, res.Total, res.HasNext, err = aitexttonovelmod.GetList(q.Filter(), int64(q.Skip()), int64(q.Limit()), q.Sort())
|
||||
return
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
UID *uint64 `json:"uid"` // 用户ID
|
||||
Background *string `json:"background"` // 小说人物设定/故事背景等
|
||||
Scene *string `json:"scene"` // 地点场景
|
||||
Text *string `json:"text"` // 故事情节描述
|
||||
Other *string `json:"other"` // 小说其他要求
|
||||
ModelType *string `json:"modelType"` // AI小说模型 1,2
|
||||
Content *string `json:"content"` // AI生成小说正文
|
||||
Status *int `json:"status"` // 状态
|
||||
Coin *int64 `json:"coin"` // AI小说金币个数
|
||||
DebitAmountCoin *int64 `json:"debitAmountCoin"` // 此次AI小说扣除金币个数
|
||||
DebitIncomeCoin *int64 `json:"debitIncomeCoin"` // 此次AI小说扣除收益金币个数
|
||||
IsFreeTimes *bool `json:"isFreeTimes"` // 是否使用免费次数
|
||||
IsHide *bool `json:"isHide"` // 是否被用户隐藏
|
||||
Remark *string `json:"remark"` // 备注
|
||||
IsDelete *bool `json:"isDelete"` // 是否删除
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
info, err := aitexttonovelmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.ID.IsZero() {
|
||||
return errors.New("data is null")
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.UID != nil {
|
||||
data["uid"] = *p.UID
|
||||
}
|
||||
if p.Background != nil {
|
||||
data["background"] = *p.Background
|
||||
}
|
||||
if p.Scene != nil {
|
||||
data["scene"] = *p.Scene
|
||||
}
|
||||
if p.Text != nil {
|
||||
data["text"] = *p.Text
|
||||
}
|
||||
if p.Other != nil {
|
||||
data["other"] = *p.Other
|
||||
}
|
||||
if p.ModelType != nil {
|
||||
data["modelType"] = *p.ModelType
|
||||
}
|
||||
if p.Content != nil {
|
||||
data["content"] = *p.Content
|
||||
}
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
if p.Coin != nil {
|
||||
data["coin"] = *p.Coin
|
||||
}
|
||||
if p.DebitAmountCoin != nil {
|
||||
data["debitAmountCoin"] = *p.DebitAmountCoin
|
||||
}
|
||||
if p.DebitIncomeCoin != nil {
|
||||
data["debitIncomeCoin"] = *p.DebitIncomeCoin
|
||||
}
|
||||
if p.IsFreeTimes != nil {
|
||||
data["isFreeTimes"] = *p.IsFreeTimes
|
||||
}
|
||||
if p.IsHide != nil {
|
||||
data["isHide"] = *p.IsHide
|
||||
}
|
||||
if p.Remark != nil {
|
||||
data["remark"] = *p.Remark
|
||||
}
|
||||
if p.IsDelete != nil {
|
||||
data["isDelete"] = *p.IsDelete
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
if p.Status != nil && *p.Status == 5 {
|
||||
if info.Status == int(aitexttonovelmod.StatusRefunded) || info.Status == int(aitexttonovelmod.StatusGenerationFailed) {
|
||||
return errors.New("Please do not refund repeatedly.")
|
||||
}
|
||||
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if _, err = aitexttonovelmod.UpdateByID(t, info.ID, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 退款
|
||||
if info.DebitAmountCoin == 0 && info.DebitIncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -info.DebitAmountCoin, -info.DebitIncomeCoin, info.UID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle text to novel Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", info.UID))
|
||||
return err
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
if info.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: info.UID,
|
||||
Amount: info.DebitAmountCoin,
|
||||
ActualAmount: float64(info.DebitAmountCoin),
|
||||
TranType: txnmod.AiTextToNovelDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToNovelDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI小说金币-%d", info.DebitAmountCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if info.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: info.UID,
|
||||
Amount: info.DebitIncomeCoin,
|
||||
ActualAmount: float64(info.DebitIncomeCoin),
|
||||
TranType: txnmod.AiTextToNovelDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToNovelDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI小说收益金币-%d", info.DebitIncomeCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(t, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle ai text to novel txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", info.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
if _, err = aitexttonoveldata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
data, err := aitexttonovelmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = validateDeleteStatus(data.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := aitexttonoveldata.DeleteData(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDeleteStatus(status int) error {
|
||||
if status == int(aitexttonovelmod.StatusOrderSuccess) ||
|
||||
status == int(aitexttonovelmod.StatusSubmitted) {
|
||||
return stderr.AiGenningDelForbidden
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CallbackReq 换脸订单回调请求
|
||||
type CallbackReq struct {
|
||||
Content string `json:"content"` // AI小说内容
|
||||
AppOrderNum string `json:"appOrderNum"` // app订单号
|
||||
Status int `json:"status"` // 订单状态 1:成功,2:失败
|
||||
Msg string `json:"msg"` // 消息
|
||||
}
|
||||
|
||||
func (p *CallbackReq) Callback() error {
|
||||
id, _ := primitive.ObjectIDFromHex(p.AppOrderNum)
|
||||
// 查询该笔订单是否存在
|
||||
data, err := aitexttonovelmod.GetInfo(id)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return errors.New("ai text to novel data is null")
|
||||
}
|
||||
|
||||
if data.Status != int(aitexttonovelmod.StatusSubmitted) {
|
||||
log.Error("AI小说-重复回调或者状态不正确", log.Any("id", data.ID.Hex()), log.Any("status", data.Status))
|
||||
return nil
|
||||
}
|
||||
|
||||
var status = aitexttonovelmod.StatusGenerationSuccess
|
||||
|
||||
// 处理金币退款以及后续操作
|
||||
set := bson.M{}
|
||||
if p.Content != "" {
|
||||
set["content"] = strings.TrimSpace(p.Content)
|
||||
}
|
||||
if p.Status != 1 {
|
||||
set["remark"] = p.Msg
|
||||
set["content"] = ""
|
||||
status = aitexttonovelmod.StatusGenerationFailed
|
||||
}
|
||||
set["status"] = status
|
||||
set["updatedAt"] = time.Now()
|
||||
|
||||
switch status {
|
||||
case aitexttonovelmod.StatusGenerationSuccess:
|
||||
if _, err = aitexttonovelmod.UpdateByID(nil, id, set); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return nil
|
||||
case aitexttonovelmod.StatusGenerationFailed: // 退款
|
||||
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if _, err = aitexttonovelmod.UpdateByID(t, id, set); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 退款
|
||||
if data.DebitAmountCoin == 0 && data.DebitIncomeCoin == 0 { // 总金额为0则不需要退款
|
||||
return nil
|
||||
}
|
||||
|
||||
// 退款进入钱包余额
|
||||
wallet, err := walletmod.ReturnAmountAndIncome(t, -data.DebitAmountCoin, -data.DebitIncomeCoin, data.UID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle Callback Edit walletmod.DebitAmount error:%+v:", err), log.Any("uid", data.UID))
|
||||
return err
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: data.DebitAmountCoin,
|
||||
ActualAmount: float64(data.DebitAmountCoin),
|
||||
TranType: txnmod.AiTextToNovelDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToNovelDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI小说金币-%d", data.DebitAmountCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: data.UID,
|
||||
Amount: data.DebitIncomeCoin,
|
||||
ActualAmount: float64(data.DebitIncomeCoin),
|
||||
TranType: txnmod.AiTextToNovelDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiTextToNovelDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("退还AI小说收益金币-%d", data.DebitIncomeCoin),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(t, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle ai text to image Callback txnmod.InsertTransactionLog error:%+v:", txnErr), log.Any("uid", data.UID))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if e, ok := err.(stderr.Code); ok {
|
||||
return e
|
||||
}
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
package ai_undress_service
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aiplazamod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/httputil"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/aiUnDressmod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/web/webg"
|
||||
)
|
||||
|
||||
func List(req *aiUnDressmod.WebListRequest) (interface{}, stderr.Code) {
|
||||
var data = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := aiUnDressmod.CountDocument(req.Filter())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := aiUnDressmod.QueryAllDocument(req.Filter(), req.Options())
|
||||
if err != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func Update(act string, req *aiUnDressmod.EditCond) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aiUnDressmod.FindDataByID(req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if (*req.Status == aiUnDressmod.FAILURE || *req.Status == aiUnDressmod.REFUND) &&
|
||||
(data.Status == aiUnDressmod.FAILURE || data.Status == aiUnDressmod.REFUND) {
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 根据状态判断
|
||||
switch *req.Status {
|
||||
case aiUnDressmod.Processing:
|
||||
if data.Status != aiUnDressmod.SUCCESS && data.Status != aiUnDressmod.SubmitOrder {
|
||||
return stderr.AiUnDressStatusIsErr
|
||||
}
|
||||
err = aiUnDressmod.Edit(nil, req.Filter(), req.Update(act))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aiUnDressmod.SUCCESS:
|
||||
err = aiUnDressmod.Edit(nil, req.Filter(), req.Update(act))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aiUnDressmod.FAILURE, aiUnDressmod.REFUND:
|
||||
// 失败处理 退金币
|
||||
deAmt := -data.DebitAmountCoin
|
||||
deIncome := -data.DebitIncomeCoin
|
||||
uid := data.UID
|
||||
oldVersion := false
|
||||
err = webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
updateSet, _ := req.Update(act)["$set"].(bson.M)
|
||||
transitioned, transitionErr := aiUnDressmod.TransitionStatus(tool, data.ID, data.Status, *req.Status, updateSet)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
|
||||
// 根据是否是免费次数退回
|
||||
if data.Count > 0 || data.IsFreeTimes {
|
||||
deCount := data.Count
|
||||
if data.Count == 0 {
|
||||
deCount = 1
|
||||
}
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -deCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress Edit walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: deCount,
|
||||
ActualAmount: float64(deCount),
|
||||
TranType: txnmod.AiUndressDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiUndressDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI脱衣免费次数退返[%d次]", deCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if !data.IsFreeTimes && data.DebitAmountCoin == 0 && data.DebitIncomeCoin == 0 {
|
||||
deAmt = -data.Coin
|
||||
oldVersion = true
|
||||
}
|
||||
if data.DebitAmountCoin > 0 || data.DebitIncomeCoin > 0 || oldVersion {
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress Edit walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiUndressDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiUndressDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("AI脱衣收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitAmountCoin > 0 || oldVersion {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiUndressDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiUndressDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("AI脱衣金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress Edit txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress Edit Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// Auto 提交AI自动化服务
|
||||
func Auto(act string, req *aiUnDressmod.AutoCond) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aiUnDressmod.FindDataByID(req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.Status == aiUnDressmod.SubmitOrder {
|
||||
return stderr.Success
|
||||
}
|
||||
//提交订单到第三方
|
||||
notifyURL, notifyErr := webg.AICallbackURL("/api/web/admin/ai/undress/callback")
|
||||
if notifyErr != nil {
|
||||
log.Error("AI脱衣回调地址配置异常", log.E(notifyErr))
|
||||
return stderr.Failure
|
||||
}
|
||||
httpReq := aiUnDressmod.AiUndressOrderReq{
|
||||
AppId: int(commod.KFK_APPID),
|
||||
FileUrl: data.OriginPics,
|
||||
UserId: strconv.FormatUint(data.UID, 10),
|
||||
AppOrderNum: data.ID.Hex(),
|
||||
NotifyUrl: notifyURL,
|
||||
}
|
||||
var resp aiUnDressmod.AiUndressOrderResp
|
||||
url := webg.Conf.URL.AiUndressServer + "/api/undress/prd/create_job"
|
||||
codeJ, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, httpReq)
|
||||
if err != nil {
|
||||
log.Error("CreateUnderss httputil.DefaultClientPostWithResp err", log.E(err))
|
||||
return stderr.Failure
|
||||
}
|
||||
if codeJ != 200 {
|
||||
log.Error("CreateUnderss req http code error", log.Any("code", codeJ))
|
||||
return stderr.Failure
|
||||
}
|
||||
//更新ai脱衣订单状态
|
||||
if err := aiUnDressmod.SubmitStatus(req.ID, aiUnDressmod.SubmitOrder, "已提交"); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// CallBackOrder AI脱衣回调
|
||||
func CallBackOrder(req *aiUnDressmod.CallBackReq) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aiUnDressmod.FindDataByID(req.AppOrderNum)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if data.Status != aiUnDressmod.SubmitOrder {
|
||||
log.Error("AI脱衣-重复回调或者状态不正确", log.Any("id", data.ID.Hex()), log.Any("status", data.Status))
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
var (
|
||||
set = bson.M{"remark": req.Msg}
|
||||
// 返还次数、返还金币、返还收益金币
|
||||
refoundCount, deAmt, deIncome int64
|
||||
// 订单状态
|
||||
status int
|
||||
oldVersion = false
|
||||
successCount = len(req.ImgUrl)
|
||||
uid = data.UID
|
||||
_id = data.ID
|
||||
isFreeTimes = false
|
||||
)
|
||||
if req.ImgUrl != nil && len(req.ImgUrl) > 0 {
|
||||
set["newPic"] = req.ImgUrl
|
||||
// 同步内存中的 data.NewPic,否则下方 goroutine 读取 data.NewPic[0] 会因未更新而越界 panic
|
||||
data.NewPic = req.ImgUrl
|
||||
}
|
||||
if successCount > 0 { // 成功
|
||||
status = aiUnDressmod.SUCCESS
|
||||
refoundCount = 0
|
||||
deAmt = 0
|
||||
deIncome = 0
|
||||
} else { // 失败
|
||||
status = aiUnDressmod.REFUND
|
||||
refoundCount = data.Count
|
||||
// 失败处理 退金币
|
||||
deAmt = -data.DebitAmountCoin
|
||||
deIncome = -data.DebitIncomeCoin
|
||||
isFreeTimes = data.IsFreeTimes
|
||||
}
|
||||
set["status"] = status
|
||||
handled := false
|
||||
if err := webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
transitioned, transitionErr := aiUnDressmod.TransitionStatus(tool, _id, aiUnDressmod.SubmitOrder, status, set)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
handled = true
|
||||
var tl []txnmod.TransactionLog
|
||||
if refoundCount > 0 || isFreeTimes {
|
||||
if refoundCount == 0 {
|
||||
refoundCount = 1
|
||||
}
|
||||
// 加免费次数,负负为正
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -refoundCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress CallBackOrder walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: refoundCount,
|
||||
ActualAmount: float64(refoundCount),
|
||||
TranType: txnmod.AiUndressDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiUndressDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI脱衣免费次数退返[%d次]", refoundCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if !data.IsFreeTimes && data.DebitAmountCoin == 0 && data.DebitIncomeCoin == 0 {
|
||||
deAmt = -data.Coin
|
||||
oldVersion = true
|
||||
}
|
||||
if deAmt < 0 || deIncome < 0 || oldVersion {
|
||||
// 加钱包余额,负负为正
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress CallBackOrder walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiUndressDebitIncomeGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiUndressDebitIncomeGoldReturn),
|
||||
Desc: fmt.Sprintf("AI脱衣收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitAmountCoin > 0 || oldVersion {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiUndressDebitGoldReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiUndressDebitGoldReturn),
|
||||
Desc: fmt.Sprintf("AI脱衣金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress CallBackOrder txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiUnDress CallBackOrder Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
if !handled {
|
||||
return stderr.Success
|
||||
}
|
||||
if successCount > 0 {
|
||||
common.Go(func() {
|
||||
if data.ShareStatus != 1 {
|
||||
return
|
||||
}
|
||||
// 新增一个分享
|
||||
alPlaza := aiplazamod.AiPlaza{
|
||||
Type: 3,
|
||||
OrderId: data.ID,
|
||||
Template: "",
|
||||
Uid: data.UID,
|
||||
Title: data.ShareTitle,
|
||||
OriginalImage: data.OriginPic,
|
||||
OriginalVideo: "",
|
||||
OriginContent: "",
|
||||
GenerateImage: data.NewPic[0],
|
||||
GenerateVideo: "",
|
||||
Status: aiplazamod.DefaultStatus,
|
||||
}
|
||||
_, err = aiplazamod.Insert(nil, alPlaza)
|
||||
if err != nil {
|
||||
log.Error("新增ai广场帖子失败", log.Any("alPlaza", alPlaza), log.E(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return stderr.Success
|
||||
}
|
||||
Executable
+223
@@ -0,0 +1,223 @@
|
||||
package aiplazaser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/aiplazadata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aiplazamod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
OrderId *string `form:"orderId"` // 关联AI订单id
|
||||
Type *int `form:"type"` // 1-ai图片换脸 2-ai视频换脸 3-ai脱衣 4-ai图生视频 5-ai绘画 6-ai小说
|
||||
Status *int `form:"status"` // 0-待审核 1-审核通过 2-拒绝 3-下架
|
||||
Uid *int64 `form:"uid"` // uid
|
||||
Title *string `form:"title"` // 标题
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []aiplazamod.AiPlaza `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
filter := bson.M{}
|
||||
if q.OrderId != nil {
|
||||
oid, _ := primitive.ObjectIDFromHex(strings.TrimSpace(*q.OrderId))
|
||||
filter["orderId"] = oid
|
||||
}
|
||||
if q.Status != nil {
|
||||
//0-待审核 1-审核通过 2-拒绝 3-下架
|
||||
filter["status"] = *q.Status
|
||||
}
|
||||
if q.Title != nil && len(*q.Title) > 0 {
|
||||
filter["title"] = bson.M{"$regex": *q.Title, "$options": "i"}
|
||||
}
|
||||
if q.Type != nil {
|
||||
// 1-ai图片换脸 2-ai视频换脸 3-ai脱衣 4-ai图生视频 5-ai绘画
|
||||
filter["type"] = *q.Type
|
||||
}
|
||||
if q.Uid != nil {
|
||||
filter["uid"] = *q.Uid
|
||||
}
|
||||
sort := bson.D{{"reviewAt", -1}}
|
||||
res.List, res.Total, res.HasNext, err = aiplazamod.GetList(filter, int64(q.Skip()), int64(q.Limit()), sort)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
Type int `json:"type" form:"type" binding:"required"` // 1-ai图片换脸 2-ai视频换脸 3-ai脱衣 4-ai图生视频 5-ai绘画
|
||||
Template string `json:"template" form:"template"` // 关联模版id
|
||||
Uid uint64 `json:"uid" form:"uid" binding:"required"` //
|
||||
Title string `json:"title" form:"title"` // 标题
|
||||
OriginalImage string `json:"originalImage" form:"originalImage"` // 原图
|
||||
OriginalVideo string `json:"originalVideo" form:"originalVideo"` // 原视频
|
||||
OriginalVideoCover string `json:"originalVideoCover" form:"originalVideoCover"` // 原视频封面
|
||||
OriginContent string `json:"originContent" form:"originContent"` // 输入的原文
|
||||
GenerateImage string `json:"generateImage" form:"generateImage"` // 生成的图
|
||||
GenerateVideo string `json:"generateVideo" form:"generateVideo"` // 生成的视频
|
||||
GenerateVideoCover string `json:"generateVideoCover" form:"generateVideoCover"` // 生成的视频封面
|
||||
Status int `json:"status" form:"status"` // 0-待审核 1-审核通过 2-拒绝 3-下架
|
||||
Reason string `json:"reason" form:"reason"` // 原因
|
||||
SortCode int `json:"sortCode" form:"sortCode"` // 置顶排序号,大于0就是置顶
|
||||
ReviewAt time.Time `json:"reviewAt" form:"reviewAt"` // 审核时间
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create() error {
|
||||
data := aiplazamod.AiPlaza{
|
||||
Type: p.Type,
|
||||
Template: p.Template,
|
||||
Uid: p.Uid,
|
||||
Title: p.Title,
|
||||
OriginalImage: p.OriginalImage,
|
||||
OriginalVideo: p.OriginalVideo,
|
||||
OriginContent: p.OriginContent,
|
||||
GenerateImage: p.GenerateImage,
|
||||
GenerateVideo: p.GenerateVideo,
|
||||
GenerateVideoCover: p.GenerateVideoCover,
|
||||
Status: p.Status,
|
||||
Reason: p.Reason,
|
||||
SortCode: p.SortCode,
|
||||
ReviewAt: p.ReviewAt,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := aiplazadata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
Type *int `json:"type"` //
|
||||
Template *string `json:"template" form:"template"` // 关联模版id
|
||||
Uid *uint64 `json:"uid"` //
|
||||
Title *string `json:"title"` // 标题
|
||||
OriginalImage *string `json:"originalImage"` // 原图
|
||||
OriginalVideo *string `json:"originalVideo"` // 原视频
|
||||
OriginalVideoCover *string `json:"originalVideoCover" form:"originalVideoCover"` // 原视频封面
|
||||
OriginContent *string `json:"originContent"` // 输入的原文
|
||||
GenerateImage *string `json:"generateImage"` // 生成的图
|
||||
GenerateVideo *string `json:"generateVideo"` // 生成的视频
|
||||
GenerateVideoCover *string `json:"generateVideoCover" form:"generateVideoCover"` // 生成的视频封面
|
||||
Status *int `json:"status"` // 0-待审核 1-审核通过 2-拒绝 3-下架
|
||||
Reason *string `json:"reason"` // 原因
|
||||
SortCode *int `json:"sortCode"` // 置顶排序号,大于0就是置顶
|
||||
ReviewAt *time.Time `json:"reviewAt"` // 审核时间
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
post, err := aiplazamod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Type != nil {
|
||||
data["type"] = *p.Type
|
||||
}
|
||||
if p.Template != nil {
|
||||
data["template"] = *p.Template
|
||||
}
|
||||
if p.Uid != nil {
|
||||
data["uid"] = *p.Uid
|
||||
}
|
||||
if p.Title != nil {
|
||||
data["title"] = *p.Title
|
||||
}
|
||||
if p.OriginalImage != nil {
|
||||
data["originalImage"] = *p.OriginalImage
|
||||
}
|
||||
if p.OriginalVideo != nil {
|
||||
data["originalVideo"] = *p.OriginalVideo
|
||||
}
|
||||
if p.OriginalVideoCover != nil {
|
||||
data["originalVideoCover"] = *p.OriginalVideoCover
|
||||
}
|
||||
if p.OriginContent != nil {
|
||||
data["originContent"] = *p.OriginContent
|
||||
}
|
||||
if p.GenerateImage != nil {
|
||||
data["generateImage"] = *p.GenerateImage
|
||||
}
|
||||
if p.GenerateVideo != nil {
|
||||
data["generateVideo"] = *p.GenerateVideo
|
||||
}
|
||||
if p.GenerateVideoCover != nil {
|
||||
data["generateVideoCover"] = *p.GenerateVideoCover
|
||||
}
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
// 审核后上线时间为当前时间
|
||||
if post.Status != *p.Status && *p.Status == 1 {
|
||||
data["reviewAt"] = time.Now()
|
||||
}
|
||||
}
|
||||
if p.Reason != nil {
|
||||
data["reason"] = *p.Reason
|
||||
}
|
||||
if p.SortCode != nil {
|
||||
data["sortCode"] = *p.SortCode
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
|
||||
if _, err = aiplazadata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID []primitive.ObjectID `json:"ids" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
if err := aiplazadata.DeleteData(nil, p.ID...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebBatchUpdateReq struct {
|
||||
Ids []primitive.ObjectID `json:"ids" binding:"required"`
|
||||
Status *int `json:"status"` // 0-待审核 1-审核通过 2-拒绝 3-下架
|
||||
Reason *string `json:"reason"` // 原因
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebBatchUpdateReq) Update() error {
|
||||
data := make(map[string]interface{})
|
||||
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
if *p.Status == 1 {
|
||||
data["reviewAt"] = time.Now()
|
||||
}
|
||||
}
|
||||
if p.Reason != nil {
|
||||
data["reason"] = *p.Reason
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
|
||||
if _, err := aiplazamod.UpdateByIds(nil, p.Ids, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package annouser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/annoumod"
|
||||
"91porn-server/web/vidhelp"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// GetAnnouList 获取公告列表
|
||||
func GetAnnouList(req annoumod.ListReq) (code stderr.Code, data interface{}) {
|
||||
if len(req.Sort) == 0 {
|
||||
req.Sort = "createdAt"
|
||||
}
|
||||
desc := -1
|
||||
if req.Desc == 1 {
|
||||
desc = req.Desc
|
||||
}
|
||||
aInfos, _, total, err := annoumod.GetAnnou(req.PageNumber, req.PageSize, req.Sort, desc)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError, nil
|
||||
}
|
||||
return stderr.Success, annoumod.ListResp{AInfos: aInfos, Total: total}
|
||||
}
|
||||
|
||||
// UpdateAnnou 更新公告信息
|
||||
func UpdateAnnou(req annoumod.EditReq) (code stderr.Code, data interface{}) {
|
||||
oid, err := primitive.ObjectIDFromHex(req.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("exchange string(%s) to objectID error.", req.ID))
|
||||
return stderr.ErrParamError, nil
|
||||
}
|
||||
m := vidhelp.Struct2Map(req.EditInfo)
|
||||
cnt, err := annoumod.UpdateAnnou(oid, m)
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError, nil
|
||||
}
|
||||
return stderr.Success, annoumod.OpeResp{Count: cnt}
|
||||
}
|
||||
|
||||
// DeleteAnnou 删除公告
|
||||
func DeleteAnnou(ids []string) (code stderr.Code, data interface{}) {
|
||||
oids := make([]primitive.ObjectID, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
oid, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("exchange string(%s) to objectID error.", id))
|
||||
continue
|
||||
}
|
||||
oids = append(oids, oid)
|
||||
}
|
||||
cnt, err := annoumod.DeleteAnnou(oids)
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError, nil
|
||||
}
|
||||
return stderr.Success, annoumod.OpeResp{Count: cnt}
|
||||
}
|
||||
|
||||
// AddAnnou 添加公告
|
||||
func AddAnnou(req annoumod.AddReq) (code stderr.Code, data interface{}) {
|
||||
infos := []annoumod.Annou{annoumod.Annou{
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
Cover: req.Cover,
|
||||
Type: req.Type,
|
||||
Href: req.Href,
|
||||
Active: req.Active,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}}
|
||||
if err := annoumod.AddAnnou(infos); err != nil {
|
||||
return stderr.ErrDbInsertError, ""
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package annouser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/annoumod"
|
||||
"91porn-server/web/vidhelp"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
// GetAnnounceList 获取会员中心跑马灯列表
|
||||
func GetAnnounceListApi() (code stderr.Code, announce string) {
|
||||
aInfos, err := annoumod.GetAnnounceApi("createdAt", -1)
|
||||
if err != nil && err != mongo.ErrNoDocuments {
|
||||
return stderr.ErrDbQueryError, ""
|
||||
}
|
||||
if err == mongo.ErrNoDocuments || len(aInfos) <= 0 {
|
||||
return stderr.Success, ""
|
||||
}
|
||||
return stderr.Success, aInfos[0].Content
|
||||
}
|
||||
|
||||
// GetAnnounceList 获取会员中心跑马灯列表
|
||||
func GetAnnounceList() (code stderr.Code, data []annoumod.Announce) {
|
||||
aInfos, err := annoumod.GetAnnounce("createdAt", -1)
|
||||
if err != nil && err != mongo.ErrNoDocuments {
|
||||
return stderr.ErrDbQueryError, nil
|
||||
}
|
||||
if err == mongo.ErrNoDocuments || len(aInfos) <= 0 {
|
||||
return stderr.Success, nil
|
||||
}
|
||||
return stderr.Success, aInfos
|
||||
}
|
||||
|
||||
// UpdateAnnounce 更新会员中心跑马灯信息
|
||||
func UpdateAnnounce(req annoumod.EditAnnounceReq) (code stderr.Code, data interface{}) {
|
||||
oid, err := primitive.ObjectIDFromHex(req.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("exchange string(%s) to objectID error.", req.ID))
|
||||
return stderr.ErrParamError, nil
|
||||
}
|
||||
m := vidhelp.Struct2Map(req.EditAnnounceInfo)
|
||||
cnt, err := annoumod.UpdateAnnounce(oid, m)
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError, nil
|
||||
}
|
||||
return stderr.Success, annoumod.OpeResp{Count: cnt}
|
||||
}
|
||||
|
||||
// DeleteAnnounce 删除会员中心跑马灯
|
||||
func DeleteAnnounce(ids []string) (code stderr.Code, data interface{}) {
|
||||
oids := make([]primitive.ObjectID, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
oid, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("exchange string(%s) to objectID error.", id))
|
||||
continue
|
||||
}
|
||||
oids = append(oids, oid)
|
||||
}
|
||||
cnt, err := annoumod.DeleteAnnounce(oids)
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError, nil
|
||||
}
|
||||
return stderr.Success, annoumod.OpeResp{Count: cnt}
|
||||
}
|
||||
|
||||
// AddAnnounce 添加会员中心跑马灯
|
||||
func AddAnnounce(req annoumod.AddAnnounceReq) (code stderr.Code) {
|
||||
infos := []annoumod.Announce{annoumod.Announce{
|
||||
ID: primitive.NewObjectID(),
|
||||
Content: req.Content,
|
||||
Url: req.Url,
|
||||
Type: annoumod.UserCenter,
|
||||
Active: req.Active,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}}
|
||||
if err := annoumod.AddAnnounce(infos); err != nil {
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package authorser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"91porn-server/models/v/adminmod"
|
||||
"91porn-server/models/v/authoritymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ObjectID ObjectID
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
// SetAuthority SetAuthority
|
||||
func SetAuthority(adminID ObjectID, authID ObjectID) error {
|
||||
existed, err := adminmod.ExistsByID(adminID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service SetAuthority adminID Exists error: " + err.Error())
|
||||
}
|
||||
if !existed {
|
||||
return fmt.Errorf("service SetAuthority adminID is not exist")
|
||||
}
|
||||
existed, err = authoritymod.ExistsByID(authID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service SetAuthority authID Exists error: " + err.Error())
|
||||
}
|
||||
if !existed {
|
||||
return fmt.Errorf("service SetAuthority authID is not exist")
|
||||
}
|
||||
authority, err := authoritymod.FindOne(authoritymod.AuthorityDoc{
|
||||
ID: authID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("service SetAuthority authID is not existed" + err.Error())
|
||||
}
|
||||
if err = adminmod.UpdateByID(adminID, adminmod.AdminDoc{
|
||||
Role: &authority.Role,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("service SetAuthority faild " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnsetAuthority(adminID ObjectID) error {
|
||||
existed, err := adminmod.ExistsByID(adminID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("author UnsetAuthority adminID Exists error: " + err.Error())
|
||||
}
|
||||
if !existed {
|
||||
return fmt.Errorf("service UnsetAuthority adminID is not exist")
|
||||
}
|
||||
if err = adminmod.UnsetPrivilege(adminID); err != nil {
|
||||
return fmt.Errorf("service UnsetAuthority faild " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package backpack_service
|
||||
|
||||
import "91porn-server/models/v/backpackmod"
|
||||
|
||||
func QueryAll(in *backpackmod.QueryAllWebCond) (*backpackmod.QueryAllRes, error) {
|
||||
var out = &backpackmod.QueryAllRes{}
|
||||
count, err := backpackmod.QueryAllCount(in.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*backpackmod.Backpack{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = backpackmod.QueryAllList(in.Filter(), in.Options())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
Executable
+129
@@ -0,0 +1,129 @@
|
||||
package bannerjumpser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/bannerjumpdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/bannerjumpmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []bannerjumpmod.BannerJump `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
filter := bson.M{}
|
||||
res.List, res.Total, res.HasNext, err = bannerjumpmod.GetList(filter, int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
Position int `json:"position" form:"position" binding:"required"` // 位置 1-视频播放器下方 2-收益页 3-首页浮窗
|
||||
Banner string `json:"banner" form:"banner" binding:"required"` // BANNER图片
|
||||
Title string `json:"title" form:"title" binding:"required"` // 标题
|
||||
Url string `json:"url" form:"url" binding:"required"` // 跳转地址
|
||||
StartAt time.Time `json:"startAt" form:"startAt" binding:"required"` // 开始时间
|
||||
EndAt time.Time `json:"endAt" form:"endAt" binding:"required"` // 结束时间
|
||||
Weight int `json:"weight" form:"weight" binding:"required"` // 权重
|
||||
Enable bool `json:"enable" form:"enable" binding:"required"` // 是否启用
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create() error {
|
||||
data := bannerjumpmod.BannerJump{
|
||||
Position: p.Position,
|
||||
Banner: p.Banner,
|
||||
Title: p.Title,
|
||||
Url: p.Url,
|
||||
StartAt: p.StartAt,
|
||||
EndAt: p.EndAt,
|
||||
Weight: p.Weight,
|
||||
Enable: p.Enable,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := bannerjumpdata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
Position *int `json:"position"` // 位置 1-视频播放器下方 2-收益页 3-首页浮窗
|
||||
Banner *string `json:"banner"` // BANNER图片
|
||||
Title *string `json:"title"` // 标题
|
||||
Url *string `json:"url"` // 跳转地址
|
||||
StartAt *time.Time `json:"startAt"` // 开始时间
|
||||
EndAt *time.Time `json:"endAt"` // 结束时间
|
||||
Weight *int `json:"weight"` // 权重
|
||||
Enable *bool `json:"enable"` // 是否启用
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
_, err := bannerjumpmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Position != nil {
|
||||
data["position"] = *p.Position
|
||||
}
|
||||
if p.Banner != nil {
|
||||
data["banner"] = *p.Banner
|
||||
}
|
||||
if p.Title != nil {
|
||||
data["title"] = *p.Title
|
||||
}
|
||||
if p.Url != nil {
|
||||
data["url"] = *p.Url
|
||||
}
|
||||
if p.StartAt != nil {
|
||||
data["startAt"] = *p.StartAt
|
||||
}
|
||||
if p.EndAt != nil {
|
||||
data["endAt"] = *p.EndAt
|
||||
}
|
||||
if p.Weight != nil {
|
||||
data["weight"] = *p.Weight
|
||||
}
|
||||
if p.Enable != nil {
|
||||
data["enable"] = *p.Enable
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
|
||||
if _, err = bannerjumpdata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
if err := bannerjumpdata.DeleteData(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package certificateser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/common/ysqr"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/web/middleware/authweb"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// QrCode doc
|
||||
// @Summary 二维码
|
||||
// @Description 二维码
|
||||
// @Tags 凭证
|
||||
// @Accept mpfd,json
|
||||
// @Produce json,html
|
||||
// @Param token formData string true "令牌"
|
||||
// @Success 200 {string} json "{"msg": "操作成功", "content": "", user:{}}"
|
||||
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||
// @Router /ext/certificate/qr [post]
|
||||
func QrCode(c *gin.Context) {
|
||||
var args struct {
|
||||
Token string `form:"token" json:"token" binding:"required"` //token
|
||||
}
|
||||
if err := c.ShouldBind(&args); err != nil {
|
||||
common.ServeJSON(c, stderr.ErrParamError, "certificateser QrCode arg error "+err.Error())
|
||||
return
|
||||
}
|
||||
claims, err := authweb.ParseWebClaims(args.Token)
|
||||
if err != nil {
|
||||
log.Error("activity InvitInfo ParseWebClaims error", log.Any("token", args.Token), log.E(err))
|
||||
common.ServeJSON(c, stderr.InvalidToken, "")
|
||||
return
|
||||
}
|
||||
user, _ := usermod.FindUserByUID(claims.UID)
|
||||
if user == nil {
|
||||
common.ServeJSON(c, stderr.AdminIsNotExist, "")
|
||||
return
|
||||
}
|
||||
content := ysqr.Content{
|
||||
UID: user.UID,
|
||||
T: ysqr.Login,
|
||||
LoginClaims: ysqr.LoginClaims{
|
||||
DevID: user.DevID,
|
||||
},
|
||||
}
|
||||
u := gin.H{
|
||||
"uid": user.UID,
|
||||
"name": user.Name,
|
||||
"portrait": user.Portrait,
|
||||
}
|
||||
common.ServeJSON(c, stderr.Success, gin.H{
|
||||
"content": content.String(authweb.GetTokenSecret()),
|
||||
"user": u,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package chat_message_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/messagemod"
|
||||
"91porn-server/models/v/sessionmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
func List(req *messagemod.WebListRequest) (interface{}, stderr.Code) {
|
||||
var data = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := messagemod.CountDocument(req.Filter())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := messagemod.QueryAllDocument(req.Filter(), req.Options())
|
||||
if err != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func Del(req *messagemod.WebDelReq) stderr.Code {
|
||||
data, err := messagemod.FindOneByFilter(req.Filter())
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return stderr.CodeEmptyData
|
||||
}
|
||||
|
||||
err = messagemod.RemoveDocument(req.Filter())
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// AddPrivateLetter 新增私信消息
|
||||
func AddPrivateLetter(req *messagemod.SendMsg) stderr.Code {
|
||||
if req.Sender == 0 || req.Receiver == 0 {
|
||||
return stderr.ErrParamError
|
||||
}
|
||||
data, err := usermod.FindUserByUID(req.Sender)
|
||||
if err != nil || data == nil {
|
||||
log.Warn(fmt.Sprintf("messageser Sender is null"), log.Any("Sender", req.Sender))
|
||||
return stderr.CommentUserInfoErr
|
||||
}
|
||||
|
||||
if req.Sender == req.Receiver {
|
||||
log.Warn(fmt.Sprintf("messageser req.Sender == req.Receiver"), log.Any("Sender", req.Sender), log.Any("Receiver", req.Receiver))
|
||||
return stderr.NoSelfAddMsg
|
||||
}
|
||||
|
||||
//查询被私信的用户是否存在
|
||||
takeUser, err := usermod.FindUserByUID(req.Receiver)
|
||||
if err != nil {
|
||||
return stderr.CommentUserInfoErr
|
||||
}
|
||||
if takeUser == nil {
|
||||
log.Warn(fmt.Sprintf("messageser Receiver is null"), log.Any("Receiver", req.Receiver))
|
||||
return stderr.CommentUserInfoErr
|
||||
}
|
||||
//会话id
|
||||
sessionId := ""
|
||||
//新增私聊信息之前, 先查询是否有会话信息
|
||||
cond := bson.M{"$or": []bson.M{bson.M{"sendUid": req.Sender, "takeUid": req.Receiver}, bson.M{"sendUid": req.Receiver, "takeUid": req.Sender}}}
|
||||
session, err := sessionmod.FindOneByFilter(cond)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("messageser Add sessionmod.FindOneByFilter error:%+v:", err), log.Any("Sender", req.Sender), log.Any("Receiver", req.Receiver))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
t := time.Now()
|
||||
if session.ID.IsZero() {
|
||||
sessionId = common.UUID()
|
||||
//新增session
|
||||
s := &sessionmod.Session{
|
||||
SendUid: req.Sender,
|
||||
TakeUid: req.Receiver,
|
||||
SessionId: sessionId,
|
||||
CreatedAt: t,
|
||||
UpdatedAt: t,
|
||||
}
|
||||
err := sessionmod.InsertOne(nil, s)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("messageser Add sessionmod.InsertOne error:%+v:", err), log.Any("session", s), log.Any("Sender", req.Sender), log.Any("Receiver", req.Receiver))
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
} else {
|
||||
sessionId = session.SessionId
|
||||
}
|
||||
|
||||
code := PublicAddMsg(messagemod.PublicAddMsg{
|
||||
SendUid: req.Sender,
|
||||
TakeUid: req.Receiver,
|
||||
SessionId: sessionId,
|
||||
Content: req.Content,
|
||||
//ImgUrl: req.ImgUrl,// 暂时不支持图片
|
||||
MsgType: req.MsgType,
|
||||
ObjId: req.ObjId,
|
||||
ObjName: req.ObjName,
|
||||
})
|
||||
if code != stderr.Success {
|
||||
return code
|
||||
}
|
||||
common.Go(func() {
|
||||
//更新会话预览消息
|
||||
content := req.Content
|
||||
// 暂时不支持图片
|
||||
//if len(req.ImgUrl) > 0 && content == "" {
|
||||
// content = "发送了一张图片"
|
||||
//}
|
||||
err := sessionmod.UpdOneByFilter(nil, bson.M{"sessionId": sessionId}, bson.M{"$set": bson.M{"preContent": content, "updatedAt": t}})
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("messageser Add messagemod.UpdOneByFilter error:%+v:", err), log.Any("sessionId", sessionId), log.Any("preContent", req.Content), log.Any("Sender", req.Sender), log.Any("Receiver", req.Receiver))
|
||||
}
|
||||
})
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// PublicAddMsg 添加消息公共方法
|
||||
func PublicAddMsg(prams messagemod.PublicAddMsg) stderr.Code {
|
||||
//查询用户信息
|
||||
user, err := usermod.FindUserByUID(prams.SendUid)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if user == nil || user.HasLocked {
|
||||
return stderr.UserIsNotExists
|
||||
}
|
||||
now := time.Now()
|
||||
//新增消息
|
||||
msg := &messagemod.Message{
|
||||
SendUid: prams.SendUid,
|
||||
TakeUid: prams.TakeUid,
|
||||
MsgType: prams.MsgType,
|
||||
Content: prams.Content,
|
||||
ImgUrl: prams.ImgUrl,
|
||||
IsRead: false,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ObjId: prams.ObjId,
|
||||
ObjName: prams.ObjName,
|
||||
}
|
||||
// 是否加入未读提醒
|
||||
needTip := false
|
||||
switch prams.MsgType {
|
||||
case messagemod.PrivateLetterMsg:
|
||||
//私信
|
||||
msg.SessionId = prams.SessionId
|
||||
needTip = true
|
||||
case messagemod.OfficialPrivateLetterMsg:
|
||||
//官方私信
|
||||
msg.SessionId = prams.SessionId
|
||||
msg.ContentType = prams.ContentType
|
||||
needTip = true
|
||||
case messagemod.FollowMsg:
|
||||
//关注
|
||||
msg.Content = "关注了你"
|
||||
needTip = true
|
||||
case messagemod.LikeMsg:
|
||||
//点赞
|
||||
msg.Content = "点赞了你的帖子"
|
||||
msg.ObjId = prams.ObjId
|
||||
msg.ObjName = prams.ObjName
|
||||
needTip = true
|
||||
case messagemod.ForwardMsg: //转发
|
||||
msg.Content = "转发了你的帖子"
|
||||
msg.ObjId = prams.ObjId
|
||||
msg.ObjName = prams.ObjName
|
||||
case messagemod.CommentMsg: //评论
|
||||
msg.Content = "评论了你的帖子"
|
||||
msg.ObjId = prams.ObjId
|
||||
msg.ObjName = prams.ObjName
|
||||
needTip = true
|
||||
case messagemod.DesireMsg:
|
||||
msg.Content = "回复了你的工单"
|
||||
msg.ObjId = prams.ObjId
|
||||
msg.ObjName = prams.ObjName
|
||||
needTip = true
|
||||
case messagemod.LikeCommentMsg: // 点赞评论
|
||||
msg.Content = "点赞了你的评论"
|
||||
msg.ObjId = prams.ObjId
|
||||
msg.ObjName = prams.ObjName
|
||||
case messagemod.ReplyCommentMsg: // 回复评论
|
||||
msg.Content = "回复了你的评论"
|
||||
msg.ObjId = prams.ObjId
|
||||
msg.ObjName = prams.ObjName
|
||||
needTip = true
|
||||
}
|
||||
_, err = messagemod.InsertOne(nil, msg)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("messageser Add messagemod.InsertOne error:%+v:", err), log.Any("message", msg))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
go func() {
|
||||
err = usermod.IncDynamic(prams.TakeUid)
|
||||
}()
|
||||
// 更新提示状态
|
||||
if needTip {
|
||||
//_ = message.SetTip(prams.TakeUid)
|
||||
}
|
||||
|
||||
return stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package chatrobotser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/chatrobotmod"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
// 获取机器人配置列表
|
||||
func GetRobotConfList(param chatrobotmod.ReqConfList) (code stderr.Code, data interface{}, err error) {
|
||||
var skip = int64((param.PageNumber - 1) * param.PageSize)
|
||||
var limit = int64(param.PageSize)
|
||||
total, cList, err := chatrobotmod.GetRobotConf(param.Type, skip, limit)
|
||||
if err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
if cList == nil {
|
||||
data = commod.ListResp{List: []chatrobotmod.ChatRobot{}}
|
||||
} else {
|
||||
list := make([]chatrobotmod.RespList, len(cList))
|
||||
for i, v := range cList {
|
||||
tmp := chatrobotmod.RespList{}
|
||||
if err = copier.Copy(&tmp, &v); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
list[i] = tmp
|
||||
}
|
||||
data = commod.ListResp{List: list, Total: total}
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加配置
|
||||
func AddRobotConf(param chatrobotmod.ReqAdd) (code stderr.Code, data interface{}, err error) {
|
||||
if code, err = addCheck(param.Type); err != nil || code != stderr.Success {
|
||||
return
|
||||
}
|
||||
switch param.Type {
|
||||
case chatrobotmod.Comment: // 评论机器人
|
||||
return addCmtRobotConf(param.Type, param.ReqAddCmt)
|
||||
default:
|
||||
code = stderr.RobotTypeInvalid
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addCheck(rType chatrobotmod.EnumRobotType) (stderr.Code, error) {
|
||||
isExists, err := chatrobotmod.ConfExists(rType)
|
||||
if err != nil {
|
||||
return stderr.Failure, err
|
||||
}
|
||||
if isExists {
|
||||
return stderr.RobotTypeExisted, nil
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
|
||||
// 新增评论机器人配置
|
||||
func addCmtRobotConf(rType chatrobotmod.EnumRobotType, param chatrobotmod.ReqAddCmt) (code stderr.Code, data interface{}, err error) {
|
||||
var now = time.Now()
|
||||
var doc = chatrobotmod.ChatRobot{
|
||||
Type: rType,
|
||||
IsActive: param.IsActive,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
CommentRobot: chatrobotmod.CommentRobot{
|
||||
From: param.From,
|
||||
To: param.To,
|
||||
TotalLimit: param.TotalLimit,
|
||||
PureLimit: param.PureLimit,
|
||||
Frequency: param.Frequency,
|
||||
MaxCommentNum: param.MaxCommentNum,
|
||||
MaxFakeLikes: param.MaxFakeLikes,
|
||||
MinFakeLikes: param.MinFakeLikes,
|
||||
MaxFakePlayCount: param.MaxFakePlayCount,
|
||||
MinFakePlayCount: param.MinFakePlayCount,
|
||||
},
|
||||
}
|
||||
if err = chatrobotmod.Insert(doc); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 更新机器人配置
|
||||
func UpdateRobotConf(param chatrobotmod.ReqUpdate) (code stderr.Code, data interface{}, err error) {
|
||||
switch param.Type {
|
||||
case chatrobotmod.Comment: // 评论机器人
|
||||
return updateCmtRobotConf(param.Type, param.ReqUpdateCmt)
|
||||
default:
|
||||
code = stderr.RobotTypeInvalid
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 更新评论机器人配置
|
||||
func updateCmtRobotConf(rType chatrobotmod.EnumRobotType, param chatrobotmod.ReqUpdateCmt) (code stderr.Code, data interface{}, err error) {
|
||||
doc := chatrobotmod.CmtUpdateDoc{}
|
||||
if err = copier.Copy(&doc, ¶m); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
doc.UpdatedAt = time.Now()
|
||||
if err = chatrobotmod.UpdateCmtRobotConf(rType, doc); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
package commentser
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/taskser"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/cmtmod"
|
||||
"91porn-server/models/v/dailytaskmod"
|
||||
"91porn-server/models/v/locmod"
|
||||
"91porn-server/models/v/messagemod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"91porn-server/web/service/chat_message_ser"
|
||||
"91porn-server/web/vidhelp"
|
||||
"91porn-server/web/webg"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// GetCommentList 后台获取评论列表
|
||||
func GetCommentList(vidID string, cUID uint64, content string, isRobot, isAdvertiser *bool, status *int, page commod.Page) (data interface{}, err error) {
|
||||
cList, count, err := cmtmod.CmtFindCommentsAndCount(vidID, cUID, content, isRobot, isAdvertiser, status, page)
|
||||
if err != nil {
|
||||
log.Error("web service comment GetCommentList FindUserByUID error", log.E(err), log.Any("vidID", vidID), log.Any("cUID", cUID), log.Any("content", content))
|
||||
return
|
||||
}
|
||||
var cmtList []cmtmod.WebCmtRespList
|
||||
var uids []uint64
|
||||
for _, v := range cList {
|
||||
// 获取评论的用户是否已禁言
|
||||
usr, err := usermod.FindUserByUID(v.UserID)
|
||||
if err != nil || usr == nil {
|
||||
log.Error("web service comment GetCommentList FindUserByUID error", log.E(err), log.Any("uid", v.UserID))
|
||||
continue // 避免测试服脏数据造成无数据返回
|
||||
}
|
||||
tmp := cmtmod.WebCmtRespList{
|
||||
ID: v.ID,
|
||||
ObjID: v.ObjID,
|
||||
UserID: v.UserID,
|
||||
LikeCount: v.LikeCount,
|
||||
Content: v.Content,
|
||||
Level: v.Level,
|
||||
Status: v.Status,
|
||||
IsDelete: v.IsDelete,
|
||||
LinkStr: v.LinkStr,
|
||||
IsBaned: usr.HasBanned,
|
||||
IsGodComment: v.IsGodComment,
|
||||
Portrait: usr.Portrait,
|
||||
Name: usr.Name,
|
||||
CreatedAt: v.CreatedAt,
|
||||
IsRobot: v.IsRobot,
|
||||
HasLocked: usr.HasLocked,
|
||||
IsAdvertiser: usr.IsAdvertiser,
|
||||
Image: v.Image,
|
||||
}
|
||||
cmtList = append(cmtList, tmp)
|
||||
uids = append(uids, v.UserID)
|
||||
}
|
||||
if cmtList == nil {
|
||||
cmtList = []cmtmod.WebCmtRespList{}
|
||||
}
|
||||
|
||||
resp := make(map[string]interface{})
|
||||
resp["list"] = cmtList
|
||||
resp["total"] = count
|
||||
resp["uids"] = uids
|
||||
data = resp
|
||||
return
|
||||
}
|
||||
|
||||
// 后台评论管理屏蔽评论
|
||||
func DeleteCommentsByID(ids []primitive.ObjectID, uid uint64, content string) (err error) {
|
||||
var cmtList []cmtmod.Comment
|
||||
if uid != 0 {
|
||||
// 获取评论
|
||||
if cmtList, err = cmtmod.GetCommentsByUID(uid); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cmtmod.DelCommentsByUID(uid); err != nil {
|
||||
return
|
||||
}
|
||||
} else if content != "" {
|
||||
if cmtList, err = cmtmod.GetCommentsByContent(content); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cmtmod.DelCommentsByContent(content); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if ids == nil || len(ids) == 0 {
|
||||
return
|
||||
}
|
||||
if cmtList, err = cmtmod.GetCommentsByIDS(ids); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cmtmod.DelCommentsByIDS(ids); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// 评论数更新
|
||||
for _, v := range cmtList {
|
||||
// 视频评论数-1
|
||||
_ = vidhelp.DecCommentCount(v.ObjID)
|
||||
// 删除子评论
|
||||
if v.Level == 1 {
|
||||
var childIDS []primitive.ObjectID
|
||||
// 获取子评论
|
||||
childCmtList, _ := cmtmod.GetCommentsByCID(v.ObjID, v.ID)
|
||||
for _, v1 := range childCmtList {
|
||||
childIDS = append(childIDS, v1.ID)
|
||||
// 视频评论数-1
|
||||
_ = vidhelp.DecCommentCount(v1.ObjID)
|
||||
}
|
||||
if childIDS == nil || len(childIDS) == 0 {
|
||||
continue
|
||||
}
|
||||
// 删除评论
|
||||
_ = cmtmod.DelCommentsByIDS(childIDS)
|
||||
}
|
||||
// 评论数-1
|
||||
if v.Level == 2 && !v.CID.IsZero() {
|
||||
_ = cmtmod.CmtDecreaseCommentCountByID(v.CID)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 后台获取评论详情
|
||||
func GetCommentInfo(id primitive.ObjectID) (data interface{}, err error) {
|
||||
return cmtmod.CmtFindOneByID(id)
|
||||
}
|
||||
|
||||
// AccessComment 后台评论通过审核
|
||||
func AccessComment(id primitive.ObjectID) (err error) {
|
||||
cmt, err := cmtmod.CmtFindOneByID(id)
|
||||
if err != nil || cmt.ID.IsZero() {
|
||||
log.Error("web service comment AccessComment CmtFindOneByID error", log.E(err), log.Any("id", id))
|
||||
return
|
||||
}
|
||||
err = cmtmod.CmtUpdateCommentStatusByID(id, 1)
|
||||
if err != nil {
|
||||
log.Error("web service comment GetCommentList CmtUpdateCommentStatusByID error", log.E(err), log.Any("uid", id))
|
||||
return
|
||||
}
|
||||
// 审核通过增加评论数
|
||||
err = vidmod.IncCommentCount(cmt.ObjID)
|
||||
if err != nil {
|
||||
log.Error("web service comment GetCommentList IncCommentCount error", log.E(err), log.Any("uid", id))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BatchAccessComment 批量后台评论通过审核
|
||||
func BatchAccessComment(ids []primitive.ObjectID, status int) (err error) {
|
||||
cmt, err := cmtmod.CmtFindOneByIds(ids)
|
||||
if err != nil || len(cmt) <= 0 {
|
||||
log.Error("web service comment AccessComment CmtFindOneByIds error", log.E(err), log.Any("ids", ids))
|
||||
return
|
||||
}
|
||||
err = cmtmod.PassCommentStatusByIds(ids, status)
|
||||
if err != nil {
|
||||
log.Error("web service BatchAccessComment PassCommentStatusByIds error", log.E(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PublishComment 发表评论 统一以作者身份回复
|
||||
func PublishComment(param cmtmod.PublishReqInfo) (code stderr.Code, err error) {
|
||||
videoInfo, err := vidmod.GetVideoInfo(param.ObjID.Hex())
|
||||
if err != nil || videoInfo.ID.IsZero() {
|
||||
code = stderr.CommentErrSendFail
|
||||
code.SetMsg("current video is not exist or find error")
|
||||
return code, err
|
||||
}
|
||||
var cmtMod cmtmod.Comment
|
||||
isAuthor := false
|
||||
if videoInfo.PublisherID == param.ToUserID {
|
||||
isAuthor = true
|
||||
}
|
||||
if code, cmtMod, err = getTrackInfo(videoInfo, "", isAuthor, param); code != stderr.Success || err != nil {
|
||||
return code, err
|
||||
}
|
||||
// 保存到数据库
|
||||
id, err := cmtmod.CmtInsertOne(&cmtMod)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, err
|
||||
}
|
||||
if err = setGid(param.CID, param.RID, id); err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, err
|
||||
}
|
||||
// 增加评论数
|
||||
incCommentCount(param.CID, param.ObjID)
|
||||
// 获取该评论
|
||||
return
|
||||
}
|
||||
|
||||
// 获取相关信息
|
||||
func getTrackInfo(videoInfo vidmod.VideoModel, ip string, isAuthor bool, param cmtmod.PublishReqInfo) (code stderr.Code, data cmtmod.Comment, err error) {
|
||||
var cid primitive.ObjectID
|
||||
var publiserID = videoInfo.PublisherID
|
||||
if param.CID != "" {
|
||||
if cid, err = primitive.ObjectIDFromHex(param.CID); err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
}
|
||||
|
||||
loc, _ := locmod.GetLocationInfoByID(videoInfo.Location)
|
||||
now := time.Now()
|
||||
data = cmtmod.Comment{
|
||||
ObjID: param.ObjID,
|
||||
CID: cid,
|
||||
PublisherID: publiserID,
|
||||
SortCnt: 0,
|
||||
ToUserID: param.ToUserID,
|
||||
Content: param.Content,
|
||||
OrgContent: param.Content,
|
||||
UserID: param.ToUserID,
|
||||
Status: 1,
|
||||
Level: param.Level,
|
||||
AuthorMark: isAuthor,
|
||||
IsAuthor: isAuthor,
|
||||
IsDelete: false,
|
||||
CommentCount: 0,
|
||||
LikeCount: 0,
|
||||
IPAddr: ip,
|
||||
City: loc.City,
|
||||
IsRobot: true,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
if param.ToUserID != 0 {
|
||||
if param.Level == 1 {
|
||||
user, err := usermod.FindUserByUID(param.ToUserID)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
data.UserID = user.UID
|
||||
} else {
|
||||
rCmt, err := cmtmod.CmtFindOneByID(cid)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
user, err := usermod.FindUserByUID(rCmt.UserID)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
data.UserID = param.ToUserID
|
||||
data.ToUserID = rCmt.UserID
|
||||
data.ToUserName = user.Name
|
||||
}
|
||||
}
|
||||
if param.RID != "" {
|
||||
rid, err := primitive.ObjectIDFromHex(param.RID)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
rCmt, err := cmtmod.CmtFindOneByID(rid)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
user, err := usermod.FindUserByUID(rCmt.UserID)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
if rCmt.Level != 1 {
|
||||
// 如果回复的不是1级,则需要写入rid
|
||||
data.RID = rid
|
||||
}
|
||||
data.GID = rCmt.GID
|
||||
data.AuthorMark = rCmt.AuthorMark
|
||||
data.SortCnt = rCmt.SortCnt
|
||||
data.Type = cmtmod.CmtTypeReply
|
||||
data.PublisherID = rCmt.UserID
|
||||
data.ToUserName = user.Name
|
||||
data.ToUserID = rCmt.UserID
|
||||
if !rCmt.CID.IsZero() {
|
||||
data.CID = rCmt.CID
|
||||
}
|
||||
} else if param.RID == "" && !cid.IsZero() {
|
||||
data.Type = cmtmod.CmtTypeComment
|
||||
cCmt, err := cmtmod.CmtFindOneByID(cid)
|
||||
if err != nil {
|
||||
code = stderr.CommentErrSendFail
|
||||
return code, data, err
|
||||
}
|
||||
data.PublisherID = cCmt.UserID
|
||||
} else {
|
||||
data.PublisherID = videoInfo.PublisherID
|
||||
data.Type = cmtmod.CmtTypeVideo
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 设置分组id
|
||||
func setGid(cid string, rid string, id primitive.ObjectID) (err error) {
|
||||
if cid != "" && rid == "" {
|
||||
return cmtmod.SetGid(id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 增加评论数
|
||||
func incCommentCount(cid string, objID primitive.ObjectID) {
|
||||
if cid != "" {
|
||||
var objCid primitive.ObjectID
|
||||
objCid, err := primitive.ObjectIDFromHex(cid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 根据cid获取评论信息
|
||||
if toComment, err := cmtmod.CmtFindOneByID(objCid); err != nil || (toComment.Level != 1 && toComment.Level != 2) {
|
||||
return
|
||||
}
|
||||
if err := cmtmod.CmtIncreaseCommentCountByID(objCid); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// 增加视频评论数
|
||||
if err := vidhelp.IncCommentCount(objID); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateComment 后台评论设置
|
||||
func UpdateComment(in *cmtmod.EditCond) (err error) {
|
||||
cmt, err := cmtmod.CmtFindOneByID(in.ID)
|
||||
if err != nil || cmt.ID.IsZero() {
|
||||
log.Error("web service comment AccessComment CmtFindOneByID error", log.E(err), log.Any("id", in.ID))
|
||||
return errors.New("comment is err or null")
|
||||
}
|
||||
if in.LinkStr == nil && cmt.Level != 1 && in.LikeCount == nil {
|
||||
return errors.New("comment level is not 1")
|
||||
}
|
||||
|
||||
if in.LinkStr == nil && cmt.IsAuthor && in.LikeCount == nil {
|
||||
return errors.New("author not allow godComment")
|
||||
}
|
||||
|
||||
if in.LinkStr == nil && in.IsGodComment == nil && in.LikeCount == nil {
|
||||
return errors.New("update params is null")
|
||||
}
|
||||
// 如果设置为神评论
|
||||
// 判断是否已经发送过该评论的神评论奖励(避免修改为普通评论后又修改为神评论,奖励多次发放的情况)
|
||||
if in.IsGodComment != nil && *in.IsGodComment == true && !cmt.GodCommentAward {
|
||||
in.GodCommentAward = true
|
||||
}
|
||||
err = cmtmod.Update(in.Filter(), in.Update())
|
||||
if err != nil {
|
||||
log.Error("web service comment GetCommentList CmtUpdateCommentStatusByID error", log.E(err), log.Any("id", in.ID))
|
||||
return
|
||||
}
|
||||
/********************* 以下是神评相关逻辑 **********************/
|
||||
// 完成任务,评论被设置为神评
|
||||
if in.IsGodComment != nil && *in.IsGodComment == true {
|
||||
// 完成评论被设置为神评
|
||||
common.Go(func() {
|
||||
err = taskser.CompleteDailyTask(nil, cmt.UserID, dailytaskmod.CommentSetGod)
|
||||
if err != nil {
|
||||
log.Error("web 设置神评,完成每日任务【评论被设置为神评】失败", log.E(err), log.Any("id", in.ID))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取神评论奖励积分配置
|
||||
if webg.Conf.GodCommentAward <= 0 {
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// 查询出评论的帖子信息
|
||||
video, err := vidmod.GetVideoByID(cmt.ObjID)
|
||||
if err != nil || video == nil {
|
||||
return
|
||||
}
|
||||
objName := fmt.Sprintf("[%v]", video.Title)
|
||||
content := fmt.Sprintf("恭喜您在%v中评论获得神评,获得%v积分", objName, webg.Conf.GodCommentAward)
|
||||
// 发放积分
|
||||
walletInfo, err := walletmod.CreditIntegral(nil, webg.Conf.GodCommentAward, cmt.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := &txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: cmt.UserID,
|
||||
Integral: webg.Conf.GodCommentAward,
|
||||
ActualIntegral: float64(webg.Conf.GodCommentAward),
|
||||
TranType: txnmod.GodCommentAward.Key(),
|
||||
TranTypeInt: int64(txnmod.GodCommentAward),
|
||||
Desc: fmt.Sprintf("%s-积分[%d个]", txnmod.GodCommentAward.Key(), webg.Conf.GodCommentAward),
|
||||
RealIntegral: walletInfo.RealIntegral(),
|
||||
}
|
||||
txnmod.InsertTransactionLog(nil, log)
|
||||
|
||||
// 发放私信告诉用户获得了积分
|
||||
code := chat_message_ser.AddPrivateLetter(&messagemod.SendMsg{
|
||||
Sender: 100001,
|
||||
Receiver: cmt.UserID,
|
||||
Content: content,
|
||||
MsgType: messagemod.OfficialPrivateLetterMsg,
|
||||
ObjId: video.ID,
|
||||
ObjName: objName,
|
||||
})
|
||||
|
||||
return code
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
package commentser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/cmtmod"
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 缓存随机用户列表,避免每次都查询数据库
|
||||
var (
|
||||
cachedUsers []*usermod.User
|
||||
cachedUsersMu sync.RWMutex
|
||||
cachedUsersTime time.Time
|
||||
cachedUsersTTL = 10 * time.Minute
|
||||
)
|
||||
|
||||
func getReplyUsers() ([]*usermod.User, error) {
|
||||
cachedUsersMu.RLock()
|
||||
if len(cachedUsers) > 0 && time.Since(cachedUsersTime) < cachedUsersTTL {
|
||||
users := cachedUsers
|
||||
cachedUsersMu.RUnlock()
|
||||
return users, nil
|
||||
}
|
||||
cachedUsersMu.RUnlock()
|
||||
|
||||
cachedUsersMu.Lock()
|
||||
defer cachedUsersMu.Unlock()
|
||||
// double check
|
||||
if len(cachedUsers) > 0 && time.Since(cachedUsersTime) < cachedUsersTTL {
|
||||
return cachedUsers, nil
|
||||
}
|
||||
|
||||
users, _, err := usermod.FetchList(bson.M{"uid": bson.M{"$lte": 300000}}, options.Find().SetLimit(1000))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cachedUsers = users
|
||||
cachedUsersTime = time.Now()
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func PullCommentList(status string, limit int) (cmtmod.PullResp, error) {
|
||||
var resp cmtmod.PullResp
|
||||
filter := bson.M{}
|
||||
if limit <= 0 {
|
||||
limit = 20
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100
|
||||
}
|
||||
// 判断评论状态,抓取未审核的评论
|
||||
if status == "pending" {
|
||||
filter["status"] = 2
|
||||
}
|
||||
// 拉取已经审核成功且无子评论的一级评论
|
||||
if status == "pending_reply" {
|
||||
filter["status"] = 1
|
||||
filter["level"] = 1
|
||||
}
|
||||
// 查询评论列表
|
||||
opts := options.FindOptions{}
|
||||
opts.SetSort(bson.D{{Key: "createdAt", Value: 1}}).SetLimit(int64(limit))
|
||||
commentList, err := cmtmod.GetList(filter, &opts)
|
||||
if err != nil {
|
||||
log.Error("pull comment list fail", log.E(err))
|
||||
return resp, err
|
||||
}
|
||||
resp = cmtmod.PullResp{
|
||||
Code: 200,
|
||||
Msg: "success",
|
||||
Data: nil,
|
||||
}
|
||||
for _, comment := range commentList {
|
||||
pullComment := cmtmod.PullComment{
|
||||
Id: comment.ID.Hex(),
|
||||
UserId: strconv.FormatUint(comment.UserID, 10),
|
||||
Nickname: "",
|
||||
Content: comment.Content,
|
||||
CreatedAt: comment.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
Ip: "",
|
||||
Context: cmtmod.Context{
|
||||
ContentId: comment.ObjID.Hex(),
|
||||
ContentTitle: comment.Title,
|
||||
},
|
||||
}
|
||||
switch comment.ObjType {
|
||||
case cmtmod.OTypeVideo:
|
||||
pullComment.Context.ContentType = 1
|
||||
case cmtmod.OTypeCartoon:
|
||||
pullComment.Context.ContentType = 3
|
||||
default:
|
||||
pullComment.Context.ContentType = 2
|
||||
}
|
||||
resp.Data = append(resp.Data, pullComment)
|
||||
}
|
||||
log.Info("pull comment list success", log.Any("resp", resp))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func CommentApprove(comments cmtmod.ApproveReq) (err error) {
|
||||
var passCommentIDs []primitive.ObjectID
|
||||
var reviewCommentIDs []primitive.ObjectID
|
||||
// 遍历评论列表
|
||||
for _, comment := range comments.Reviews {
|
||||
oid, parseErr := primitive.ObjectIDFromHex(comment.CommentId)
|
||||
if parseErr != nil || oid.IsZero() {
|
||||
continue
|
||||
}
|
||||
reviewCommentIDs = append(reviewCommentIDs, oid)
|
||||
// 审核通过评论
|
||||
if comment.Action == "approve" {
|
||||
passCommentIDs = append(passCommentIDs, oid)
|
||||
} else if comment.Action == "reject" {
|
||||
// 评论没有通过,更新评论状态,以及评论没有通过原因
|
||||
err = cmtmod.Update(bson.M{"_id": oid}, bson.M{"$set": bson.M{"status": 4, "reason": comment.Reason, "desc": "Agent system review reject"}})
|
||||
if err != nil {
|
||||
log.Error("web service comment CommentApprove cmtmod.Update fail", log.E(err), log.Any("id", oid))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(passCommentIDs) > 0 {
|
||||
err = cmtmod.UpdateMany(bson.M{"_id": bson.M{"$in": passCommentIDs}}, bson.M{"$set": bson.M{"status": 1, "desc": "Agent system review"}})
|
||||
if err != nil {
|
||||
log.Error("web service comment CommentApprove cmtmod.UpdateMany fail", log.E(err), log.Any("_id", passCommentIDs))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(reviewCommentIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
commentList, err := cmtmod.GetCommentsByIDS(reviewCommentIDs)
|
||||
if err != nil {
|
||||
log.Error("web service comment CommentApprove cmtmod.GetCommentsByIDS fail", log.E(err), log.Any("ids", reviewCommentIDs))
|
||||
return err
|
||||
}
|
||||
go rebuildVideoAndMediaCommentCount(commentList)
|
||||
return
|
||||
}
|
||||
|
||||
func ReplyComment(comments cmtmod.RepliesReq) (cmtmod.ReplyResp, error) {
|
||||
now := time.Now()
|
||||
// 从缓存获取随机用户列表
|
||||
userInfos, err := getReplyUsers()
|
||||
if err != nil {
|
||||
log.Error("web service comment ReplyComment getReplyUsers fail", log.E(err))
|
||||
return cmtmod.ReplyResp{Code: 0, Msg: "web service comment ReplyComment Error"}, err
|
||||
}
|
||||
if len(userInfos) == 0 {
|
||||
return cmtmod.ReplyResp{Code: 0, Msg: "web service comment ReplyComment Error"}, fmt.Errorf("no available users for auto reply")
|
||||
}
|
||||
|
||||
// 先收集所有有效的评论ID
|
||||
oidMap := make(map[primitive.ObjectID]string, len(comments.Replies)) // oid -> reply content
|
||||
oids := make([]primitive.ObjectID, 0, len(comments.Replies))
|
||||
for _, reply := range comments.Replies {
|
||||
oid, parseErr := primitive.ObjectIDFromHex(reply.CommentId)
|
||||
if parseErr != nil || oid.IsZero() {
|
||||
continue
|
||||
}
|
||||
oids = append(oids, oid)
|
||||
oidMap[oid] = reply.Content
|
||||
}
|
||||
|
||||
// 批量查询评论信息
|
||||
cmtInfos, err := cmtmod.CmtFindOneByIds(oids)
|
||||
if err != nil {
|
||||
log.Error("web service comment ReplyComment cmtmod.CmtFindOneByIds fail", log.E(err))
|
||||
return cmtmod.ReplyResp{Code: 0, Msg: "web service comment ReplyComment Error"}, err
|
||||
}
|
||||
|
||||
affectedComments := make([]cmtmod.Comment, 0, len(cmtInfos))
|
||||
insertDocs := make([]cmtmod.Comment, 0, len(cmtInfos))
|
||||
for _, cmtInfo := range cmtInfos {
|
||||
if cmtInfo.ID.IsZero() {
|
||||
continue
|
||||
}
|
||||
content, ok := oidMap[cmtInfo.ID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rand := common.RandInt(0, len(userInfos))
|
||||
insertDocs = append(insertDocs, cmtmod.Comment{
|
||||
ID: primitive.NewObjectID(),
|
||||
ObjType: cmtInfo.ObjType,
|
||||
Title: cmtInfo.Title,
|
||||
ObjID: cmtInfo.ObjID,
|
||||
CID: cmtInfo.ID,
|
||||
RID: cmtmod.ObjectID{},
|
||||
GID: cmtmod.ObjectID{},
|
||||
Type: cmtmod.CmtTypeReply,
|
||||
PublisherID: 0,
|
||||
AuthorMark: false,
|
||||
SortCnt: 0,
|
||||
UserID: userInfos[rand].UID,
|
||||
ToUserID: 0,
|
||||
ToUserName: "",
|
||||
LikeCount: 0,
|
||||
CommentCount: 0,
|
||||
IsGodComment: false,
|
||||
IsAuthor: false,
|
||||
IsRobot: false,
|
||||
Content: content,
|
||||
OrgContent: "",
|
||||
Level: 2,
|
||||
IPAddr: "",
|
||||
Status: 1,
|
||||
Reason: "",
|
||||
Desc: "Agent system reply",
|
||||
Image: "",
|
||||
City: "",
|
||||
LinkStr: "",
|
||||
QuoteType: "",
|
||||
QuoteID: primitive.ObjectID{},
|
||||
QuoteImg: "",
|
||||
QuoteTitle: "",
|
||||
IsDelete: false,
|
||||
IsAdvertiser: false,
|
||||
CreatedAt: now,
|
||||
DeletedAt: time.Time{},
|
||||
})
|
||||
affectedComments = append(affectedComments, cmtInfo)
|
||||
}
|
||||
|
||||
if len(insertDocs) > 0 {
|
||||
_, err = cmtmod.CmtInsertMany(insertDocs)
|
||||
if err != nil {
|
||||
log.Error("web service comment ReplyComment cmtmod.CmtInsertMany fail", log.E(err))
|
||||
return cmtmod.ReplyResp{Code: 0, Msg: "web service comment ReplyComment Error"}, err
|
||||
}
|
||||
}
|
||||
|
||||
rebuildVideoAndMediaCommentCount(affectedComments)
|
||||
return cmtmod.ReplyResp{
|
||||
Code: 200,
|
||||
Msg: "success",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func rebuildVideoAndMediaCommentCount(comments []cmtmod.Comment) {
|
||||
if len(comments) == 0 {
|
||||
return
|
||||
}
|
||||
type objInfo struct {
|
||||
objType cmtmod.OType
|
||||
objID primitive.ObjectID
|
||||
}
|
||||
objMap := make(map[string]objInfo)
|
||||
for _, cmt := range comments {
|
||||
if cmt.ObjID.IsZero() {
|
||||
continue
|
||||
}
|
||||
if cmt.ObjType != cmtmod.OTypeVideo && cmt.ObjType != cmtmod.OTypeCartoon {
|
||||
continue
|
||||
}
|
||||
key := string(cmt.ObjType) + "_" + cmt.ObjID.Hex()
|
||||
objMap[key] = objInfo{
|
||||
objType: cmt.ObjType,
|
||||
objID: cmt.ObjID,
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, v := range objMap {
|
||||
obj := v
|
||||
wg.Add(1)
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
rebuildSingleObjCommentCount(obj.objType, obj.objID)
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func rebuildSingleObjCommentCount(objType cmtmod.OType, objID primitive.ObjectID) {
|
||||
cond := bson.M{
|
||||
"objType": objType,
|
||||
"objID": objID,
|
||||
"status": 1,
|
||||
"isAdvertiser": false,
|
||||
"isDelete": false,
|
||||
}
|
||||
count, err := cmtmod.CmtFindCountByCond(cond)
|
||||
if err != nil {
|
||||
log.Error("web service comment rebuildSingleObjCommentCount cmtmod.CmtFindCountByCond fail",
|
||||
log.E(err), log.Any("objType", objType), log.Any("objID", objID))
|
||||
return
|
||||
}
|
||||
|
||||
switch objType {
|
||||
case cmtmod.OTypeVideo:
|
||||
fakeDelta := 0
|
||||
videoInfo, err := vidmod.GetVideoInfo(objID.Hex())
|
||||
if err == nil && !videoInfo.ID.IsZero() {
|
||||
fakeDelta = videoInfo.FakeCommentCount - videoInfo.CommentCount
|
||||
if fakeDelta < 0 {
|
||||
fakeDelta = 0
|
||||
}
|
||||
}
|
||||
_, err = vidmod.UpdateOneByID(objID, bson.M{
|
||||
"commentCount": int(count),
|
||||
"fakeCommentCount": int(count) + fakeDelta,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("web service comment rebuildSingleObjCommentCount vidmod.UpdateOneByID fail",
|
||||
log.E(err), log.Any("objID", objID), log.Any("count", count))
|
||||
}
|
||||
case cmtmod.OTypeCartoon:
|
||||
_, err = mediacontentmod.UpdateOneByID(objID, bson.M{"countComment": count})
|
||||
if err != nil {
|
||||
log.Error("web service comment rebuildSingleObjCommentCount mediacontentmod.UpdateOneByID fail",
|
||||
log.E(err), log.Any("objID", objID), log.Any("count", count))
|
||||
}
|
||||
_, err = mediamod.UpdateByID(nil, objID, map[string]interface{}{"countComment": count})
|
||||
if err != nil {
|
||||
log.Error("web service comment rebuildSingleObjCommentCount mediamod.UpdateByID fail",
|
||||
log.E(err), log.Any("objID", objID), log.Any("count", count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ComicsTranslatePull(req *cmtmod.ComicsTitlePullReq) (cmtmod.ComicsTitlePullResp, error) {
|
||||
resp := cmtmod.ComicsTitlePullResp{List: make([]cmtmod.ComicsTitleInfo, 0)}
|
||||
if req == nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
opts := options.Find()
|
||||
pageNum := req.PageNum
|
||||
if pageNum <= 0 {
|
||||
pageNum = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
opts.SetSkip((pageNum - 1) * pageSize)
|
||||
opts.SetLimit(pageSize)
|
||||
opts.SetSort(bson.D{{Key: "_id", Value: -1}})
|
||||
|
||||
filter := bson.M{}
|
||||
if strings.TrimSpace(req.TagId) != "" {
|
||||
tagID, err := primitive.ObjectIDFromHex(strings.TrimSpace(req.TagId))
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
filter["tags"] = tagID
|
||||
}
|
||||
|
||||
list, err := mediamod.QueryAllList(filter, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for _, one := range list {
|
||||
resp.List = append(resp.List, cmtmod.ComicsTitleInfo{Id: one.ID.Hex(), Title: one.Title})
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func ComicsTranslatePush(req *cmtmod.ComicsTitlePushReq) (cmtmod.ComicsTitlePushRes, error) {
|
||||
resp := cmtmod.ComicsTitlePushRes{}
|
||||
if req == nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
var tagID primitive.ObjectID
|
||||
var hasTag bool
|
||||
if strings.TrimSpace(req.TagId) != "" {
|
||||
parsedTagID, err := primitive.ObjectIDFromHex(strings.TrimSpace(req.TagId))
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
tagID = parsedTagID
|
||||
hasTag = true
|
||||
}
|
||||
|
||||
for _, one := range req.List {
|
||||
mediaID, err := primitive.ObjectIDFromHex(strings.TrimSpace(one.Id))
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
update := bson.M{"title": strings.TrimSpace(one.Title), "updateTime": time.Now()}
|
||||
if hasTag {
|
||||
mediaInfo, err := mediamod.GetInfo(mediaID)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
newTags := make([]primitive.ObjectID, 0, len(mediaInfo.Tags))
|
||||
for _, tag := range mediaInfo.Tags {
|
||||
if tag != tagID {
|
||||
newTags = append(newTags, tag)
|
||||
}
|
||||
}
|
||||
update["tags"] = newTags
|
||||
}
|
||||
if _, err = mediamod.UpdateByID(nil, mediaID, update); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package contentlibser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/chatrobotmod"
|
||||
"91porn-server/models/v/contentlibmod"
|
||||
"91porn-server/web/webg"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
const RobotContentSet = "robot_content" // 机器人聊天内容集合
|
||||
|
||||
// 获取内容列表
|
||||
func GetContentList(param contentlibmod.ReqContentList) (code stderr.Code, data interface{}, err error) {
|
||||
var skip = int64((param.PageNumber - 1) * param.PageSize)
|
||||
var limit = int64(param.PageSize)
|
||||
total, cList, err := contentlibmod.GetContentList(param.RobotType, skip, limit)
|
||||
if err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
if cList == nil {
|
||||
data = commod.ListResp{List: []contentlibmod.RespList{}}
|
||||
} else {
|
||||
list := make([]contentlibmod.RespList, len(cList))
|
||||
for i, v := range cList {
|
||||
list[i] = contentlibmod.RespList(v)
|
||||
}
|
||||
data = commod.ListResp{List: list, Total: total}
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 增加机器人聊天内容
|
||||
func AddChatContent(param contentlibmod.ReqContentAdd) (code stderr.Code, data interface{}, err error) {
|
||||
// 检查类型是否有效
|
||||
switch param.RobotType {
|
||||
case chatrobotmod.Comment:
|
||||
default:
|
||||
code = stderr.RobotTypeInvalid
|
||||
return
|
||||
}
|
||||
// 内容不能为空
|
||||
if param.Content == "" {
|
||||
code = stderr.RobotContentIsNull
|
||||
return
|
||||
}
|
||||
var doc = contentlibmod.ContentLibrary{
|
||||
RobotType: param.RobotType,
|
||||
Content: param.Content,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
// 添加到redis
|
||||
if _, err = webg.Redis.SAdd(RobotContentSet, param.Content); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
if err = contentlibmod.Insert(doc); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除内容
|
||||
func DeleteChatContent(param contentlibmod.ReqContentDel) (code stderr.Code, data interface{}, err error) {
|
||||
if param.IDS == nil || len(param.IDS) == 0 {
|
||||
code = stderr.RobotContentDelParamIsNull
|
||||
return
|
||||
}
|
||||
// 获取内容
|
||||
cList, err := contentlibmod.GetContentListByIDS(param.IDS)
|
||||
if err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
if len(cList) == 0 {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
contents := make([]string, len(cList))
|
||||
for i, v := range cList {
|
||||
contents[i] = v.Content
|
||||
}
|
||||
// 从redis中移除
|
||||
if _, err = webg.Redis.SRem(RobotContentSet, contents); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
if err = contentlibmod.DeleteByIDS(param.IDS); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package contentreviewser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"91porn-server/models/v/contentreviewmod"
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ApplyResolution 把命中记录里管理员提交的修改写回到对应数据源
|
||||
// 按 TargetType 分发:
|
||||
//
|
||||
// VIDEO → vidmod.UpdateForReview(title, content, richText)
|
||||
// ACG_MEDIA → mediamod.UpdateForReview(title, summary)
|
||||
// ACG_CONTENT → mediacontentmod.UpdateForReview(name, text)
|
||||
//
|
||||
// 该函数不修改 issue 自身的 resolveStatus;调用方在写回成功后再调 ApproveIssue
|
||||
func ApplyResolution(issue *contentreviewmod.ReviewIssue) error {
|
||||
switch issue.TargetType {
|
||||
case contentreviewmod.IssueTargetVideo:
|
||||
return vidmod.UpdateForReview(
|
||||
issue.TargetID,
|
||||
issue.SubmittedTitle,
|
||||
issue.SubmittedContent,
|
||||
issue.SubmittedRichText,
|
||||
)
|
||||
case contentreviewmod.IssueTargetACGMedia:
|
||||
return mediamod.UpdateForReview(
|
||||
issue.TargetID,
|
||||
issue.SubmittedTitle,
|
||||
issue.SubmittedContent,
|
||||
)
|
||||
case contentreviewmod.IssueTargetACGContent:
|
||||
return mediacontentmod.UpdateForReview(
|
||||
issue.TargetID,
|
||||
issue.SubmittedTitle,
|
||||
issue.SubmittedContent,
|
||||
)
|
||||
default:
|
||||
return fmt.Errorf("unsupported targetType: %s", issue.TargetType)
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyOffShelfBatch 按 TargetType 分组把命中目标批量下架:
|
||||
//
|
||||
// VIDEO → vidmod.OffShelfManyForReview(TargetIDs) 视频置为 OffShelf(5)
|
||||
// ACG_MEDIA → mediamod.OffShelfManyForReview(TargetIDs) Media 置为 status=0
|
||||
// ACG_CONTENT → mediacontentmod.OffShelfManyForReview(TargetIDs) 章节置为 isActive=false
|
||||
//
|
||||
// 每个类型一次 UpdateMany;遇到未知 TargetType 直接返回错误,整批回滚交给调用方语义处理。
|
||||
// 该函数不修改 issue 自身 resolveStatus;调用方下架成功后再调 BatchOffShelfResolveIssues
|
||||
func ApplyOffShelfBatch(issues []*contentreviewmod.ReviewIssue) error {
|
||||
var videoIDs, mediaIDs, contentIDs []primitive.ObjectID
|
||||
for _, iss := range issues {
|
||||
switch iss.TargetType {
|
||||
case contentreviewmod.IssueTargetVideo:
|
||||
videoIDs = append(videoIDs, iss.TargetID)
|
||||
case contentreviewmod.IssueTargetACGMedia:
|
||||
mediaIDs = append(mediaIDs, iss.TargetID)
|
||||
case contentreviewmod.IssueTargetACGContent:
|
||||
contentIDs = append(contentIDs, iss.TargetID)
|
||||
default:
|
||||
return fmt.Errorf("unsupported targetType: %s (issueId=%s)", iss.TargetType, iss.ID.Hex())
|
||||
}
|
||||
}
|
||||
if err := vidmod.OffShelfManyForReview(videoIDs); err != nil {
|
||||
return fmt.Errorf("offshelf videos fail: %w", err)
|
||||
}
|
||||
if err := mediamod.OffShelfManyForReview(mediaIDs); err != nil {
|
||||
return fmt.Errorf("offshelf media fail: %w", err)
|
||||
}
|
||||
if err := mediacontentmod.OffShelfManyForReview(contentIDs); err != nil {
|
||||
return fmt.Errorf("offshelf media content fail: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package currencyser
|
||||
|
||||
import (
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/currencymod"
|
||||
)
|
||||
|
||||
// Add 新增裸聊资料
|
||||
func Add(in *AddCond) stderr.Code {
|
||||
// 插入数据库
|
||||
n, err := in.Document()
|
||||
if err != nil {
|
||||
return stderr.ErrReqForbidden
|
||||
}
|
||||
if err := currencymod.Add(n); err != nil {
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// Edit 编辑裸聊资料
|
||||
func Edit(in *EditCond) stderr.Code {
|
||||
if err := currencymod.Edit(in.Cond(), in.Update()); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// QueryAll 查询裸聊列表
|
||||
func QueryAll(in *QueryAllCond) (interface{}, stderr.Code) {
|
||||
var data = map[string]interface{}{
|
||||
"data": []interface{}{},
|
||||
"total": 0,
|
||||
}
|
||||
count, err := currencymod.QueryCount(in.Query())
|
||||
if err != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
data["total"] = count
|
||||
list, err := currencymod.QueryAll(in.Query(), in.Options())
|
||||
if err != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
data["data"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package currencyser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/currencymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type AddCond struct {
|
||||
Name string `json:"name"` //货币名称
|
||||
Type commod.CurrencyType `json:"type"` //货币类型
|
||||
Coins int64 `json:"coins"` //购买货币数
|
||||
Price int64 `json:"price"` //价格 单位:元
|
||||
CouponDesc string `json:"couponDesc"` //优惠描述
|
||||
GiveVipDays int `json:"giveVipDays"` //赠送vip天数
|
||||
LouFengUnlockTimes int `json:"louFengUnlockTimes"` //赠送楼凤解锁次数
|
||||
GiveGameCoin int64 `json:"giveGameCoin"` //赠送游戏金币
|
||||
GiveFruitCoin int64 `json:"giveFruitCoin"` //赠送果币
|
||||
GiveGold int64 `json:"giveGold"` //赠送金币
|
||||
FirstGiveVipDays int `json:"firstGiveVipDays"` //首次赠送vip天数
|
||||
FirstGiveGameCoin int64 `json:"firstGiveGameCoin"` //首次赠送游戏金币
|
||||
NotFirst bool `json:"notFirst"` //非首充依然赠送
|
||||
Active bool `json:"active"` //是否激活
|
||||
}
|
||||
|
||||
func (cond *AddCond) Document() (interface{}, error) {
|
||||
data, err := json.Marshal(&cond)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("新增裸聊json序列化异常[%v]", err))
|
||||
return nil, err
|
||||
}
|
||||
var n currencymod.Currency
|
||||
if err = json.Unmarshal(data, &n); err != nil {
|
||||
log.Error(fmt.Sprintf("新增裸聊json反序列化异常[%v]", err))
|
||||
return nil, err
|
||||
}
|
||||
n.CreatedAt = time.Now()
|
||||
n.UpdatedAt = time.Now()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type EditCond struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"` //裸聊ID
|
||||
Name *string `json:"name"` //货币名称
|
||||
Type *commod.CurrencyType `json:"type"` //货币类型
|
||||
Coins *int64 `json:"coins"` //购买货币数
|
||||
Price *int64 `json:"price"` //价格 单位:元
|
||||
CouponDesc *string `json:"couponDesc"` //优惠描述
|
||||
GiveVipDays *int `json:"giveVipDays"` //赠送vip天数
|
||||
LouFengUnlockTimes *int `json:"louFengUnlockTimes"` //赠送楼凤解锁次数
|
||||
GiveGold *int64 `json:"giveGold"` //赠送金币
|
||||
GiveGameCoin *int64 `json:"giveGameCoin"` //赠送游戏金币
|
||||
GiveFruitCoin *int64 `json:"giveFruitCoin"` //赠送果币
|
||||
NotFirst *bool `json:"notFirst"` //非首充依然赠送
|
||||
IsActive *bool `json:"isActive"` //是否激活
|
||||
FirstGiveVipDays *int `json:"firstGiveVipDays" bson:"firstGiveVipDays"` //首次赠送vip天数
|
||||
FirstGiveGameCoin *int64 `json:"firstGiveGameCoin" bson:"firstGiveGameCoin"` //首次赠送游戏金币
|
||||
}
|
||||
|
||||
func (cond *EditCond) Cond() primitive.M {
|
||||
return bson.M{"_id": cond.ID}
|
||||
}
|
||||
|
||||
func (cond *EditCond) Update() primitive.M {
|
||||
var update = bson.M{}
|
||||
if cond.Name != nil {
|
||||
update["name"] = cond.Name
|
||||
}
|
||||
if cond.Type != nil {
|
||||
update["type"] = cond.Type
|
||||
}
|
||||
if cond.Coins != nil {
|
||||
update["coins"] = cond.Coins
|
||||
}
|
||||
if cond.Price != nil {
|
||||
update["price"] = cond.Price
|
||||
}
|
||||
if cond.CouponDesc != nil {
|
||||
update["couponDesc"] = cond.CouponDesc
|
||||
}
|
||||
if cond.GiveVipDays != nil {
|
||||
update["giveVipDays"] = cond.GiveVipDays
|
||||
}
|
||||
if cond.LouFengUnlockTimes != nil {
|
||||
update["louFengUnlockTimes"] = cond.LouFengUnlockTimes
|
||||
}
|
||||
if cond.GiveGold != nil {
|
||||
update["giveGold"] = cond.GiveGold
|
||||
}
|
||||
if cond.GiveGameCoin != nil {
|
||||
update["giveGameCoin"] = cond.GiveGameCoin
|
||||
}
|
||||
if cond.GiveFruitCoin != nil {
|
||||
update["giveFruitCoin"] = cond.GiveFruitCoin
|
||||
}
|
||||
if cond.NotFirst != nil {
|
||||
update["notFirst"] = cond.NotFirst
|
||||
}
|
||||
if cond.IsActive != nil {
|
||||
update["isActive"] = cond.IsActive
|
||||
}
|
||||
if cond.FirstGiveVipDays != nil {
|
||||
update["firstGiveVipDays"] = cond.FirstGiveVipDays
|
||||
}
|
||||
if cond.FirstGiveGameCoin != nil {
|
||||
update["firstGiveGameCoin"] = cond.FirstGiveGameCoin
|
||||
}
|
||||
return bson.M{"$set": update}
|
||||
}
|
||||
|
||||
type QueryAllCond struct {
|
||||
IsActive *bool `form:"isActive"` //是否激活
|
||||
Type commod.CurrencyType `form:"type" binding:"required"` //货币类型
|
||||
Page commod.Page
|
||||
}
|
||||
|
||||
func (cond *QueryAllCond) Query() primitive.M {
|
||||
var query = bson.M{"type": cond.Type}
|
||||
if cond.IsActive != nil {
|
||||
query["isActive"] = cond.IsActive
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (cond *QueryAllCond) Options() *options.FindOptions {
|
||||
return options.Find().SetLimit(int64(cond.Page.Limit())).SetSkip(int64(cond.Page.Skip())).SetSort(bson.D{{Key: "coins", Value: 1}})
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package daichongser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/daichong"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/rchgordmod"
|
||||
"91porn-server/web/webg"
|
||||
)
|
||||
|
||||
const (
|
||||
rate_yinse_coin = 10 //金额转换成金币的单位 1元=10音色币
|
||||
rate_yuan_feng = 100 //将元转换为分
|
||||
retries = 7 //通知7次
|
||||
)
|
||||
|
||||
var interval = []int64{0, 60, 5 * 60, 10 * 60, 30 * 60, 60 * 60, 2 * 60 * 60} //通知时间间隔
|
||||
|
||||
// 上分通知 将订单的处理结果通知给代充平台
|
||||
func OrderCallBack(ctx context.Context, req daichong.OrderCallSign) (commod.Resp, stderr.Code) {
|
||||
sign, err := daichong.Convert2Sign(req, webg.Conf.DaiChong.AppSecret)
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, "create sign data wrong", log.Any("warn", err))
|
||||
return commod.Resp{}, stderr.Failure
|
||||
}
|
||||
orderReq := daichong.CommonReq{
|
||||
AppId: webg.Conf.DaiChong.AppID,
|
||||
Data: sign,
|
||||
}
|
||||
resp, err := daichong.OrderCallBack(ctx, orderReq, webg.Conf.DaiChong.Domain)
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, "GetOrderInfo send http wrong", log.Any("warn", err))
|
||||
return resp, stderr.Failure
|
||||
}
|
||||
return resp, resp.Code
|
||||
}
|
||||
|
||||
// 通知第三方回调的结果 按照时间间隔通知
|
||||
func NotifyThirdOrderCallBack(ctx context.Context, oid string, callBackTime int64, orderStatus int) {
|
||||
var cnt = 0
|
||||
selector := rchgordmod.ReplaceEditSelector{}
|
||||
for cnt < retries {
|
||||
order, _ := rchgordmod.FindRechargeOrderByThirdOrderId(oid)
|
||||
if order.NotifyStatus == rchgordmod.Notify_SUCCESS {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Duration(interval[cnt]) * time.Second)
|
||||
resp, code := OrderCallBack(ctx, daichong.OrderCallSign{
|
||||
OrderId: oid,
|
||||
Time: callBackTime,
|
||||
Status: orderStatus,
|
||||
})
|
||||
if code == stderr.Success {
|
||||
notifySuccess := rchgordmod.Notify_SUCCESS
|
||||
selector.NotifyStatus = ¬ifySuccess
|
||||
_ = rchgordmod.UpdateBaseOnOidAndStatus(nil, oid, order.Status, selector)
|
||||
log.Info("daichong order call back send http request successfully", log.Any("msg", resp.Msg), log.Any("orderstatus", orderStatus),
|
||||
log.Any("orderId", oid),
|
||||
log.Any("time", time.Now().Format(time.RFC3339)))
|
||||
return
|
||||
}
|
||||
notifyFailure := rchgordmod.Notify_FAILURE
|
||||
selector.NotifyStatus = ¬ifyFailure
|
||||
_ = rchgordmod.UpdateBaseOnOidAndStatus(nil, oid, order.Status, selector)
|
||||
log.Warn("daichong order call back send http request failed", log.Any("responseCode", code), log.Any("msg", resp.Msg))
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package deduction
|
||||
|
||||
import (
|
||||
"91porn-server/models/l/payvidlgmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
)
|
||||
|
||||
func StatPublisherDeduction(arg *Query) (*PublisherDeduction, error) {
|
||||
res, err := payvidlgmod.PublisherVideoDeductionRange(*arg.UID, *arg.StartTime, *arg.EndTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publisher, err := usermod.FindUserByUIDForNoCache(*arg.UID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 避免脏数据引起的空指针
|
||||
if publisher == nil {
|
||||
publisher = &usermod.User{UID: *arg.UID}
|
||||
}
|
||||
deduction, deductionCount, totalAmount, totalCount := int64(0), int64(0), int64(0), int64(0)
|
||||
for _, stat := range res {
|
||||
if stat.IsVideoDeduction {
|
||||
deduction = stat.TotalAmount
|
||||
deductionCount = stat.VideoCount
|
||||
}
|
||||
totalAmount += stat.TotalAmount
|
||||
totalCount += stat.VideoCount
|
||||
}
|
||||
ret := PublisherDeduction{
|
||||
PublisherInfo: PublisherInfo{
|
||||
UID: publisher.UID,
|
||||
Name: publisher.Name,
|
||||
VideoDeduction: publisher.VideoDeduction,
|
||||
VideoDeducitonCount: publisher.VideoDeductionCount,
|
||||
VideoDeductionPayCount: publisher.VideoDeductionPayCount,
|
||||
},
|
||||
AfterDeduction: totalAmount - deduction,
|
||||
BeforeDeduction: totalAmount,
|
||||
DeductionCount: deductionCount,
|
||||
NonDeductionCount: totalCount - deductionCount,
|
||||
}
|
||||
return &ret, err
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package deduction
|
||||
|
||||
import "time"
|
||||
|
||||
type Query struct {
|
||||
StartTime *time.Time `form:"startTime" json:"startTime,omitempty" binding:"required"` //开始时间
|
||||
EndTime *time.Time `form:"endTime" json:"endTime,omitempty" binding:"required"` //结束时间
|
||||
UID *uint64 `form:"uid" json:"uid" binding:"required"` //uid
|
||||
}
|
||||
|
||||
// 视频博主信息
|
||||
type PublisherInfo struct {
|
||||
UID uint64 `json:"uid"` // 视频博主id
|
||||
Name string `json:"name"` // 博主姓名
|
||||
VideoDeduction float64 `json:"videoDeduction"` // 视频扣量
|
||||
VideoDeducitonCount int `json:"videoDeductionCount"` // 已扣量视频数
|
||||
VideoDeductionPayCount int `json:"videoDeductionPayCount"` // 视频售卖总次数
|
||||
}
|
||||
|
||||
// 视频扣量统计
|
||||
type PublisherDeduction struct {
|
||||
PublisherInfo `json:",inline"`
|
||||
BeforeDeduction int64 `json:"beforeDeduction"` // 扣量前总售卖(金币)
|
||||
AfterDeduction int64 `json:"afterDeduction"` // 扣量后总售卖(金币)
|
||||
DeductionCount int64 `json:"deductionCount"` // 被扣量视频总数
|
||||
NonDeductionCount int64 `json:"nonDedcutionCount"` // 未扣量视频总数
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package discount_area_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/cachev2"
|
||||
"91porn-server/common/constant/redisconst"
|
||||
sli "91porn-server/common/slice"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/discount_area_mod"
|
||||
"91porn-server/models/v/discount_area_video_mod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
"github.com/shopspring/decimal"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"time"
|
||||
)
|
||||
|
||||
func AddDiscountArea(req *discount_area_mod.WebDiscountAreaAddReq) (err error) {
|
||||
now := time.Now()
|
||||
model := &discount_area_mod.DiscountArea{
|
||||
Title: req.Title,
|
||||
Desc: req.Desc,
|
||||
Discount: int(req.Discount),
|
||||
Status: int(req.Status),
|
||||
SortCode: int(req.SortCode),
|
||||
RecommendNum: int(req.RecommendNum),
|
||||
UpdatedAt: now,
|
||||
CreatedAt: now,
|
||||
}
|
||||
//最多10个
|
||||
if model.RecommendNum > 10 {
|
||||
model.RecommendNum = 10
|
||||
}
|
||||
err = discount_area_mod.AddDiscountArea(model)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 删除缓存
|
||||
cachev2.Classes().AutoListKey(redisconst.DiscountArea).AutoClear(true, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func UpdateDiscountArea(req *discount_area_mod.WebDiscountAreaUpdateReq) (err error) {
|
||||
var maxRecomment uint = 10
|
||||
if req.RecommendNum != nil && *req.RecommendNum > 10 {
|
||||
req.RecommendNum = &maxRecomment
|
||||
}
|
||||
objId, err := primitive.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
update, err := common.ToBsonM(req.WebDiscountAreaUpdateData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
update["updateAt"] = time.Now()
|
||||
err = discount_area_mod.UpdateDiscountArea(objId, update)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 删除缓存
|
||||
cachev2.Classes().AutoListKey(redisconst.DiscountArea).AutoClear(true, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteDiscountArea 删除折扣专区
|
||||
func DeleteDiscountArea(id string) (err error) {
|
||||
objId, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
update := bson.M{
|
||||
"status": 2,
|
||||
"updateAt": time.Now(),
|
||||
}
|
||||
err = discount_area_mod.UpdateDiscountArea(objId, update)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 删除缓存
|
||||
cachev2.Classes().AutoListKey(redisconst.DiscountArea).AutoClear(true, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// GetDiscountAreaVids 获取专区下的视频
|
||||
func GetDiscountAreaVids(req *discount_area_mod.WebDiscountAreaVidsReq) (list []discount_area_mod.WebDiscountAreaVideo, total int64, err error) {
|
||||
filter := bson.M{}
|
||||
if req.DiscountAreaId != "" {
|
||||
discountAreaId, _ := primitive.ObjectIDFromHex(req.DiscountAreaId)
|
||||
filter["discountAreaId"] = discountAreaId
|
||||
}
|
||||
if req.Vid != "" {
|
||||
videoID, _ := primitive.ObjectIDFromHex(req.Vid)
|
||||
filter["videoID"] = videoID
|
||||
}
|
||||
opts := options.Find().SetSort(bson.D{{Key: "createdAt", Value: -1}}).SetSkip(int64(req.Skip())).SetLimit(int64(req.Limit()))
|
||||
discountAreaVideoList, total, err := discount_area_video_mod.GetListByCond(filter, opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
vids := []primitive.ObjectID{}
|
||||
discountAreaIds := []primitive.ObjectID{}
|
||||
// 获取视频信息
|
||||
for _, v := range discountAreaVideoList {
|
||||
vids = append(vids, v.VideoID)
|
||||
discountAreaIds = append(discountAreaIds, v.DiscountAreaId)
|
||||
}
|
||||
|
||||
videoList, err := vidmod.GetVideoListByIDsNoStatus(sli.RemoveRepObjectID(vids))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
videoMap := make(map[primitive.ObjectID]vidmod.VideoModel)
|
||||
for _, video := range videoList {
|
||||
videoMap[video.ID] = *video
|
||||
}
|
||||
discountAreaList, err := discount_area_mod.GetNoStatusDiscountAreaByIds(sli.RemoveRepObjectID(discountAreaIds))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
discountAreaMap := make(map[primitive.ObjectID]discount_area_mod.DiscountArea)
|
||||
for _, discountArea := range discountAreaList {
|
||||
discountAreaMap[discountArea.ID] = discountArea
|
||||
}
|
||||
|
||||
for _, v := range discountAreaVideoList {
|
||||
discountAreaVideo := discount_area_mod.WebDiscountAreaVideo{
|
||||
Id: v.ID,
|
||||
VideoID: v.VideoID,
|
||||
DiscountAreaId: v.DiscountAreaId,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
if video, ok := videoMap[v.VideoID]; ok {
|
||||
discountAreaVideo.Title = video.Title
|
||||
discountAreaVideo.Cover = video.Cover
|
||||
discountAreaVideo.PublisherID = video.PublisherID
|
||||
discountAreaVideo.OriginalPrice = video.Coins
|
||||
}
|
||||
if discountArea, ok := discountAreaMap[v.DiscountAreaId]; ok {
|
||||
discountAreaVideo.DiscountAreaTitle = discountArea.Title
|
||||
discountAreaVideo.DiscountedPrice = decimal.NewFromInt(int64(discountArea.Discount)).Shift(-2).
|
||||
Mul(decimal.NewFromInt(discountAreaVideo.OriginalPrice)).Round(0).IntPart()
|
||||
}
|
||||
list = append(list, discountAreaVideo)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DiscountAreaAddVid 折扣区新增视频
|
||||
func DiscountAreaAddVid(req *discount_area_mod.WebDiscountAreaAddVidReq) (code stderr.Code) {
|
||||
if req.DiscountAreaId.IsZero() || len(req.VIds) == 0 {
|
||||
return stderr.ErrParamError
|
||||
}
|
||||
// 判断是否是视频
|
||||
videoList, err := vidmod.GetVideoListByIDs(req.VIds)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
vids := []primitive.ObjectID{}
|
||||
for _, v := range videoList {
|
||||
// 不是视频,直接返回错误
|
||||
if v.NewsType != vidmod.SP {
|
||||
return stderr.NotAVideo
|
||||
}
|
||||
vids = append(vids, v.ID)
|
||||
}
|
||||
if len(vids) == 0 {
|
||||
return
|
||||
}
|
||||
// 查询是否已经在其他折扣区,是的话则直接报错
|
||||
list, err := discount_area_video_mod.GetDiscountAreaVideoByVids(vids)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if len(list) > 0 {
|
||||
// 不能一个视频同时在两个折扣区
|
||||
return stderr.VideoInDiscountArea
|
||||
}
|
||||
now := time.Now()
|
||||
batchAddVids := []discount_area_video_mod.DiscountAreaVideo{}
|
||||
for _, vid := range vids {
|
||||
batchAddVids = append(batchAddVids, discount_area_video_mod.DiscountAreaVideo{
|
||||
DiscountAreaId: req.DiscountAreaId,
|
||||
VideoID: vid,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
}
|
||||
// 写入折扣区
|
||||
err = discount_area_video_mod.BatchAdd(batchAddVids)
|
||||
if err != nil {
|
||||
return stderr.ErrDbInsertManyError
|
||||
}
|
||||
update := bson.M{}
|
||||
update["discountAreaId"] = req.DiscountAreaId
|
||||
update["updatedAt"] = time.Now()
|
||||
_, err = vidmod.UpdateManyVideo(vids, update)
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
// DiscountAreaDeleteVid 移除折扣区视频
|
||||
func DiscountAreaDeleteVid(ids []primitive.ObjectID) (code stderr.Code) {
|
||||
if len(ids) == 0 {
|
||||
return stderr.ErrParamError
|
||||
}
|
||||
|
||||
list, err := discount_area_video_mod.GetDiscountAreaVideoByIds(ids)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
vids := []primitive.ObjectID{}
|
||||
for _, v := range list {
|
||||
vids = append(vids, v.VideoID)
|
||||
}
|
||||
// 删除视频
|
||||
err = discount_area_video_mod.DeleteDiscountAreaVideoByIds(ids)
|
||||
if err != nil {
|
||||
return stderr.ErrDbDeleteError
|
||||
}
|
||||
// 移除
|
||||
update := bson.M{}
|
||||
update["discountAreaId"] = nil
|
||||
update["updatedAt"] = time.Now()
|
||||
_, err = vidmod.UpdateManyVideo(vids, update)
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
package dramatopicser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/dramatopic"
|
||||
"91porn-server/models/cache/sysconfdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/modulesectionmod"
|
||||
"91porn-server/models/v/sysconfmod"
|
||||
"91porn-server/web/webg"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ListRequest struct {
|
||||
Name string `form:"name" json:"name"`
|
||||
TopicType string `form:"topicType" json:"topicType" binding:"omitempty,oneof=SYSTEM CUSTOM"`
|
||||
Status *int `form:"status" json:"status" binding:"omitempty,oneof=0 1"`
|
||||
commod.Page
|
||||
}
|
||||
|
||||
type Topic struct {
|
||||
TopicID string `json:"topicId"`
|
||||
Name string `json:"name"`
|
||||
TopicType string `json:"topicType"`
|
||||
SystemKey string `json:"systemKey"`
|
||||
Status int `json:"status"`
|
||||
Sort int `json:"sort"`
|
||||
WorkCount int64 `json:"workCount"`
|
||||
Editable bool `json:"editable"`
|
||||
WorksEditable bool `json:"worksEditable"`
|
||||
CreatedAt *time.Time `json:"createdAt"`
|
||||
UpdatedAt *time.Time `json:"updatedAt"`
|
||||
systemOrder int
|
||||
}
|
||||
|
||||
type ListResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []Topic `json:"list"`
|
||||
}
|
||||
|
||||
func (req ListRequest) List(now time.Time) (ListResponse, error) {
|
||||
moduleID, ok, err := dramatopic.ModuleID(now)
|
||||
if err != nil {
|
||||
return ListResponse{}, err
|
||||
}
|
||||
sections := make([]modulesectionmod.Section, 0)
|
||||
if ok {
|
||||
sections, err = modulesectionmod.ListBySubModule(moduleID, 1000)
|
||||
if err != nil {
|
||||
return ListResponse{}, err
|
||||
}
|
||||
}
|
||||
sectionIDs := make([]primitive.ObjectID, 0, len(sections))
|
||||
for _, section := range sections {
|
||||
sectionIDs = append(sectionIDs, section.ID)
|
||||
}
|
||||
counts, err := mediamod.CountActiveDramaBySectionIDs(sectionIDs)
|
||||
if err != nil {
|
||||
return ListResponse{}, err
|
||||
}
|
||||
activeTotal, err := mediamod.QueryAllCount(bson.M{"mediaType": mediamod.MediaTypeDrama, "status": 1, "isDelete": false})
|
||||
if err != nil {
|
||||
return ListResponse{}, err
|
||||
}
|
||||
all := make([]Topic, 0, 3+len(sections))
|
||||
if req.TopicType == "" || req.TopicType == dramatopic.TypeSystem {
|
||||
for _, system := range dramatopic.SystemTopics() {
|
||||
if !matches(req, system.Name, 1) {
|
||||
continue
|
||||
}
|
||||
all = append(all, Topic{
|
||||
TopicID: system.ID, Name: system.Name, TopicType: dramatopic.TypeSystem,
|
||||
SystemKey: system.SystemKey, Status: 1, Sort: system.Sort, WorkCount: activeTotal,
|
||||
Editable: false, WorksEditable: false, systemOrder: system.TieOrder,
|
||||
})
|
||||
}
|
||||
}
|
||||
if req.TopicType == "" || req.TopicType == dramatopic.TypeCustom {
|
||||
for _, section := range sections {
|
||||
status := int(valueOrZeroUint8(section.Status))
|
||||
if !matches(req, section.SectionName, status) {
|
||||
continue
|
||||
}
|
||||
createdAt := section.CreatedAt
|
||||
updatedAt := section.UpdatedAt
|
||||
all = append(all, Topic{
|
||||
TopicID: section.ID.Hex(), Name: section.SectionName, TopicType: dramatopic.TypeCustom,
|
||||
Status: status, Sort: valueOrZeroInt(section.Sort), WorkCount: counts[section.ID],
|
||||
Editable: true, WorksEditable: true, CreatedAt: &createdAt, UpdatedAt: &updatedAt,
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.SliceStable(all, func(i, j int) bool {
|
||||
if all[i].Sort != all[j].Sort {
|
||||
return all[i].Sort > all[j].Sort
|
||||
}
|
||||
if all[i].systemOrder != all[j].systemOrder {
|
||||
return all[i].systemOrder > all[j].systemOrder
|
||||
}
|
||||
if all[i].UpdatedAt == nil {
|
||||
return false
|
||||
}
|
||||
if all[j].UpdatedAt == nil {
|
||||
return true
|
||||
}
|
||||
return all[i].UpdatedAt.After(*all[j].UpdatedAt)
|
||||
})
|
||||
response := ListResponse{Total: int64(len(all)), List: []Topic{}}
|
||||
start := int(req.Skip64())
|
||||
if start >= len(all) {
|
||||
return response, nil
|
||||
}
|
||||
end := start + int(req.PageSize)
|
||||
if end > len(all) {
|
||||
end = len(all)
|
||||
}
|
||||
response.HasNext = end < len(all)
|
||||
response.List = all[start:end]
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func matches(req ListRequest, name string, status int) bool {
|
||||
if req.Status != nil && *req.Status != status {
|
||||
return false
|
||||
}
|
||||
return req.Name == "" || strings.Contains(strings.ToLower(name), strings.ToLower(strings.TrimSpace(req.Name)))
|
||||
}
|
||||
|
||||
type CreateRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Status *int `json:"status" binding:"omitempty,oneof=0 1"`
|
||||
Sort *int `json:"sort" binding:"omitempty,min=0"`
|
||||
}
|
||||
|
||||
func (req CreateRequest) Create(now time.Time) (primitive.ObjectID, error) {
|
||||
name, err := validateName(req.Name)
|
||||
if err != nil {
|
||||
return primitive.NilObjectID, err
|
||||
}
|
||||
moduleID, ok, err := dramatopic.ModuleID(now)
|
||||
if err != nil || !ok {
|
||||
if err == nil {
|
||||
err = errors.New("短剧专题模块未配置")
|
||||
}
|
||||
return primitive.NilObjectID, err
|
||||
}
|
||||
status := uint8(1)
|
||||
if req.Status != nil {
|
||||
status = uint8(*req.Status)
|
||||
}
|
||||
topicSort := 0
|
||||
if req.Sort != nil {
|
||||
topicSort = *req.Sort
|
||||
}
|
||||
id := primitive.NewObjectID()
|
||||
err = modulesectionmod.InsertOne(modulesectionmod.Section{
|
||||
ID: id, SectionName: name, SubModuleID: moduleID, Status: &status, Sort: &topicSort,
|
||||
})
|
||||
return id, err
|
||||
}
|
||||
|
||||
type UpdateRequest struct {
|
||||
TopicID string `json:"topicId" binding:"required"`
|
||||
Name *string `json:"name"`
|
||||
Status *int `json:"status" binding:"omitempty,oneof=0 1"`
|
||||
Sort *int `json:"sort" binding:"omitempty,min=0"`
|
||||
}
|
||||
|
||||
func (req UpdateRequest) Update(now time.Time) error {
|
||||
topicID, moduleID, _, err := customTopic(req.TopicID, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields := bson.M{}
|
||||
if req.Name != nil {
|
||||
name, nameErr := validateName(*req.Name)
|
||||
if nameErr != nil {
|
||||
return nameErr
|
||||
}
|
||||
fields["sectionName"] = name
|
||||
}
|
||||
if req.Status != nil {
|
||||
fields["status"] = uint8(*req.Status)
|
||||
}
|
||||
if req.Sort != nil {
|
||||
fields["sort"] = *req.Sort
|
||||
}
|
||||
if len(fields) == 0 {
|
||||
return errors.New("至少需要修改一个字段")
|
||||
}
|
||||
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
matched, updateErr := modulesectionmod.UpdateTopicFields(t, topicID, moduleID, fields)
|
||||
if updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
if matched == 0 {
|
||||
return errors.New("专题不存在")
|
||||
}
|
||||
if name, renamed := fields["sectionName"]; renamed {
|
||||
if _, updateErr = mediamod.UpdateMany(t, bson.M{"sId": topicID}, bson.M{"$set": bson.M{"sectionName": name}}); updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type DeleteRequest struct {
|
||||
TopicID string `json:"topicId" binding:"required"`
|
||||
}
|
||||
|
||||
func (req DeleteRequest) Delete(now time.Time) error {
|
||||
topicID, moduleID, _, err := customTopic(req.TopicID, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
if _, updateErr := mediamod.UpdateMany(t, bson.M{"sId": topicID}, bson.M{"$set": bson.M{
|
||||
"sId": primitive.NilObjectID, "sectionName": "", "sectionSort": 0,
|
||||
}}); updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
deleted, deleteErr := modulesectionmod.DeleteTopic(t, topicID, moduleID)
|
||||
if deleteErr != nil {
|
||||
return deleteErr
|
||||
}
|
||||
if deleted != 1 {
|
||||
return errors.New("自定义专题不存在")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type SortRequest struct {
|
||||
Items []SortRequestItem `json:"items" binding:"required,min=1,max=1000,dive"`
|
||||
}
|
||||
|
||||
type SortRequestItem struct {
|
||||
TopicID string `json:"topicId" binding:"required"`
|
||||
Sort int `json:"sort" binding:"min=0"`
|
||||
}
|
||||
|
||||
func (req SortRequest) Update(now time.Time) error {
|
||||
moduleID, moduleOK, err := dramatopic.ModuleID(now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
type target struct {
|
||||
item SortRequestItem
|
||||
system *dramatopic.SystemTopic
|
||||
custom primitive.ObjectID
|
||||
}
|
||||
targets := make([]target, 0, len(req.Items))
|
||||
seen := make(map[string]struct{}, len(req.Items))
|
||||
systemTopics := make(map[string]dramatopic.SystemTopic, 3)
|
||||
for _, system := range dramatopic.SystemTopics() {
|
||||
systemTopics[system.ID] = system
|
||||
}
|
||||
for _, item := range req.Items {
|
||||
if _, exists := seen[item.TopicID]; exists {
|
||||
return errors.New("topicId不能重复")
|
||||
}
|
||||
seen[item.TopicID] = struct{}{}
|
||||
if system, found := systemTopics[item.TopicID]; found {
|
||||
topic := system
|
||||
targets = append(targets, target{item: item, system: &topic})
|
||||
continue
|
||||
}
|
||||
if !moduleOK {
|
||||
return errors.New("短剧专题模块未配置")
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(item.TopicID)
|
||||
if parseErr != nil {
|
||||
return errors.New("无效的专题ID")
|
||||
}
|
||||
section, findErr := modulesectionmod.GetBySectionByID(id)
|
||||
if findErr != nil || section.SubModuleID != moduleID || section.DeletedAt != nil {
|
||||
return errors.New("自定义专题不存在")
|
||||
}
|
||||
targets = append(targets, target{item: item, custom: id})
|
||||
}
|
||||
sysconfmod.EnsureInitData()
|
||||
for _, target := range targets {
|
||||
if target.system != nil {
|
||||
config, configErr := sysconfmod.GetByVCode(target.system.VCode)
|
||||
if configErr != nil {
|
||||
return configErr
|
||||
}
|
||||
if _, configErr = sysconfdata.UpdateData(nil, config.ID.Hex(), map[string]interface{}{
|
||||
"value": strconv.Itoa(target.item.Sort), "updatedAt": time.Now(),
|
||||
}); configErr != nil {
|
||||
return configErr
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, updateErr := modulesectionmod.UpdateTopicFields(nil, target.custom, moduleID, bson.M{"sort": target.item.Sort}); updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateWorksRequest struct {
|
||||
TopicID string `json:"topicId" binding:"required"`
|
||||
WorkIDs []string `json:"workIds" binding:"max=500,dive,required"`
|
||||
}
|
||||
|
||||
type UpdateWorksResponse struct {
|
||||
TopicID string `json:"topicId"`
|
||||
WorkCount int `json:"workCount"`
|
||||
}
|
||||
|
||||
type WorkConflict struct {
|
||||
MediaID string `json:"mediaId"`
|
||||
TopicID string `json:"topicId"`
|
||||
TopicName string `json:"topicName"`
|
||||
}
|
||||
|
||||
type WorksConflictError struct {
|
||||
Message string `json:"message"`
|
||||
Conflicts []WorkConflict `json:"conflicts"`
|
||||
}
|
||||
|
||||
func (e *WorksConflictError) Error() string { return e.Message }
|
||||
|
||||
type topicWorkAssignment struct {
|
||||
ID primitive.ObjectID
|
||||
Sort int
|
||||
}
|
||||
|
||||
func appendTopicWorkAssignments(
|
||||
topicID primitive.ObjectID,
|
||||
ids []primitive.ObjectID,
|
||||
mediaMap map[primitive.ObjectID]mediamod.Media,
|
||||
currentCount int64,
|
||||
) ([]topicWorkAssignment, int) {
|
||||
newIDs := make([]primitive.ObjectID, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
if mediaMap[id].SID != topicID {
|
||||
newIDs = append(newIDs, id)
|
||||
}
|
||||
}
|
||||
total := int(currentCount) + len(newIDs)
|
||||
assignments := make([]topicWorkAssignment, 0, len(newIDs))
|
||||
for i, id := range newIDs {
|
||||
assignments = append(assignments, topicWorkAssignment{ID: id, Sort: total - i})
|
||||
}
|
||||
return assignments, total
|
||||
}
|
||||
|
||||
func (req UpdateWorksRequest) Update(now time.Time) (UpdateWorksResponse, error) {
|
||||
topicID, _, section, err := customTopic(req.TopicID, now)
|
||||
if err != nil {
|
||||
return UpdateWorksResponse{}, err
|
||||
}
|
||||
ids := make([]primitive.ObjectID, 0, len(req.WorkIDs))
|
||||
seen := make(map[primitive.ObjectID]struct{}, len(req.WorkIDs))
|
||||
for _, raw := range req.WorkIDs {
|
||||
id, parseErr := primitive.ObjectIDFromHex(raw)
|
||||
if parseErr != nil {
|
||||
return UpdateWorksResponse{}, errors.New("workIds包含无效ID")
|
||||
}
|
||||
if _, exists := seen[id]; exists {
|
||||
return UpdateWorksResponse{}, errors.New("workIds不能重复")
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
medias, mediaMap, err := mediamod.GetListByIds(ids)
|
||||
if err != nil {
|
||||
return UpdateWorksResponse{}, err
|
||||
}
|
||||
if len(medias) != len(ids) {
|
||||
return UpdateWorksResponse{}, errors.New("部分短剧不存在")
|
||||
}
|
||||
conflictTopicIDs := make([]primitive.ObjectID, 0)
|
||||
for _, id := range ids {
|
||||
media := mediaMap[id]
|
||||
if media.MediaType != mediamod.MediaTypeDrama || media.IsDelete {
|
||||
return UpdateWorksResponse{}, fmt.Errorf("作品%s不是有效短剧", id.Hex())
|
||||
}
|
||||
if !media.SID.IsZero() && media.SID != topicID {
|
||||
conflictTopicIDs = append(conflictTopicIDs, media.SID)
|
||||
}
|
||||
}
|
||||
if len(conflictTopicIDs) > 0 {
|
||||
sections, _ := modulesectionmod.GetBySectionByIDs(conflictTopicIDs)
|
||||
names := make(map[primitive.ObjectID]string, len(sections))
|
||||
for _, item := range sections {
|
||||
names[item.ID] = item.SectionName
|
||||
}
|
||||
conflict := &WorksConflictError{Message: "部分短剧已配置到其他专题", Conflicts: []WorkConflict{}}
|
||||
for _, id := range ids {
|
||||
media := mediaMap[id]
|
||||
if !media.SID.IsZero() && media.SID != topicID {
|
||||
conflict.Conflicts = append(conflict.Conflicts, WorkConflict{MediaID: id.Hex(), TopicID: media.SID.Hex(), TopicName: names[media.SID]})
|
||||
}
|
||||
}
|
||||
return UpdateWorksResponse{}, conflict
|
||||
}
|
||||
currentCount, err := mediamod.QueryAllCount(bson.M{
|
||||
"sId": topicID, "mediaType": mediamod.MediaTypeDrama, "isDelete": false,
|
||||
})
|
||||
if err != nil {
|
||||
return UpdateWorksResponse{}, err
|
||||
}
|
||||
// 作品配置是增量追加;已在当前专题内的作品保持原顺序,避免单个追加覆盖整批历史配置。
|
||||
assignments, total := appendTopicWorkAssignments(topicID, ids, mediaMap, currentCount)
|
||||
if len(assignments) == 0 {
|
||||
return UpdateWorksResponse{TopicID: req.TopicID, WorkCount: total}, nil
|
||||
}
|
||||
err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
for _, assignment := range assignments {
|
||||
matched, updateErr := mediamod.UpdateMany(t, bson.M{
|
||||
"_id": assignment.ID, "mediaType": mediamod.MediaTypeDrama, "isDelete": false,
|
||||
"$or": []bson.M{{"sId": primitive.NilObjectID}, {"sId": bson.M{"$exists": false}}},
|
||||
}, bson.M{"$set": bson.M{
|
||||
"sId": topicID, "sectionName": section.SectionName, "sectionSort": assignment.Sort,
|
||||
}})
|
||||
if updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
if matched != 1 {
|
||||
return errors.New("专题作品并发冲突,请刷新后重试")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return UpdateWorksResponse{}, err
|
||||
}
|
||||
return UpdateWorksResponse{TopicID: req.TopicID, WorkCount: total}, nil
|
||||
}
|
||||
|
||||
func customTopic(rawID string, now time.Time) (primitive.ObjectID, primitive.ObjectID, modulesectionmod.Section, error) {
|
||||
if _, system := dramatopic.FindSystem(rawID); system {
|
||||
return primitive.NilObjectID, primitive.NilObjectID, modulesectionmod.Section{}, errors.New("系统专题不允许此操作")
|
||||
}
|
||||
id, err := primitive.ObjectIDFromHex(rawID)
|
||||
if err != nil {
|
||||
return primitive.NilObjectID, primitive.NilObjectID, modulesectionmod.Section{}, errors.New("无效的专题ID")
|
||||
}
|
||||
moduleID, ok, err := dramatopic.ModuleID(now)
|
||||
if err != nil || !ok {
|
||||
if err == nil {
|
||||
err = errors.New("短剧专题模块未配置")
|
||||
}
|
||||
return primitive.NilObjectID, primitive.NilObjectID, modulesectionmod.Section{}, err
|
||||
}
|
||||
section, err := modulesectionmod.GetBySectionByID(id)
|
||||
if err != nil || section.SubModuleID != moduleID || section.DeletedAt != nil {
|
||||
if err == nil {
|
||||
err = errors.New("自定义专题不存在")
|
||||
}
|
||||
return primitive.NilObjectID, primitive.NilObjectID, modulesectionmod.Section{}, err
|
||||
}
|
||||
return id, moduleID, section, nil
|
||||
}
|
||||
|
||||
func validateName(raw string) (string, error) {
|
||||
name := strings.TrimSpace(raw)
|
||||
if count := utf8.RuneCountInString(name); count < 1 || count > 20 {
|
||||
return "", errors.New("专题名称长度必须为1到20个字符")
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func valueOrZeroInt(value *int) int {
|
||||
if value == nil {
|
||||
return 0
|
||||
}
|
||||
return *value
|
||||
}
|
||||
|
||||
func valueOrZeroUint8(value *uint8) uint8 {
|
||||
if value == nil {
|
||||
return 0
|
||||
}
|
||||
return *value
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
package exchcodeser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/adminmod"
|
||||
"91porn-server/models/v/exchcodemod"
|
||||
)
|
||||
|
||||
const SuperAdmin = "superAdmin"
|
||||
|
||||
// 获取兑换码列表
|
||||
func GetExchangeCodeList(param exchcodemod.ListReqParam, manager string) (code stderr.Code, data interface{}, err error) {
|
||||
var filterParams = exchcodemod.FilterDoc{
|
||||
BatchNum: param.BatchNum,
|
||||
Channel: param.Channel,
|
||||
Code: param.Code,
|
||||
App: param.App,
|
||||
Authority: param.Authority,
|
||||
}
|
||||
// 获取管理员角色
|
||||
admin, err := adminmod.FindOneByName(manager)
|
||||
if err != nil || admin.ID.IsZero() {
|
||||
code = stderr.AdminIDErr
|
||||
return
|
||||
}
|
||||
var isSuperAdmin bool
|
||||
if admin.Role == SuperAdmin {
|
||||
isSuperAdmin = true
|
||||
}
|
||||
total, cList, err := exchcodemod.GetCodeList(filterParams, param.Status, param.Page, isSuperAdmin)
|
||||
if err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
list := make([]exchcodemod.RespList, len(cList))
|
||||
for i, v := range cList {
|
||||
tmp := exchcodemod.RespList(v)
|
||||
if v.InvalidAt.Before(time.Now()) {
|
||||
tmp.Status = exchcodemod.StatusExpired
|
||||
}
|
||||
list[i] = tmp
|
||||
}
|
||||
if len(list) == 0 {
|
||||
data = commod.ListResp{Total: 0, List: []exchcodemod.RespList{}}
|
||||
} else {
|
||||
data = commod.ListResp{Total: total, List: list}
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// ExchangeCodes 获取兑换码列表,导出
|
||||
func ExchangeCodes(param exchcodemod.ListReqParam) []exchcodemod.RespList {
|
||||
var filterParams = exchcodemod.FilterDoc{
|
||||
BatchNum: param.BatchNum,
|
||||
Channel: param.Channel,
|
||||
Code: param.Code,
|
||||
App: param.App,
|
||||
Authority: param.Authority,
|
||||
}
|
||||
var isSuperAdmin bool
|
||||
if param.Role != nil && *param.Role == SuperAdmin {
|
||||
isSuperAdmin = true
|
||||
}
|
||||
_, cList, err := exchcodemod.GetCodeList(filterParams, param.Status, param.Page, isSuperAdmin)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
list := make([]exchcodemod.RespList, len(cList))
|
||||
for i, v := range cList {
|
||||
var tmp = exchcodemod.RespList(v)
|
||||
if v.InvalidAt.Before(time.Now()) {
|
||||
tmp.Status = exchcodemod.StatusExpired
|
||||
}
|
||||
list[i] = tmp
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// 增加兑换码
|
||||
func AddExchangeCode(param exchcodemod.AddReqParam, manager string) (code stderr.Code, err error) {
|
||||
// 检查app类型
|
||||
switch param.App {
|
||||
case exchcodemod.AppTypeYSVideo:
|
||||
case exchcodemod.AppTypePFVideo:
|
||||
case exchcodemod.AppTypeLTVpn:
|
||||
default:
|
||||
code = stderr.ExchangeCodeInvalidApp
|
||||
return code, err
|
||||
}
|
||||
// 检查权限
|
||||
switch param.Authority {
|
||||
case exchcodemod.AuthorityShortVideoVip:
|
||||
case exchcodemod.AuthorityFilmVip:
|
||||
case exchcodemod.AuthoritySuper:
|
||||
case exchcodemod.AuthorityVideoCoupon:
|
||||
case exchcodemod.Authority3dPermanentVIP:
|
||||
param.Channels = []string{"XUA01"}
|
||||
case exchcodemod.AuthorityGold:
|
||||
// 获取管理员角色
|
||||
admin, err := adminmod.FindOneByName(manager)
|
||||
if err != nil || admin.ID.IsZero() {
|
||||
code = stderr.AdminIDErr
|
||||
return code, err
|
||||
}
|
||||
if admin.Role != SuperAdmin {
|
||||
code = stderr.ExchangeCodeOperateDenied
|
||||
return code, err
|
||||
}
|
||||
default:
|
||||
code = stderr.ExchangeCodeInvalidAuthority
|
||||
return code, err
|
||||
}
|
||||
// 检查渠道
|
||||
if param.Channels == nil || len(param.Channels) == 0 {
|
||||
param.Channels = []string{"system"}
|
||||
}
|
||||
// 获取批次号
|
||||
eMod, err := exchcodemod.GetLatestBatchNumber(param.App)
|
||||
if err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
var batchNum = produceBatchNumber(param.App, eMod.BatchNum)
|
||||
if batchNum == "" {
|
||||
code = stderr.ExchangeCodeInvalidBatchNumber
|
||||
return
|
||||
}
|
||||
var docs []exchcodemod.ExchCode
|
||||
for i := 0; i < len(param.Channels); i++ {
|
||||
for i2 := 0; int64(i2) < param.Count; i2++ {
|
||||
var exchangeCode = common.InvitePromotionCodeGenera()
|
||||
if exchangeCode == "" {
|
||||
code = stderr.ExchangeCodeProduceCodeFailed
|
||||
return
|
||||
}
|
||||
var now = time.Now()
|
||||
var doc = exchcodemod.ExchCode{
|
||||
BatchNum: batchNum,
|
||||
App: param.App,
|
||||
Channel: param.Channels[i],
|
||||
Code: exchangeCode,
|
||||
Authority: param.Authority,
|
||||
Status: exchcodemod.StatusUnused,
|
||||
Reward: param.Reward,
|
||||
RewardCount: param.RewardCount,
|
||||
UsableNum: param.UsableNum,
|
||||
Operator: manager,
|
||||
Remark: param.Remark,
|
||||
EffectiveAt: param.EffectiveAt,
|
||||
InvalidAt: param.InvalidAt,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
docs = append(docs, doc)
|
||||
}
|
||||
}
|
||||
if err = exchcodemod.BulkWrite(docs); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 生成兑换码
|
||||
func ProduceExchangeCode(n int) string {
|
||||
if n >= 5 {
|
||||
return ""
|
||||
}
|
||||
var length = 12
|
||||
c := common.RandBase32Upper(length)
|
||||
if mod, err := exchcodemod.GetExchangeCodeByCode(c); err != nil || mod != nil {
|
||||
c = ProduceExchangeCode(n + 1)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// 根据兑换类型和批次号删除兑换码你
|
||||
func UpdateExchangeCode(param exchcodemod.UpdateReqParam, manager string) (code stderr.Code, err error) {
|
||||
// 获取管理员角色
|
||||
admin, err := adminmod.FindOneByName(manager)
|
||||
if err != nil || admin.ID.IsZero() {
|
||||
code = stderr.AdminIDErr
|
||||
return
|
||||
}
|
||||
if param.Authority != nil && *param.Authority == exchcodemod.AuthorityGold && admin.Role != SuperAdmin {
|
||||
code = stderr.ExchangeCodeOperateDenied
|
||||
return
|
||||
}
|
||||
// 获取兑换码状态
|
||||
cMode, err := exchcodemod.GetExchangeCodeByID(param.ID)
|
||||
if err != nil || cMode == nil {
|
||||
code = stderr.ExchangeCodeInvalidCode
|
||||
return
|
||||
}
|
||||
if cMode.InvalidAt.Before(time.Now()) || cMode.Status == exchcodemod.StatusUsed {
|
||||
code = stderr.ExchangeCodeForbidUpdate
|
||||
return
|
||||
}
|
||||
var update = exchcodemod.UpdateDoc{
|
||||
Status: param.Status,
|
||||
Authority: param.Authority,
|
||||
Channel: param.Channel,
|
||||
Reward: param.Reward,
|
||||
EffectiveAt: param.EffectiveAt,
|
||||
InvalidAt: param.InvalidAt,
|
||||
Remark: param.Remark,
|
||||
UsableNum: param.UsableNum,
|
||||
}
|
||||
if err = exchcodemod.UpdateExchangeCode(param.ID, update); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 生成批次号
|
||||
func produceBatchNumber(app exchcodemod.AppType, lastBatchNum string) string {
|
||||
var suffix string
|
||||
switch app {
|
||||
case exchcodemod.AppTypeYSVideo:
|
||||
suffix = "YS-"
|
||||
case exchcodemod.AppTypePFVideo:
|
||||
suffix = "PF-"
|
||||
case exchcodemod.AppTypeLTVpn:
|
||||
suffix = "LT-"
|
||||
}
|
||||
if lastBatchNum == "" {
|
||||
return suffix + "0001"
|
||||
}
|
||||
s := strings.Split(lastBatchNum, "-")
|
||||
if len(s) < 2 {
|
||||
return ""
|
||||
}
|
||||
n := s[1]
|
||||
num, _ := strconv.ParseInt(n, 10, 64)
|
||||
num++
|
||||
var batchNum string
|
||||
if num >= 1000 {
|
||||
batchNum = suffix + strconv.FormatInt(num, 10)
|
||||
} else if num >= 100 && num < 1000 {
|
||||
batchNum = suffix + fmt.Sprintf("0%d", num)
|
||||
} else if num >= 10 && num < 100 {
|
||||
batchNum = suffix + fmt.Sprintf("00%d", num)
|
||||
} else {
|
||||
batchNum = suffix + fmt.Sprintf("000%d", num)
|
||||
}
|
||||
return batchNum
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package exchcodeser
|
||||
|
||||
import (
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/l/exchlogmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
// 获取兑换码日志列表
|
||||
func GetExchangeTypeList(param exchlogmod.ListReqParam) (code stderr.Code, data interface{}, err error) {
|
||||
var filterParam = exchlogmod.FilterDoc{
|
||||
Code: param.Code,
|
||||
UserID: param.UserID,
|
||||
Channel: param.Channel,
|
||||
Authority: param.Authority,
|
||||
}
|
||||
total, tList, err := exchlogmod.GetExchangeLogList(filterParam, param.Page)
|
||||
if err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
list := make([]exchlogmod.ListResp, len(tList))
|
||||
for i, v := range tList {
|
||||
// 兼容之前的数据
|
||||
if v.RewardCount == 0 {
|
||||
v.RewardCount = 1
|
||||
}
|
||||
list[i] = exchlogmod.ListResp{
|
||||
Code: v.Code,
|
||||
UserID: v.UserID,
|
||||
Channel: v.Channel,
|
||||
Authority: v.Authority,
|
||||
Reward: v.Reward,
|
||||
RewardCount: v.RewardCount,
|
||||
CreatedAt: v.CreatedAt,
|
||||
}
|
||||
}
|
||||
if tList == nil {
|
||||
data = commod.ListResp{Total: 0, List: []exchlogmod.ExchangeLog{}}
|
||||
} else {
|
||||
data = commod.ListResp{Total: total, List: list}
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
package exportser
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/constant"
|
||||
"91porn-server/common/file"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/s/statordermod"
|
||||
"91porn-server/models/s/statvidmod"
|
||||
"91porn-server/models/v/exchcodemod"
|
||||
"91porn-server/models/v/rchgordmod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/wdordmod"
|
||||
|
||||
"github.com/tealeg/xlsx"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// Import 读取文件
|
||||
func Import(inFile string) (data *list.List, errr error) {
|
||||
// 打开文件
|
||||
xlFile, err := xlsx.OpenFile(inFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := list.New()
|
||||
// 遍历sheet页读取
|
||||
for _, sheet := range xlFile.Sheets {
|
||||
fmt.Println("sheet name: ", sheet.Name)
|
||||
//遍历行读取
|
||||
for _, row := range sheet.Rows {
|
||||
var rowCell []string
|
||||
// 遍历每行的列读取
|
||||
for _, cell := range row.Cells {
|
||||
text := cell.String()
|
||||
rowCell = append(rowCell, text)
|
||||
}
|
||||
l.PushBack(rowCell)
|
||||
}
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
type Student struct {
|
||||
Name string
|
||||
age int
|
||||
Phone string
|
||||
Gender string
|
||||
Mail string
|
||||
}
|
||||
|
||||
// Export 导出xlsx文件
|
||||
func Export(typeFile int64) (string, error) {
|
||||
switch typeFile {
|
||||
case constant.ChannelLogTypeFile:
|
||||
return ExampleExport()
|
||||
default:
|
||||
return "", errors.New("not this type of excel")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleExport() (string, error) {
|
||||
var fileName = "导出excel案列文件.xlsx"
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("导出excel案列")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
data := getStudents()
|
||||
//add data
|
||||
for _, stu := range data {
|
||||
row := sheet.AddRow()
|
||||
nameCell := row.AddCell()
|
||||
nameCell.Value = stu.Name
|
||||
ageCell := row.AddCell()
|
||||
ageCell.Value = strconv.Itoa(stu.age)
|
||||
phoneCell := row.AddCell()
|
||||
phoneCell.Value = stu.Phone
|
||||
genderCell := row.AddCell()
|
||||
genderCell.Value = stu.Gender
|
||||
mailCell := row.AddCell()
|
||||
mailCell.Value = stu.Mail
|
||||
}
|
||||
var path, _ = filepath.Abs("temp")
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
fmt.Println(fpath)
|
||||
if err = f.Save(fpath); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
fmt.Println("\n\nexport success")
|
||||
return fpath, nil
|
||||
}
|
||||
|
||||
func StructToTitle(s *xlsx.Sheet, obj interface{}) {
|
||||
t := reflect.TypeOf(obj)
|
||||
row := s.AddRow()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
k, _ := t.Field(i).Tag.Lookup("json")
|
||||
c := row.AddCell()
|
||||
c.Value = k
|
||||
}
|
||||
}
|
||||
|
||||
func StructToTitle2(s *xlsx.Sheet, obj interface{}, lableMap map[string]string) {
|
||||
t := reflect.TypeOf(obj)
|
||||
v := reflect.ValueOf(obj)
|
||||
row := s.AddRow()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
vv := v.Field(i)
|
||||
fv := vv.Type()
|
||||
switch fv.Kind() {
|
||||
case reflect.Struct:
|
||||
}
|
||||
k, _ := t.Field(i).Tag.Lookup("json")
|
||||
lable, ok := lableMap[k]
|
||||
if !ok {
|
||||
lable = k
|
||||
}
|
||||
row.AddCell().Value = lable
|
||||
}
|
||||
}
|
||||
|
||||
func getValue(kind reflect.Kind, value reflect.Value) string {
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
return value.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(value.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return strconv.FormatUint(value.Uint(), 10)
|
||||
case reflect.Bool:
|
||||
return strconv.FormatBool(value.Bool())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return strconv.FormatFloat(value.Float(), 'f', 2, 64)
|
||||
case reflect.Struct:
|
||||
timeV, ok := value.Interface().(time.Time)
|
||||
if ok {
|
||||
return timeV.String()
|
||||
}
|
||||
if oid, ok := value.Interface().(primitive.ObjectID); ok {
|
||||
return oid.Hex()
|
||||
}
|
||||
return ""
|
||||
case reflect.Interface:
|
||||
if timeV, ok := value.Interface().(time.Time); ok {
|
||||
return timeV.String()
|
||||
}
|
||||
return ""
|
||||
case reflect.Ptr:
|
||||
if value.IsNil() {
|
||||
return ""
|
||||
}
|
||||
value = value.Elem()
|
||||
if value.Interface() != nil {
|
||||
if timeV, ok := value.Interface().(time.Time); ok {
|
||||
return timeV.String()
|
||||
}
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
case reflect.Array:
|
||||
if id, ok := value.Interface().(primitive.ObjectID); ok {
|
||||
if id.IsZero() {
|
||||
return ""
|
||||
} else {
|
||||
return id.Hex()
|
||||
}
|
||||
}
|
||||
}
|
||||
return "未知"
|
||||
}
|
||||
|
||||
func StructToRow(row *xlsx.Row, obj interface{}) error {
|
||||
t := reflect.TypeOf(obj)
|
||||
v := reflect.ValueOf(obj)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
fv := v.Field(i).Type()
|
||||
vv := v.Field(i)
|
||||
c := row.AddCell()
|
||||
c.Value = getValue(fv.Kind(), vv)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UserListExport(path string, index int, users []usermod.ExportUser) (string, error) {
|
||||
var fileName = fmt.Sprintf("用户列表_%v.xlsx", index)
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("用户列表")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
StructToTitle(sheet, usermod.ExportUser{})
|
||||
//add data
|
||||
for _, u := range users {
|
||||
row := sheet.AddRow()
|
||||
_ = StructToRow(row, u)
|
||||
}
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
fmt.Println(fpath)
|
||||
err = f.Save(fpath)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
fmt.Println("\n\nexport success")
|
||||
return fpath, nil
|
||||
}
|
||||
|
||||
func getStudents() []Student {
|
||||
students := make([]Student, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
stu := Student{}
|
||||
stu.Name = "name" + strconv.Itoa(i+1)
|
||||
stu.Mail = stu.Name + "@chairis.cn"
|
||||
stu.Phone = "1380013800" + strconv.Itoa(i)
|
||||
stu.age = 20
|
||||
stu.Gender = "男"
|
||||
students[i] = stu
|
||||
}
|
||||
return students
|
||||
}
|
||||
|
||||
// RechargeOrderExport 充值订单存xlsx
|
||||
func RechargeOrderExport(orders []*rchgordmod.RchargeOrderExport) (string, error) {
|
||||
var fileName = "充值订单.xlsx"
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("充值订单")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
StructToTitle(sheet, rchgordmod.RchargeOrderExport{})
|
||||
//add data
|
||||
for _, o := range orders {
|
||||
row := sheet.AddRow()
|
||||
_ = StructToRow(row, *o)
|
||||
}
|
||||
var path, _ = filepath.Abs("temp")
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
if err = f.Save(fpath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fpath, nil
|
||||
}
|
||||
|
||||
// WithdrawOrderExport 提现订单存xlsx
|
||||
func WithdrawOrderExport(orders []*wdordmod.WithdrawOrder) (string, error) {
|
||||
var fileName = "提现订单.xlsx"
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("提现订单")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
StructToTitle(sheet, wdordmod.WithdrawOrder{})
|
||||
//add data
|
||||
for _, o := range orders {
|
||||
row := sheet.AddRow()
|
||||
_ = StructToRow(row, *o)
|
||||
}
|
||||
var path, _ = filepath.Abs("temp")
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
if err = f.Save(fpath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fpath, nil
|
||||
}
|
||||
|
||||
// GoldTurnoverExport 金币流水存xlsx
|
||||
func GoldTurnoverExport(turnover []*txnmod.TransactionLog) (string, error) {
|
||||
var fileName = "金币流水.xlsx"
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("金币流水")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
StructToTitle(sheet, txnmod.TransactionLog{})
|
||||
//add data
|
||||
for _, t := range turnover {
|
||||
row := sheet.AddRow()
|
||||
_ = StructToRow(row, *t)
|
||||
}
|
||||
var path, _ = filepath.Abs("temp")
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
if err = f.Save(fpath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fpath, nil
|
||||
}
|
||||
|
||||
func changeVideoStatRecord(incomes []statvidmod.VideoStatRecord) []statvidmod.VideoStatExport {
|
||||
infos := make([]statvidmod.VideoStatExport, len(incomes))
|
||||
for i, v := range incomes {
|
||||
vs := statvidmod.VideoStatExport{}
|
||||
if v.SumDate != nil {
|
||||
vs.SumDate = *v.SumDate
|
||||
}
|
||||
if v.Video.ID != nil {
|
||||
vs.ID = *v.Video.ID
|
||||
}
|
||||
if v.Video.Title != nil {
|
||||
vs.Title = *v.Video.Title
|
||||
}
|
||||
if v.Video.PublisherID != nil {
|
||||
vs.PublisherID = *v.Video.PublisherID
|
||||
}
|
||||
if v.Video.Coins != nil {
|
||||
vs.Coins = *v.Video.Coins
|
||||
}
|
||||
if v.Income != nil {
|
||||
vs.Income = *v.Income
|
||||
}
|
||||
if v.Tax != nil {
|
||||
vs.Tax = *v.Tax
|
||||
}
|
||||
if v.PayCount != nil {
|
||||
vs.PayCount = *v.PayCount
|
||||
}
|
||||
if v.CreatedAt != nil {
|
||||
vs.CreatedAt = *v.CreatedAt
|
||||
}
|
||||
infos[i] = vs
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// VideoIncomeExport 视频收入存xlsx
|
||||
func VideoIncomeExport(incomes []statvidmod.VideoStatRecord) (string, error) {
|
||||
infos := changeVideoStatRecord(incomes)
|
||||
var fileName = "视频收入.xlsx"
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("视频收入")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
StructToTitle(sheet, statvidmod.VideoStatExport{})
|
||||
//add data
|
||||
for _, i := range infos {
|
||||
row := sheet.AddRow()
|
||||
_ = StructToRow(row, i)
|
||||
}
|
||||
var path, _ = filepath.Abs("temp")
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
if err = f.Save(fpath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fpath, nil
|
||||
}
|
||||
|
||||
// OrderStatExport 订单统计存xlsx
|
||||
func OrderStatExport(orders []statordermod.OrderState) (string, error) {
|
||||
var fileName = "订单统计.xlsx"
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("订单统计")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
StructToTitle(sheet, statordermod.OrderState{})
|
||||
//add data
|
||||
for _, o := range orders {
|
||||
row := sheet.AddRow()
|
||||
_ = StructToRow(row, o)
|
||||
}
|
||||
var path, _ = filepath.Abs("temp")
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
if err = f.Save(fpath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fpath, nil
|
||||
}
|
||||
|
||||
// ExchangeCodeExport 兑换码导出
|
||||
func ExchangeCodeExport(codes []exchcodemod.RespList) (string, error) {
|
||||
var fileName = "兑换码.xlsx"
|
||||
f := xlsx.NewFile()
|
||||
sheet, err := f.AddSheet("兑换码")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
StructToTitle(sheet, exchcodemod.RespList{})
|
||||
//add data
|
||||
for _, c := range codes {
|
||||
row := sheet.AddRow()
|
||||
_ = StructToRow(row, c)
|
||||
}
|
||||
var path, _ = filepath.Abs("temp")
|
||||
if err = file.MakeDir(path); err != nil {
|
||||
log.Warn("can't make fs dir", log.E(err))
|
||||
}
|
||||
fpath := path + "/" + fileName
|
||||
if err = f.Save(fpath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fpath, nil
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package ffser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"91porn-server/common/crypt"
|
||||
)
|
||||
|
||||
// AvFormat 调用ffmpeg 获取信息
|
||||
func AvFormat(fpath string) (resloution string, playTime uint, err error) {
|
||||
args := makeArgs(fpath, []string{"width", "height", "duration"}...)
|
||||
fmt.Printf("%v", args)
|
||||
command := exec.CommandContext(context.Background(), "ffprobe", args...)
|
||||
out, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("ffprobe convert to ts wrong %+v", err)
|
||||
return
|
||||
}
|
||||
if strings.Contains(string(out), "Invalid argument") {
|
||||
err = errors.New("ffmpeg convert err")
|
||||
return
|
||||
}
|
||||
result, _ := crypt.JSON2Map(string(out))
|
||||
if v, ok := result["streams"].([]interface{}); ok {
|
||||
if v2, ok := v[0].(map[string]interface{}); ok {
|
||||
width, _ := json.Marshal(v2["width"])
|
||||
height, _ := json.Marshal(v2["height"])
|
||||
duration, _ := v2["duration"].(string)
|
||||
resloution = string(width) + "*" + string(height)
|
||||
pt, _ := strconv.ParseFloat(string(duration), 32)
|
||||
playTime = uint(math.Ceil(pt))
|
||||
fmt.Println(playTime)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeArgs(path string, command ...string) []string {
|
||||
com := strings.Join(command, ",")
|
||||
return []string{
|
||||
"-v", "quiet",
|
||||
"-v", "quiet",
|
||||
"-select_streams", "v", "-show_entries",
|
||||
"stream=" + com,
|
||||
"-i", path,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package filterser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/filter"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/filtermod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// 获取敏感词列表
|
||||
func GetFilterWordList(content *string, page commod.Page) (code stderr.Code, data interface{}, err error) {
|
||||
total, wordList, err := filtermod.GetFilterWordList(content, page)
|
||||
if err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
list := make([]filtermod.WordListResp, len(wordList))
|
||||
for i, v := range wordList {
|
||||
list[i] = filtermod.WordListResp(v)
|
||||
}
|
||||
data = commod.ListResp{Total: total, List: list}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加过滤词
|
||||
func AddFilterWord(words []string) (code stderr.Code, err error) {
|
||||
docs := make([]filtermod.WordsFilter, len(words))
|
||||
for i, v := range words {
|
||||
docs[i] = filtermod.WordsFilter{
|
||||
Word: v,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
if len(docs) == 0 {
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
if err = filtermod.InsertMany(docs); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
if err = filter.UpdateFilterWords(); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除过滤词
|
||||
func DeleteFilterWords(ids []primitive.ObjectID) (code stderr.Code, err error) {
|
||||
if err = filtermod.DeleteFilterWords(ids); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
if err = filter.UpdateFilterWords(); err != nil {
|
||||
code = stderr.Failure
|
||||
return
|
||||
}
|
||||
code = stderr.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package imagetopser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/imagetopmod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func FindImageTopList(req imagetopmod.ImageTopListReq) (code stderr.Code, data interface{}) {
|
||||
resp := imagetopmod.ImageTopListRes{
|
||||
Total: 0,
|
||||
List: make([]imagetopmod.ImageTopResp, 0),
|
||||
}
|
||||
//查询帖子列表
|
||||
skip := (req.PageNumber - 1) * req.PageSize
|
||||
var filter = bson.M{}
|
||||
switch req.TopType {
|
||||
case 1: //推荐
|
||||
filter = bson.M{"recommendSort": bson.M{"$gt": 0}}
|
||||
case 2: //最新
|
||||
filter = bson.M{"newSort": bson.M{"$gt": 0}}
|
||||
}
|
||||
sort := bson.D{{Key: "updatedAt", Value: -1}}
|
||||
opts := options.Find().SetSort(sort).SetSkip(int64(skip)).SetLimit(int64(req.PageSize))
|
||||
imageTopList, err := imagetopmod.GetImageTopList(filter, opts)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("获取视频列表 发生错误:%v", err), log.Any("params", req))
|
||||
return stderr.ErrDbQueryError, nil
|
||||
}
|
||||
//获取置顶视频数量
|
||||
total, _ := imagetopmod.GetImageTopCount(filter)
|
||||
resp.Total = total
|
||||
//根据视频ID获取置顶视频信息
|
||||
resp.List = make([]imagetopmod.ImageTopResp, len(imageTopList))
|
||||
for i, v := range imageTopList {
|
||||
vidInfo, _ := vidmod.GetVideoByID(v.ImageId)
|
||||
resp.List[i] = imagetopmod.ImageTopResp{
|
||||
ID: vidInfo.ID,
|
||||
NewsType: vidInfo.NewsType,
|
||||
PubliserID: vidInfo.PublisherID,
|
||||
Title: vidInfo.Title,
|
||||
SourceID: vidInfo.SourceID,
|
||||
SourceURL: vidInfo.SourceURL,
|
||||
FileName: vidInfo.Filename,
|
||||
PlayTime: vidInfo.PlayTime,
|
||||
Status: vidInfo.Status,
|
||||
Cover: vidInfo.Cover,
|
||||
CoverThumb: vidInfo.CoverThumb,
|
||||
SeriesCover: vidInfo.SeriesCover,
|
||||
PlayCount: vidInfo.PlayCount,
|
||||
LikeCount: vidInfo.LikeCount,
|
||||
CommentCount: vidInfo.CommentCount,
|
||||
ShareCount: vidInfo.ShareCount,
|
||||
FakeLikeCount: vidInfo.FakeLikeCount,
|
||||
FakeCommentCount: vidInfo.FakeCommentCount,
|
||||
FakeShareCount: vidInfo.FakeShareCount,
|
||||
FakePlayCount: vidInfo.FakePlayCount,
|
||||
Coins: vidInfo.Coins,
|
||||
Resolution: vidInfo.Resolution,
|
||||
FreeTime: vidInfo.FreeTime,
|
||||
Chosen: vidInfo.Chosen,
|
||||
FreeArea: vidInfo.FreeArea,
|
||||
Size: vidInfo.Size,
|
||||
CreatedAt: vidInfo.CreatedAt,
|
||||
UpdatedAt: vidInfo.UpdatedAt,
|
||||
Reason: vidInfo.Reason,
|
||||
IsTopping: vidInfo.IsTopping,
|
||||
IsRecommend: vidInfo.IsRecommend,
|
||||
IsChoosen: vidInfo.IsChoosen,
|
||||
RecoWeight: vidInfo.RecoWeight,
|
||||
ReviewAccount: vidInfo.ReviewAccount,
|
||||
LinkUrl: vidInfo.LinkUrl,
|
||||
SortCode: vidInfo.SortCode,
|
||||
Rewarded: vidInfo.Rewarded,
|
||||
FakeRewarded: vidInfo.FakeRewarded,
|
||||
LiaoBaTop: vidInfo.LiaoBaTop,
|
||||
LiaoBaTopSort: vidInfo.LiaoBaTopSort,
|
||||
WorksSort: vidInfo.WorksSort,
|
||||
PageViewCount: vidInfo.PageViewCount,
|
||||
ActivityID: vidInfo.ActivityID,
|
||||
NewSort: v.NewSort,
|
||||
NewEndTime: *v.NewEndTime,
|
||||
RecommendSort: v.RecommendSort,
|
||||
RecommendEndTime: *v.RecommendEndTime,
|
||||
}
|
||||
}
|
||||
code = stderr.Success
|
||||
data = resp
|
||||
return
|
||||
}
|
||||
|
||||
func UpdateImageTop(req imagetopmod.EditReq) (code stderr.Code, data interface{}) {
|
||||
doc := imagetopmod.ImageTopModel{
|
||||
ImageId: req.ID,
|
||||
NewSort: req.NewSort,
|
||||
RecommendSort: req.RecommendSort,
|
||||
NewEndTime: &req.NewEndTime,
|
||||
RecommendEndTime: &req.RecommendEndTime,
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
update, _ := common.ToBsonM(doc)
|
||||
if _, err := imagetopmod.UpdateImageTop(req.ID, update); err != nil {
|
||||
log.Error(fmt.Sprintf("更新置顶帖子 发生错误:%v", err), log.Any("params", req))
|
||||
return stderr.ErrDbUpdateError, nil
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
|
||||
func DeleteImageTop(req imagetopmod.DeleteReq) (code stderr.Code, data interface{}) {
|
||||
if err := imagetopmod.DeleteOne(req.Id); err != nil {
|
||||
log.Error(fmt.Sprintf("删除置顶贴子 发生错误:%v", err), log.Any("params", req))
|
||||
return stderr.ErrDbDeleteError, nil
|
||||
}
|
||||
set := bson.M{"happinessPlazaTop": 0}
|
||||
//兼容老版本,将幸福广场参数置为0
|
||||
if _, err := vidmod.UpdateOneByID(req.Id, set); err != nil {
|
||||
log.Error(fmt.Sprintf("更新视频信息 发生错误:%v", err), log.Any("params", req))
|
||||
return stderr.ErrDbUpdateError, nil
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package imgroupmemberser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/imgroupmemberdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/imgroupmembermod"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []imgroupmembermod.ImGroupMember `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
filter := bson.M{}
|
||||
res.List, res.Total, res.HasNext, err = imgroupmembermod.GetList(filter, int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
GroupId int64 `json:"groupId" bson:"groupId"` // 群组id
|
||||
Uid uint64 `json:"uid" form:"uid" binding:"required"` // uid
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create() error {
|
||||
data := imgroupmembermod.ImGroupMember{
|
||||
GroupId: p.GroupId,
|
||||
Uid: p.Uid,
|
||||
Status: 1,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := imgroupmemberdata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
Status *int `json:"status" bson:"status"` // 0-禁言 1-可用 2-已移除
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
_, err := imgroupmembermod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
if _, err = imgroupmemberdata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
package imgroupser
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/imgroupdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/imgroupmod"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []imgroupmod.ImGroup `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
filter := bson.M{}
|
||||
res.List, res.Total, res.HasNext, err = imgroupmod.GetList(filter, int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
Name string `json:"name" form:"name" binding:"required"` // 群组名
|
||||
Cover string `json:"cover" form:"cover" binding:"required"` // 封面
|
||||
Summary string `json:"summary" form:"summary" binding:"required"` // 简介
|
||||
FakeMemberNum int64 `json:"fakeMemberNum" form:"fakeMemberNum"` // 成员数量(假的)
|
||||
Price int64 `json:"price" form:"price"` // 加入群聊价格 0-免费
|
||||
Status int `json:"status" form:"status"` // 0-不可用 1-可用
|
||||
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create() error {
|
||||
data := imgroupmod.ImGroup{
|
||||
GroupId: 10000000 + rand.Int63n(89999999), // 随机一个
|
||||
Name: p.Name,
|
||||
Cover: p.Cover,
|
||||
Summary: p.Summary,
|
||||
FakeMemberNum: p.FakeMemberNum,
|
||||
Price: p.Price,
|
||||
Status: p.Status,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := imgroupdata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
Name *string `json:"name"` // 群组名
|
||||
Cover *string `json:"cover"` // 封面
|
||||
Summary *string `json:"summary"` // 简介
|
||||
FakeMemberNum *int64 `json:"fakeMemberNum"` // 成员数量(假的)
|
||||
Price *int64 `json:"price"` // 加入群聊价格 0-免费
|
||||
Status *int `json:"status" form:"status"` // 0-不可用 1-可用
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
_, err := imgroupmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Name != nil {
|
||||
data["name"] = *p.Name
|
||||
}
|
||||
if p.Cover != nil {
|
||||
data["cover"] = *p.Cover
|
||||
}
|
||||
if p.Summary != nil {
|
||||
data["summary"] = *p.Summary
|
||||
}
|
||||
if p.FakeMemberNum != nil {
|
||||
data["fakeMemberNum"] = *p.FakeMemberNum
|
||||
}
|
||||
if p.Price != nil {
|
||||
data["price"] = *p.Price
|
||||
}
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
|
||||
if _, err = imgroupdata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
package immessageser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/immessagedata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/immessagemod"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
GroupId *int64 `json:"groupId" form:"groupId"`
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []immessagemod.ImMessage `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
filter := bson.M{}
|
||||
if q.GroupId != nil {
|
||||
filter["groupId"] = *q.GroupId
|
||||
}
|
||||
res.List, res.Total, res.HasNext, err = immessagemod.GetList(filter, int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
GroupId int64 `json:"groupId" form:"groupId" binding:"required"` // 群组id
|
||||
Uid uint64 `json:"uid" form:"uid" binding:"required"` // 用户id
|
||||
Content string `json:"content" form:"content"` // 消息内容
|
||||
Image string `json:"image" form:"image"` // 图片
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create() error {
|
||||
data := immessagemod.ImMessage{
|
||||
GroupId: p.GroupId,
|
||||
Uid: p.Uid,
|
||||
Content: p.Content,
|
||||
Image: p.Image,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := immessagedata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
GroupId *int64 `json:"groupId"` // 群组id
|
||||
Uid *int64 `json:"uid"` // 用户id
|
||||
Content *string `json:"content"` // 消息内容
|
||||
Image *string `json:"image"` // 图片
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
_, err := immessagemod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.GroupId != nil {
|
||||
data["groupId"] = *p.GroupId
|
||||
}
|
||||
if p.Uid != nil {
|
||||
data["uid"] = *p.Uid
|
||||
}
|
||||
if p.Content != nil {
|
||||
data["content"] = *p.Content
|
||||
}
|
||||
if p.Image != nil {
|
||||
data["image"] = *p.Image
|
||||
}
|
||||
|
||||
if _, err = immessagedata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
if err := immessagedata.DeleteData(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package infmtser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/pageopt"
|
||||
"91porn-server/models/v/noticefmtmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type Sender = noticefmtmod.Sender
|
||||
|
||||
type Receiver = noticefmtmod.Receiver
|
||||
|
||||
type UIDSlice = noticefmtmod.UIDSlice
|
||||
|
||||
type NoticeType = noticefmtmod.NoticeType
|
||||
|
||||
type RegularNotice = noticefmtmod.RegularNotice
|
||||
|
||||
type PlanNotice = noticefmtmod.PlanNotice
|
||||
|
||||
type SpecifyNotice = noticefmtmod.SpecifyNotice
|
||||
|
||||
type AfterRegistNotice = noticefmtmod.AfterRegistNotice
|
||||
|
||||
type MailNotice = noticefmtmod.MailNotice
|
||||
|
||||
type TimeUnit = noticefmtmod.TimeUnit
|
||||
|
||||
type TimeSlice = noticefmtmod.TimeSlice
|
||||
|
||||
type Record struct {
|
||||
ID ObjectID `json:"_id,omitempty"` //_id
|
||||
NoticeCode string `json:"noticeCode"` //通知码 唯一标记不同的通知计划,可代替_id,方便复制数据
|
||||
Title string `json:"title"` //标题
|
||||
Sender Sender `json:"sender"` //发送者
|
||||
Receiver Receiver `json:"receiver"` //接受者
|
||||
Content string `json:"content"` //内容
|
||||
Remark string `json:"remark"` //备注
|
||||
NoticeType NoticeType `json:"noticeType"` //通知类型
|
||||
Enable bool `json:"enable"` //消息开关 true:允许通知Receiver,否则不允许通知Receiver
|
||||
UIDList UIDSlice `json:"uidList"` //用户列表
|
||||
Rule string `json:"rule"` //描述
|
||||
|
||||
StartAt *time.Time `json:"startAt"` //缺省字段
|
||||
EndAt *time.Time `json:"endAt"` //缺省字段
|
||||
TimeUnit *TimeUnit `json:"timeUnit"` //缺省字段
|
||||
TimeCount *int64 `json:"timeCount"` //缺省字段
|
||||
PlanCount *int64 `json:"planCount"` //缺省字段
|
||||
SpecifyTimeList TimeSlice `json:"specifyTimeList"` //缺省字段
|
||||
}
|
||||
|
||||
type NoticePage struct {
|
||||
Total int64 `json:"total" bson:"total"`
|
||||
List []Record `json:"list" bson:"list"`
|
||||
}
|
||||
|
||||
type TitleMatch = noticefmtmod.TitleMatch
|
||||
type NoticeCodeMatch = noticefmtmod.NoticeCodeMatch
|
||||
type NoticeTypeMatch = noticefmtmod.NoticeTypeMatch
|
||||
type SenderMatch = noticefmtmod.SenderMatch
|
||||
type ReceiverMatch = noticefmtmod.ReceiverMatch
|
||||
type EnableMatch = noticefmtmod.EnableMatch
|
||||
|
||||
func NoticePages(skip, limit int64,
|
||||
titleMatch TitleMatch,
|
||||
noticeCodeMatch NoticeCodeMatch,
|
||||
noticeTypeMatch NoticeTypeMatch,
|
||||
senderMatch SenderMatch,
|
||||
receiverMatch ReceiverMatch,
|
||||
enableMatch EnableMatch,
|
||||
) (NoticePage, error) {
|
||||
mats := []pageopt.Matcher{
|
||||
titleMatch.New(),
|
||||
noticeCodeMatch.New(),
|
||||
noticeTypeMatch.New(),
|
||||
senderMatch.New(),
|
||||
receiverMatch.New(),
|
||||
enableMatch.New(),
|
||||
}
|
||||
sort := bson.D{{Key: "createdAt", Value: -1}}
|
||||
list, err := noticefmtmod.List(sort, skip, limit, mats...)
|
||||
if err != nil {
|
||||
return NoticePage{}, err
|
||||
}
|
||||
recordList := make([]Record, len(list))
|
||||
for i, v := range list {
|
||||
record := Record{
|
||||
ID: v.ID,
|
||||
NoticeCode: v.NoticeCode,
|
||||
Title: v.Title,
|
||||
Sender: v.Sender,
|
||||
Receiver: v.Receiver,
|
||||
Content: v.Content,
|
||||
Remark: v.Remark,
|
||||
NoticeType: v.NoticeType,
|
||||
Enable: v.Enable,
|
||||
UIDList: v.UIDList,
|
||||
}
|
||||
switch v.NoticeType {
|
||||
case noticefmtmod.RegularNoticeType:
|
||||
record.Rule = v.RegularNotice.Rule()
|
||||
record.StartAt = v.RegularNotice.StartAt
|
||||
record.TimeUnit = v.RegularNotice.TimeUnit
|
||||
record.TimeCount = v.RegularNotice.TimeCount
|
||||
case noticefmtmod.PlanNoticeType:
|
||||
record.Rule = v.PlanNotice.Rule()
|
||||
record.StartAt = v.PlanNotice.StartAt
|
||||
record.EndAt = v.PlanNotice.EndAt
|
||||
record.PlanCount = v.PlanNotice.PlanCount
|
||||
case noticefmtmod.SpecifyNoticeType:
|
||||
record.Rule = v.SpecifyNotice.Rule()
|
||||
record.SpecifyTimeList = v.SpecifyNotice.SpecifyTimeList
|
||||
case noticefmtmod.AfterRegistNoticeType:
|
||||
record.Rule = v.AfterRegistNotice.Rule()
|
||||
record.TimeUnit = v.AfterRegistNotice.TimeUnit
|
||||
record.TimeCount = v.AfterRegistNotice.TimeCount
|
||||
}
|
||||
recordList[i] = record
|
||||
}
|
||||
count, err := noticefmtmod.Count(mats...)
|
||||
if err != nil {
|
||||
return NoticePage{}, err
|
||||
}
|
||||
return NoticePage{
|
||||
count,
|
||||
recordList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Mail struct {
|
||||
ID ObjectID `json:"_id,omitempty"` //_id
|
||||
NoticeCode string `json:"noticeCode"` //通知码 唯一标记不同的通知计划,可代替_id,方便复制数据
|
||||
Title string `json:"title"` //标题
|
||||
Receiver Receiver `json:"receiver"` //接受者
|
||||
Content string `json:"content"` //内容
|
||||
Remark string `json:"remark"` //备注
|
||||
Enable bool `json:"-"` //消息开关 true:允许通知Receiver,否则不允许通知Receiver
|
||||
UIDList UIDSlice `json:"uidList"` //用户列表
|
||||
SendAt time.Time `json:"sendAt"` //发送时间
|
||||
}
|
||||
|
||||
type MailPage struct {
|
||||
Total int64 `json:"total" bson:"total"`
|
||||
List []Mail `json:"list" bson:"list"`
|
||||
}
|
||||
|
||||
func MailPages(skip, limit int64,
|
||||
titleMatch TitleMatch,
|
||||
noticeCodeMatch NoticeCodeMatch,
|
||||
receiverMatch ReceiverMatch,
|
||||
) (MailPage, error) {
|
||||
noticeType := noticefmtmod.MailNoticeType
|
||||
mats := []pageopt.Matcher{
|
||||
(&NoticeTypeMatch{NoticeType: ¬iceType}).New(),
|
||||
titleMatch.New(),
|
||||
noticeCodeMatch.New(),
|
||||
receiverMatch.New(),
|
||||
}
|
||||
sort := bson.D{{Key: "createdAt", Value: -1}}
|
||||
list, err := noticefmtmod.List(sort, skip, limit, mats...)
|
||||
if err != nil {
|
||||
return MailPage{}, err
|
||||
}
|
||||
recordList := make([]Mail, len(list))
|
||||
for i, v := range list {
|
||||
record := Mail{
|
||||
ID: v.ID,
|
||||
NoticeCode: v.NoticeCode,
|
||||
Title: v.Title,
|
||||
Receiver: v.Receiver,
|
||||
Content: v.Content,
|
||||
Remark: v.Remark,
|
||||
Enable: v.Enable,
|
||||
UIDList: v.UIDList,
|
||||
}
|
||||
if v.NoticeType == noticefmtmod.MailNoticeType {
|
||||
if v.MailNotice.SendAt != nil {
|
||||
record.SendAt = *v.MailNotice.SendAt
|
||||
}
|
||||
}
|
||||
recordList[i] = record
|
||||
}
|
||||
count, err := noticefmtmod.Count(mats...)
|
||||
if err != nil {
|
||||
return MailPage{}, err
|
||||
}
|
||||
return MailPage{
|
||||
count,
|
||||
recordList,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package integarl_exchange_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/integralexcangemod"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func Edit(in *integralexcangemod.EditCond, manager string) error {
|
||||
oid, _ := primitive.ObjectIDFromHex(in.ID)
|
||||
vp, err := integralexcangemod.FindOneById(oid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if vp.ID.IsZero() {
|
||||
return errors.New("integral config is null")
|
||||
}
|
||||
if *in.Status == integralexcangemod.REFUSE {
|
||||
// 退还积分
|
||||
|
||||
}
|
||||
|
||||
if err = integralexcangemod.Edit(in.Filter(), in.Update(manager)); err != nil {
|
||||
log.Error(fmt.Sprintf("integralconfigmod update error:%+v:", err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func QueryAll(in *integralexcangemod.WebListRequest) (*integralexcangemod.QueryAllRes, error) {
|
||||
var out = &integralexcangemod.QueryAllRes{}
|
||||
count, err := integralexcangemod.QueryAllCount(in.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*integralexcangemod.IntegralExchange{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = integralexcangemod.QueryAllList(in.Filter(), in.Options())
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("integralexcangemod queryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package integralconfigser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/integralconfigmod"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Add(in *integralconfigmod.AddCond, manager string) stderr.Code {
|
||||
err := integralconfigmod.InsertOne(nil, in.Generate(manager))
|
||||
if err != nil {
|
||||
return stderr.ErrDbInsertError
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
func Edit(in *integralconfigmod.EditCond, manager string) error {
|
||||
vp, err := integralconfigmod.FindOneById(in.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if vp.ID.IsZero() {
|
||||
return errors.New("integral config is null")
|
||||
}
|
||||
|
||||
if err = integralconfigmod.Edit(in.Filter(), in.Update(manager)); err != nil {
|
||||
log.Error(fmt.Sprintf("integralconfigmod update error:%+v:", err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func QueryAll(in *integralconfigmod.QueryAllCond) (*integralconfigmod.QueryAllRes, error) {
|
||||
var out = &integralconfigmod.QueryAllRes{}
|
||||
count, err := integralconfigmod.QueryAllCount(in.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*integralconfigmod.IntegralConfig{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = integralconfigmod.QueryAllList(in.Filter(), in.Options())
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("integralconfigmod queryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package jingangser
|
||||
|
||||
import (
|
||||
"91porn-server/models/v/jingangmod"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"time"
|
||||
)
|
||||
|
||||
func UpdateJingang(r UpdateJingangReq) error {
|
||||
cond := make(bson.M)
|
||||
if r.Name != nil {
|
||||
cond["name"] = *r.Name
|
||||
}
|
||||
if r.Img != nil {
|
||||
cond["img"] = *r.Img
|
||||
}
|
||||
if r.Desc != nil {
|
||||
cond["desc"] = *r.Desc
|
||||
}
|
||||
if r.LinkType != nil {
|
||||
cond["link_type"] = *r.LinkType
|
||||
}
|
||||
if r.LinkUrl != nil {
|
||||
cond["link_url"] = *r.LinkUrl
|
||||
}
|
||||
if r.Sort != nil {
|
||||
cond["sort"] = *r.Sort
|
||||
}
|
||||
if r.Status != nil {
|
||||
cond["status"] = *r.Status
|
||||
}
|
||||
if !r.Mid.IsZero() {
|
||||
cond["mid"] = r.Mid
|
||||
}
|
||||
if len(cond) == 0 {
|
||||
return nil
|
||||
}
|
||||
cond["updatedAt"] = time.Now()
|
||||
return jingangmod.EditJingang(nil, r.ID, cond)
|
||||
}
|
||||
|
||||
func AddJingang(r AddJingangReq) (*primitive.ObjectID, error) {
|
||||
now := time.Now()
|
||||
j := jingangmod.JingangModel{
|
||||
Name: r.Name,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if r.Img != nil {
|
||||
j.Img = *r.Img
|
||||
}
|
||||
if r.Desc != nil {
|
||||
j.Desc = *r.Desc
|
||||
}
|
||||
if r.LinkType != nil {
|
||||
j.LinkType = *r.LinkType
|
||||
}
|
||||
if r.LinkUrl != nil {
|
||||
j.LinkUrl = *r.LinkUrl
|
||||
}
|
||||
if r.Sort != nil {
|
||||
j.Sort = *r.Sort
|
||||
}
|
||||
if r.Status != nil {
|
||||
j.Status = *r.Status
|
||||
}
|
||||
if !r.Mid.IsZero() {
|
||||
j.Mid = r.Mid
|
||||
}
|
||||
return jingangmod.AddJingang(nil, j)
|
||||
}
|
||||
|
||||
func DeleteJingang(r DeleteJingangReq) error {
|
||||
return jingangmod.DeleteJingang(nil, r.ID)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package jingangser
|
||||
|
||||
import "go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
type JingangListRequest struct {
|
||||
PageNum uint64 `form:"pageNum" json:"pageNum" binding:"required,min=1"` // 页码
|
||||
PageSize uint64 `form:"pageSize" json:"pageSize" binding:"required,min=1"` // 每页展示数
|
||||
}
|
||||
|
||||
type UpdateJingangReq struct {
|
||||
ID primitive.ObjectID `json:"id" form:"id" binding:"required"` // 金刚id
|
||||
Mid primitive.ObjectID `json:"mid" bson:"mid"` // 所属模块
|
||||
Name *string `json:"name" form:"name"` // 名称
|
||||
Img *string `json:"img" form:"img"` // 图片地址
|
||||
Desc *string `json:"desc" form:"desc"` // 说明
|
||||
LinkType *int64 `json:"link_type" form:"link_type"` // 1 内部链接; 2 外部连接
|
||||
LinkUrl *string `json:"link_url" form:"link_url"` // 链接地址
|
||||
Sort *uint64 `json:"sort" form:"sort"` // 排序
|
||||
Status *uint64 `json:"status" form:"status"` // 1 正常; 2 禁用
|
||||
}
|
||||
|
||||
type AddJingangReq struct {
|
||||
Mid primitive.ObjectID `json:"mid" bson:"mid"` // 所属模块
|
||||
Name string `json:"name" form:"name" binding:"required"` // 名称
|
||||
Img *string `json:"img" form:"img"` // 图片地址
|
||||
Desc *string `json:"desc" form:"desc"` // 说明
|
||||
LinkType *int64 `json:"link_type" form:"link_type"` // 1 内部链接; 2 外部链接
|
||||
LinkUrl *string `json:"link_url" form:"link_url"` // 链接地址
|
||||
Sort *uint64 `json:"sort" form:"sort"` // 排序
|
||||
Status *uint64 `json:"status" form:"status"` // 1 正常; 2 禁用
|
||||
}
|
||||
|
||||
type DeleteJingangReq struct {
|
||||
ID primitive.ObjectID `json:"id" form:"id" binding:"required"`
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
package laosijiser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/laosiji"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/web/service/vidser"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func ComicsSearch(ctx context.Context, param laosiji.ComicsSearchListReq) (resp laosiji.ComicsSearchListResp, err error) {
|
||||
comicsResp, err := laosiji.ComicsSearch(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("MovieSearch", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
//是否导入
|
||||
lsjIds := make([]string, 0)
|
||||
for _, v := range comicsResp.Data {
|
||||
lsjIds = append(lsjIds, v.Id)
|
||||
}
|
||||
comicsList, _ := mediamod.GetListByLsjIds(lsjIds)
|
||||
|
||||
// 处理域名
|
||||
resp = laosiji.ComicsSearchListResp{
|
||||
Data: make([]laosiji.ComicsSearchInfo, 0),
|
||||
Total: comicsResp.Total,
|
||||
Current_page: comicsResp.Current_page,
|
||||
Page_size: comicsResp.Page_size,
|
||||
Last_page: comicsResp.Last_page,
|
||||
}
|
||||
for _, comicsInfo := range comicsResp.Data {
|
||||
//comicsInfo.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, comicsInfo.Img)
|
||||
comicsInfo.Img = common.JoinUrlPath("laosiji", comicsInfo.Img)
|
||||
|
||||
for _, comicsIn := range comicsList {
|
||||
if comicsIn.LsjId == comicsInfo.Id {
|
||||
comicsInfo.IsAdd = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resp.Data = append(resp.Data, comicsInfo)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func ComicsDetail(ctx context.Context, param laosiji.ComicsDetailReq) (resp laosiji.ComicsDetailResp, serr error) {
|
||||
resp, serr = laosiji.ComicsDetail(ctx, param)
|
||||
if serr != nil {
|
||||
log.Error("MovieSearch", log.E(serr))
|
||||
return
|
||||
}
|
||||
|
||||
// 处理域名
|
||||
//resp.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, resp.Img)
|
||||
resp.Img = common.JoinUrlPath("laosiji", resp.Img)
|
||||
if resp.Chapter != nil && len(resp.Chapter) > 0 {
|
||||
for i, cha := range resp.Chapter {
|
||||
if len(cha.Img) > 0 {
|
||||
//resp.Chapter[i].Img = common.JoinUrlPath(laosiji.IMAGEYUAN, resp.Chapter[i].Img)
|
||||
resp.Chapter[i].Img = common.JoinUrlPath("laosiji", resp.Chapter[i].Img)
|
||||
}
|
||||
if cha.Content != nil && len(cha.Content) > 0 {
|
||||
for y, content := range cha.Content {
|
||||
//resp.Chapter[i].Content[y].F = common.JoinUrlPath(laosiji.IMAGEYUAN, content.F)
|
||||
resp.Chapter[i].Content[y].F = common.JoinUrlPath("laosiji", content.F)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ComicsAddList(ctx context.Context, param laosiji.ComicsSyncReq, manager string) (hits []vidser.SensitiveHit, err error) {
|
||||
if param.Ids == nil || len(param.Ids) <= 0 {
|
||||
return
|
||||
}
|
||||
// 批量查询所有详情
|
||||
comicsDetails := make([]laosiji.ComicsDetailResp, 0)
|
||||
for _, id := range param.Ids {
|
||||
var comicsDetail laosiji.ComicsDetailResp
|
||||
req := laosiji.ComicsDetailReq{
|
||||
Id: id,
|
||||
}
|
||||
comicsDetail, err = laosiji.ComicsDetail(ctx, req)
|
||||
if err != nil {
|
||||
log.Warn("ComicsAddList laosiji.ComicsDetail failed", log.Any("id", id), log.E(err))
|
||||
continue
|
||||
}
|
||||
// 处理域名
|
||||
//comicsDetail.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, comicsDetail.Img)
|
||||
comicsDetail.Img = common.JoinUrlPath("laosiji", comicsDetail.Img)
|
||||
if comicsDetail.Chapter != nil && len(comicsDetail.Chapter) > 0 {
|
||||
for i, cha := range comicsDetail.Chapter {
|
||||
if len(cha.Img) > 0 {
|
||||
comicsDetail.Chapter[i].Img = common.JoinUrlPath("laosiji", comicsDetail.Chapter[i].Img)
|
||||
}
|
||||
if cha.Content != nil && len(cha.Content) > 0 {
|
||||
for y, content := range cha.Content {
|
||||
comicsDetail.Chapter[i].Content[y].F = common.JoinUrlPath("laosiji", content.F)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
comicsDetails = append(comicsDetails, comicsDetail)
|
||||
}
|
||||
if len(comicsDetails) <= 0 {
|
||||
log.Warn("ComicsAddList laosiji.ComicsDetailResp len is 0")
|
||||
err = errors.New("laosiji.ComicsDetailResp len is 0")
|
||||
return
|
||||
}
|
||||
// 批量处理标签
|
||||
tags := make(map[string]struct{})
|
||||
for _, comicsDetail := range comicsDetails {
|
||||
if comicsDetail.Tags != nil && len(comicsDetail.Tags) > 0 {
|
||||
for _, tag := range comicsDetail.Tags {
|
||||
tags[tag.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
tagMaps := make(map[string]primitive.ObjectID)
|
||||
if len(tags) > 0 {
|
||||
tagList := make([]string, 0)
|
||||
for k, _ := range tags {
|
||||
tagList = append(tagList, k)
|
||||
}
|
||||
tagMaps, err = checkInsertMediaTag(tagList, 2)
|
||||
if err != nil {
|
||||
log.Error("ComicsAdd checkInsertMediaTag failed", log.E(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
terms := vidser.LoadEnabledSensitiveTerms()
|
||||
hits = make([]vidser.SensitiveHit, 0)
|
||||
failedIds := make([]string, 0)
|
||||
for _, comicsDetail := range comicsDetails {
|
||||
hit, oneErr := syncOneComics(comicsDetail, tagMaps, manager, terms)
|
||||
if oneErr != nil {
|
||||
log.Warn("ComicsAdd syncOneComics failed", log.Any("id", comicsDetail.Id), log.E(oneErr))
|
||||
failedIds = append(failedIds, comicsDetail.Id)
|
||||
continue
|
||||
}
|
||||
if hit != nil {
|
||||
hits = append(hits, *hit)
|
||||
}
|
||||
}
|
||||
if len(failedIds) > 0 {
|
||||
err = errors.New(fmt.Sprintf("需要同步%d部漫画,失败%d,失败Id[%s]", len(param.Ids), len(failedIds), strings.Join(failedIds, ",")))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func syncOneComics(comicsDetail laosiji.ComicsDetailResp, tagMaps map[string]primitive.ObjectID, manager string, terms []string) (*vidser.SensitiveHit, error) {
|
||||
now := time.Now()
|
||||
mediaTemp, err := mediamod.GetListByLsjId(comicsDetail.Id)
|
||||
if err != nil {
|
||||
log.Warn("syncOneComics GetListByLsjId failed", log.Any("id", comicsDetail.Id), log.Any("err", err))
|
||||
return nil, err
|
||||
}
|
||||
if !mediaTemp.ID.IsZero() {
|
||||
return nil, nil
|
||||
}
|
||||
mediaTemp = mediamod.Media{
|
||||
ID: primitive.NewObjectID(),
|
||||
LsjId: comicsDetail.Id,
|
||||
Title: comicsDetail.Name,
|
||||
HorizontalCover: comicsDetail.Img,
|
||||
VerticalCover: comicsDetail.Img,
|
||||
Tags: make([]primitive.ObjectID, 0),
|
||||
TagDetails: make([]mediamod.TagDetail, 0),
|
||||
Summary: comicsDetail.Description,
|
||||
FreeEpisode: 0,
|
||||
CurrentEpisode: 0,
|
||||
MediaType: mediamod.MediaTypeImage,
|
||||
Kind: 5,
|
||||
PermissionIconHide: false,
|
||||
Permission: 2,
|
||||
Price: 0,
|
||||
ContentsPrice: 0,
|
||||
Status: 1,
|
||||
CreatedAt: now,
|
||||
UpdateTime: now,
|
||||
UpdatedAct: manager,
|
||||
}
|
||||
mediaTemp.TotalEpisode, _ = strconv.Atoi(comicsDetail.Chapter_count)
|
||||
mediaTemp.UpdateStatus, _ = strconv.Atoi(comicsDetail.Update_status)
|
||||
mediaTemp.Price, _ = strconv.ParseInt(comicsDetail.Money, 10, 64)
|
||||
if mediaTemp.Price > 0 {
|
||||
mediaTemp.Permission = 1
|
||||
}
|
||||
//if mediaTemp.UpdateStatus == 1 {
|
||||
// mediaTemp.UpdatedDesc = fmt.Sprintf("已完结%d话", mediaTemp.TotalEpisode)
|
||||
//} else {
|
||||
// mediaTemp.UpdatedDesc = fmt.Sprintf("更新至%d话", mediaTemp.TotalEpisode)
|
||||
//}
|
||||
for _, tag := range comicsDetail.Tags {
|
||||
if v, ok := tagMaps[tag.Name]; ok {
|
||||
mediaTemp.TagDetails = append(mediaTemp.TagDetails, mediamod.TagDetail{
|
||||
Id: v,
|
||||
Name: tag.Name,
|
||||
})
|
||||
mediaTemp.Tags = append(mediaTemp.Tags, v)
|
||||
}
|
||||
}
|
||||
contents := make([]mediacontentmod.MediaContent, 0)
|
||||
if len(comicsDetail.Chapter) > 0 {
|
||||
for i, chapter := range comicsDetail.Chapter {
|
||||
mediaContent := mediacontentmod.MediaContent{
|
||||
ID: primitive.NewObjectID(),
|
||||
MediaID: mediaTemp.ID,
|
||||
EpisodeNumber: i + 1,
|
||||
ListenPermission: mediaTemp.Permission,
|
||||
Price: mediaTemp.Price,
|
||||
Name: fmt.Sprintf("第%d话", i+1),
|
||||
Text: "",
|
||||
Md5: "",
|
||||
VideoUrl: "",
|
||||
UrlSet: make([]string, 0),
|
||||
Status: 1,
|
||||
IsActive: true,
|
||||
UpdatedAct: manager,
|
||||
CreatedAt: now,
|
||||
UpdateTime: now,
|
||||
HashId: chapter.Id,
|
||||
}
|
||||
mediaContent.Height, _ = strconv.Atoi(chapter.Content[0].H)
|
||||
mediaContent.Weight, _ = strconv.Atoi(chapter.Content[0].W)
|
||||
if chapter.Content != nil && len(chapter.Content) > 0 {
|
||||
for _, content := range chapter.Content {
|
||||
mediaContent.UrlSet = append(mediaContent.UrlSet, content.F)
|
||||
}
|
||||
}
|
||||
contents = append(contents, mediaContent)
|
||||
}
|
||||
}
|
||||
mediaTemp.CurrentEpisode = len(contents)
|
||||
// 敏感词命中 → 强制下架(Status=0)
|
||||
hit := vidser.CheckTextHits(terms, comicsDetail.Id, mediaTemp.Title, mediaTemp.Summary, "")
|
||||
if hit != nil {
|
||||
mediaTemp.Status = 0
|
||||
log.Warn("syncOneComics sensitive hit, forced offline",
|
||||
log.Any("sourceId", hit.SourceID),
|
||||
log.Any("title", hit.Title),
|
||||
log.Any("detail", hit.Detail))
|
||||
}
|
||||
_, err = mediamod.Insert(nil, mediaTemp)
|
||||
if err != nil {
|
||||
log.Warn("syncOneComics InsertMedia failed", log.Any("media", mediaTemp.ID), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
if len(contents) > 0 {
|
||||
_ = mediacontentmod.InsetBatch(nil, contents)
|
||||
}
|
||||
return hit, nil
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package laosijiser
|
||||
|
||||
import (
|
||||
"91porn-server/common/laosiji"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/mediatagmod"
|
||||
"91porn-server/web/service/vidser"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func checkInsertMediaTag(names []string, typ int) (data map[string]primitive.ObjectID, err error) {
|
||||
data, err = mediatagmod.FindOneTagByNames(names, typ)
|
||||
if err != nil {
|
||||
log.Error("checkInsertMediaTag FindOneTagByiNames failed", log.Any("tags", names), log.E(err))
|
||||
return
|
||||
}
|
||||
insertTags := make([]mediatagmod.MediaTag, 0)
|
||||
for _, tag := range names {
|
||||
if _, ok := data[tag]; !ok {
|
||||
insertTag := mediatagmod.MediaTag{
|
||||
ID: primitive.NewObjectID(),
|
||||
Type: typ,
|
||||
Name: tag,
|
||||
Active: true,
|
||||
Sort: 0,
|
||||
IsDiscovery: false,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
insertTags = append(insertTags, insertTag)
|
||||
data[tag] = insertTag.ID
|
||||
}
|
||||
}
|
||||
if len(insertTags) > 0 {
|
||||
err = mediatagmod.InsertMany(nil, insertTags)
|
||||
if err != nil {
|
||||
log.Warn("checkInsertMediaTag InsertMany failed", log.Any("tags", insertTags), log.E(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func syncCartoonList(movieDetails []laosiji.MovieDetailResp, tags map[string]struct{}, manager string, terms []string) ([]vidser.SensitiveHit, error) {
|
||||
hits := make([]vidser.SensitiveHit, 0)
|
||||
tagMaps := make(map[string]primitive.ObjectID)
|
||||
var err error
|
||||
if len(tags) > 0 {
|
||||
tagList := make([]string, 0)
|
||||
for k, _ := range tags {
|
||||
tagList = append(tagList, k)
|
||||
}
|
||||
tagMaps, err = checkInsertMediaTag(tagList, 1)
|
||||
if err != nil {
|
||||
log.Error("syncCartoonList checkInsertVidTags failed", log.E(err))
|
||||
return hits, err
|
||||
}
|
||||
}
|
||||
failedIds := make([]string, 0)
|
||||
for _, movieDetail := range movieDetails {
|
||||
hit, oneErr := syncOneCartoon(movieDetail, tagMaps, manager, terms)
|
||||
if oneErr != nil {
|
||||
log.Warn("syncCartoonList syncOneCartoon failed", log.Any("id", movieDetail.Id), log.E(oneErr))
|
||||
failedIds = append(failedIds, movieDetail.Id)
|
||||
continue
|
||||
}
|
||||
if hit != nil {
|
||||
hits = append(hits, *hit)
|
||||
}
|
||||
}
|
||||
if len(failedIds) > 0 {
|
||||
return hits, errors.New(fmt.Sprintf("需要同步%d部动漫,失败%d,失败Id[%s]", len(movieDetails), len(failedIds), strings.Join(failedIds, ",")))
|
||||
}
|
||||
return hits, nil
|
||||
}
|
||||
|
||||
func syncOneCartoon(movieDetail laosiji.MovieDetailResp, tagMaps map[string]primitive.ObjectID, manager string, terms []string) (*vidser.SensitiveHit, error) {
|
||||
now := time.Now()
|
||||
mediaTemp, err := mediamod.GetListByLsjId(movieDetail.Id)
|
||||
if err != nil {
|
||||
log.Warn("syncOneCartoon GetListByLsjId failed", log.Any("id", movieDetail.Id), log.Any("err", err))
|
||||
return nil, err
|
||||
}
|
||||
if !mediaTemp.ID.IsZero() {
|
||||
return nil, syncExistingCartoonH265(mediaTemp.ID, movieDetail.Links, manager)
|
||||
}
|
||||
mediaTemp = mediamod.Media{
|
||||
ID: primitive.NewObjectID(),
|
||||
LsjId: movieDetail.Id,
|
||||
Title: movieDetail.Name,
|
||||
HorizontalCover: movieDetail.Img_x,
|
||||
VerticalCover: movieDetail.Img_y,
|
||||
Tags: make([]primitive.ObjectID, 0),
|
||||
TagDetails: make([]mediamod.TagDetail, 0),
|
||||
Summary: movieDetail.Description,
|
||||
MediaType: mediamod.MediaTypeVideo,
|
||||
Kind: 1,
|
||||
PermissionIconHide: false,
|
||||
Permission: 2,
|
||||
Status: 1,
|
||||
CreatedAt: now,
|
||||
UpdateTime: now,
|
||||
UpdatedAct: manager,
|
||||
Anchor: movieDetail.Director,
|
||||
}
|
||||
|
||||
mediaTemp.UpdateStatus, _ = strconv.Atoi(movieDetail.Update_status)
|
||||
|
||||
//if mediaTemp.UpdateStatus == 1 {
|
||||
// mediaTemp.UpdatedDesc = fmt.Sprintf("已完结%d话", mediaTemp.TotalEpisode)
|
||||
//} else {
|
||||
// mediaTemp.UpdatedDesc = fmt.Sprintf("更新至%d话", mediaTemp.TotalEpisode)
|
||||
//}
|
||||
for _, tag := range movieDetail.Tags {
|
||||
if v, ok := tagMaps[tag.Name]; ok {
|
||||
mediaTemp.TagDetails = append(mediaTemp.TagDetails, mediamod.TagDetail{
|
||||
Id: v,
|
||||
Name: tag.Name,
|
||||
})
|
||||
mediaTemp.Tags = append(mediaTemp.Tags, v)
|
||||
}
|
||||
}
|
||||
contents := make([]mediacontentmod.MediaContent, 0)
|
||||
if len(movieDetail.Links) > 0 {
|
||||
for i, chapter := range movieDetail.Links {
|
||||
mediaContent := mediacontentmod.MediaContent{
|
||||
ID: primitive.NewObjectID(),
|
||||
MediaID: mediaTemp.ID,
|
||||
EpisodeNumber: i + 1,
|
||||
ListenPermission: mediaTemp.Permission,
|
||||
Price: mediaTemp.Price,
|
||||
Name: chapter.Name,
|
||||
VideoUrl: chapter.M3u8_url,
|
||||
H265Url: strings.TrimSpace(chapter.Hevc_m3u8_url),
|
||||
UrlSet: make([]string, 0),
|
||||
Status: 1,
|
||||
IsActive: true,
|
||||
UpdatedAct: manager,
|
||||
CreatedAt: now,
|
||||
UpdateTime: now,
|
||||
HashId: chapter.Id,
|
||||
}
|
||||
contents = append(contents, mediaContent)
|
||||
}
|
||||
}
|
||||
|
||||
mediaTemp.CurrentEpisode = len(contents)
|
||||
mediaTemp.TotalEpisode = mediaTemp.CurrentEpisode
|
||||
|
||||
// 敏感词命中 → 强制下架(Status=0)
|
||||
hit := vidser.CheckTextHits(terms, movieDetail.Id, mediaTemp.Title, mediaTemp.Summary, "")
|
||||
if hit != nil {
|
||||
mediaTemp.Status = 0
|
||||
log.Warn("syncOneCartoon sensitive hit, forced offline",
|
||||
log.Any("sourceId", hit.SourceID),
|
||||
log.Any("title", hit.Title),
|
||||
log.Any("detail", hit.Detail))
|
||||
}
|
||||
|
||||
_, err = mediamod.Insert(nil, mediaTemp)
|
||||
if err != nil {
|
||||
log.Warn("syncOneCartoon InsertMedia failed", log.Any("media", mediaTemp.ID), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
if len(contents) > 0 {
|
||||
_ = mediacontentmod.InsetBatch(nil, contents)
|
||||
}
|
||||
return hit, nil
|
||||
}
|
||||
|
||||
// syncExistingCartoonH265 允许重复导入老司机动漫时回填历史章节的 H.265 地址。
|
||||
// 只接受上游非空地址,不改动现有 H.264 地址,也不会用空值覆盖已入库资源。
|
||||
func syncExistingCartoonH265(mediaID primitive.ObjectID, links []laosiji.LinkInfo, manager string) error {
|
||||
updatedIDs := make([]primitive.ObjectID, 0)
|
||||
for _, chapter := range links {
|
||||
h265URL := strings.TrimSpace(chapter.Hevc_m3u8_url)
|
||||
if h265URL == "" || strings.TrimSpace(chapter.Id) == "" {
|
||||
continue
|
||||
}
|
||||
content, err := mediacontentmod.GetInfoByHashId(mediaID, chapter.Id)
|
||||
if err == mediacontentmod.MediaContentNotFound {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(content.H265Url) == h265URL {
|
||||
continue
|
||||
}
|
||||
if _, err = mediacontentmod.UpdateByID(nil, content.ID, map[string]interface{}{
|
||||
"h265Url": h265URL,
|
||||
"updatedAct": manager,
|
||||
"updateTime": time.Now(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
updatedIDs = append(updatedIDs, content.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
package laosijiser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/crypt"
|
||||
"91porn-server/common/httputil"
|
||||
"91porn-server/common/laosiji"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/web/webg"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func NovelSearch(ctx context.Context, param laosiji.NovelSearchListReq) (resp laosiji.NovelSearchListResp, err error) {
|
||||
novelResp, err := laosiji.NovelSearch(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("MovieSearch", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp = laosiji.NovelSearchListResp{
|
||||
Data: make([]laosiji.NovelSearchInfo, 0),
|
||||
Total: novelResp.Total,
|
||||
Current_page: novelResp.Current_page,
|
||||
Page_size: novelResp.Page_size,
|
||||
Last_page: novelResp.Last_page,
|
||||
}
|
||||
//是否导入
|
||||
taskIds := make([]string, 0)
|
||||
for _, v := range novelResp.Data {
|
||||
taskIds = append(taskIds, v.Id)
|
||||
}
|
||||
novelList, _ := mediamod.GetListByLsjIds(taskIds)
|
||||
|
||||
// 处理域名
|
||||
for _, novelInfo := range novelResp.Data {
|
||||
//novelInfo.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, novelInfo.Img)
|
||||
novelInfo.Img = common.JoinUrlPath("laosiji", novelInfo.Img)
|
||||
|
||||
for _, novelIn := range novelList {
|
||||
if novelIn.LsjId == novelInfo.Id {
|
||||
novelInfo.IsAdd = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resp.Data = append(resp.Data, novelInfo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NovelDetail(ctx context.Context, param laosiji.NovelDetailReq) (resp laosiji.NovelDetailResp, err error) {
|
||||
resp, err = laosiji.NovelDetail(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("MovieSearch", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 处理域名
|
||||
//resp.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, resp.Img)
|
||||
resp.Img = common.JoinUrlPath("laosiji", resp.Img)
|
||||
if resp.Chapter != nil && len(resp.Chapter) > 0 {
|
||||
for i, cha := range resp.Chapter {
|
||||
if len(cha.Img) > 0 {
|
||||
//resp.Chapter[i].Img = common.JoinUrlPath(laosiji.IMAGEYUAN, resp.Chapter[i].Img)
|
||||
resp.Chapter[i].Img = common.JoinUrlPath("laosiji", resp.Chapter[i].Img)
|
||||
}
|
||||
if len(cha.Content) > 0 {
|
||||
if cha.Is_audio == "y" {
|
||||
resp.Chapter[i].Content = common.JoinUrlPath("laosiji/m3m", resp.Chapter[i].Content)
|
||||
} else {
|
||||
resp.Chapter[i].Content = common.JoinUrlPath("laosiji", resp.Chapter[i].Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NovelAddList(ctx context.Context, param laosiji.NovelAddListReq, manager string) (err error) {
|
||||
if param.Ids == nil || len(param.Ids) <= 0 {
|
||||
return
|
||||
}
|
||||
novelDetails := make([]laosiji.NovelDetailResp, 0)
|
||||
for _, id := range param.Ids {
|
||||
var novelDetail laosiji.NovelDetailResp
|
||||
req := laosiji.NovelDetailReq{
|
||||
Id: id,
|
||||
}
|
||||
novelDetail, err = laosiji.NovelDetail(ctx, req)
|
||||
if err != nil {
|
||||
log.Warn("NovelAddList laosiji.NovelDetail failed", log.Any("id", id), log.E(err))
|
||||
continue
|
||||
}
|
||||
// 处理域名
|
||||
//novelDetail.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, novelDetail.Img)
|
||||
novelDetail.Img = common.JoinUrlPath("laosiji/", novelDetail.Img)
|
||||
if novelDetail.Chapter != nil && len(novelDetail.Chapter) > 0 {
|
||||
for i, cha := range novelDetail.Chapter {
|
||||
if len(cha.Img) > 0 {
|
||||
novelDetail.Chapter[i].Img = common.JoinUrlPath("laosiji/", novelDetail.Chapter[i].Img)
|
||||
}
|
||||
if len(cha.Content) > 0 {
|
||||
if cha.Is_audio == "y" {
|
||||
return errors.New("暂不支持有声小说!!")
|
||||
//novelDetail.Chapter[i].Content = common.JoinUrlPath("laosiji/m3m", novelDetail.Chapter[i].Content)
|
||||
} else {
|
||||
novelDetail.Chapter[i].Content = common.JoinUrlPath("laosiji", novelDetail.Chapter[i].Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
novelDetails = append(novelDetails, novelDetail)
|
||||
}
|
||||
if len(novelDetails) <= 0 {
|
||||
log.Warn("NovelAddList laosiji.NovelDetailResp len is 0")
|
||||
err = errors.New("laosiji.NovelDetailResp len is 0")
|
||||
return
|
||||
}
|
||||
// 批量处理标签
|
||||
tagMaps := make(map[string]primitive.ObjectID)
|
||||
tagList := make([]string, 0) // 减少重复循环
|
||||
tagList2 := make([]string, 0) // 减少重复循环
|
||||
tagTmpMap := map[int]map[string]any{ // 区分类别和去重
|
||||
3: make(map[string]any), // 文本
|
||||
4: make(map[string]any), // 有声
|
||||
}
|
||||
for _, comicsDetail := range novelDetails {
|
||||
for _, tag := range comicsDetail.Tags {
|
||||
if len(comicsDetail.Chapter) > 0 && comicsDetail.Chapter[0].Is_audio == "y" {
|
||||
if _, ok := tagTmpMap[4][tag.Name]; !ok {
|
||||
tagTmpMap[4][tag.Name] = nil
|
||||
tagList = append(tagList, tag.Name)
|
||||
}
|
||||
} else {
|
||||
if _, ok := tagTmpMap[3][tag.Name]; !ok {
|
||||
tagTmpMap[3][tag.Name] = nil
|
||||
tagList2 = append(tagList, tag.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(tagTmpMap[3]) > 0 {
|
||||
tagMaps, err = checkInsertMediaTag(tagList, 3)
|
||||
if err != nil {
|
||||
log.Error("NovelAddList checkInsertMediaTag failed", log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(tagTmpMap[4]) > 0 {
|
||||
tagMaps2, err := checkInsertMediaTag(tagList2, 4)
|
||||
if err != nil {
|
||||
log.Error("NovelAddList checkInsertMediaTag failed", log.E(err))
|
||||
return err
|
||||
}
|
||||
for k, id := range tagMaps2 {
|
||||
tagMaps[k] = id
|
||||
}
|
||||
}
|
||||
|
||||
failedIds := make([]string, 0)
|
||||
wg := sync.WaitGroup{}
|
||||
for index, _ := range novelDetails {
|
||||
wg.Add(1)
|
||||
common.GoParam(index, func(i int) {
|
||||
defer wg.Done()
|
||||
comicsDetail := novelDetails[i]
|
||||
err = syncOneNovel(comicsDetail, tagMaps, manager)
|
||||
if err != nil {
|
||||
log.Warn("NovelAddList syncOneNovel failed", log.Any("id", comicsDetail.Id), log.E(err))
|
||||
failedIds = append(failedIds, comicsDetail.Id)
|
||||
}
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
if len(failedIds) > 0 {
|
||||
err = errors.New(fmt.Sprintf("需要同步%d部小说,失败%d,失败Id[%s]", len(param.Ids), len(failedIds), strings.Join(failedIds, ",")))
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func syncOneNovel(novelDetail laosiji.NovelDetailResp, tagMaps map[string]primitive.ObjectID, manager string) error {
|
||||
now := time.Now()
|
||||
mediaTemp, err := mediamod.GetListByLsjId(novelDetail.Id)
|
||||
if err != nil {
|
||||
log.Warn("syncOneNovel GetListByLsjId failed", log.Any("id", novelDetail.Id), log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
if !mediaTemp.ID.IsZero() {
|
||||
return nil
|
||||
}
|
||||
mediaTemp = mediamod.Media{
|
||||
ID: primitive.NewObjectID(),
|
||||
LsjId: novelDetail.Id,
|
||||
Title: novelDetail.Name,
|
||||
HorizontalCover: novelDetail.Img,
|
||||
VerticalCover: novelDetail.Img,
|
||||
Tags: make([]primitive.ObjectID, 0),
|
||||
TagDetails: make([]mediamod.TagDetail, 0),
|
||||
Summary: novelDetail.Description,
|
||||
FreeEpisode: 0,
|
||||
CurrentEpisode: 0,
|
||||
MediaType: mediamod.MediaTypeText,
|
||||
Kind: 7,
|
||||
Anchor: novelDetail.Author,
|
||||
PermissionIconHide: false,
|
||||
Permission: 2,
|
||||
Price: 0,
|
||||
ContentsPrice: 0,
|
||||
Status: 1,
|
||||
CreatedAt: now,
|
||||
UpdateTime: now,
|
||||
UpdatedAct: manager,
|
||||
}
|
||||
isAudo := false
|
||||
mediaTemp.TotalEpisode, _ = strconv.Atoi(novelDetail.Chapter_count)
|
||||
mediaTemp.UpdateStatus, _ = strconv.Atoi(novelDetail.Update_status)
|
||||
mediaTemp.Price, _ = strconv.ParseInt(novelDetail.Money, 10, 64)
|
||||
if mediaTemp.Price > 0 {
|
||||
mediaTemp.Permission = 1
|
||||
}
|
||||
//if mediaTemp.UpdateStatus == 1 {
|
||||
// mediaTemp.UpdatedDesc = fmt.Sprintf("已完结%d话", mediaTemp.TotalEpisode)
|
||||
//} else {
|
||||
// mediaTemp.UpdatedDesc = fmt.Sprintf("更新至%d话", mediaTemp.TotalEpisode)
|
||||
//}
|
||||
for _, tag := range novelDetail.Tags {
|
||||
if v, ok := tagMaps[tag.Name]; ok {
|
||||
mediaTemp.TagDetails = append(mediaTemp.TagDetails, mediamod.TagDetail{
|
||||
Id: v,
|
||||
Name: tag.Name,
|
||||
})
|
||||
mediaTemp.Tags = append(mediaTemp.Tags, v)
|
||||
}
|
||||
}
|
||||
contents := make([]mediacontentmod.MediaContent, 0)
|
||||
if len(novelDetail.Chapter) > 0 {
|
||||
for i, chapter := range novelDetail.Chapter {
|
||||
mediaContent := mediacontentmod.MediaContent{
|
||||
ID: primitive.NewObjectID(),
|
||||
MediaID: mediaTemp.ID,
|
||||
EpisodeNumber: i + 1,
|
||||
ListenPermission: mediaTemp.Permission,
|
||||
Price: mediaTemp.Price,
|
||||
Name: fmt.Sprintf("第%d章", i+1),
|
||||
UrlSet: make([]string, 0),
|
||||
Status: 1,
|
||||
IsActive: true,
|
||||
Author: mediaTemp.Anchor,
|
||||
IsDelete: false,
|
||||
UpdatedAct: manager,
|
||||
CreatedAt: now,
|
||||
UpdateTime: now,
|
||||
HashId: chapter.Id,
|
||||
}
|
||||
if chapter.Is_audio == "y" {
|
||||
mediaContent.AudioUrl = chapter.Content
|
||||
isAudo = true
|
||||
} else {
|
||||
content, err := textNovelContent(chapter.Content)
|
||||
if err != nil {
|
||||
log.Warn("syncOneNovel textNovelContent failed", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
mediaContent.Text = content
|
||||
}
|
||||
contents = append(contents, mediaContent)
|
||||
}
|
||||
}
|
||||
|
||||
if isAudo {
|
||||
mediaTemp.MediaSubType = 1
|
||||
}
|
||||
_, err = mediamod.Insert(nil, mediaTemp)
|
||||
if err != nil {
|
||||
log.Warn("syncOneNovel InsertMedia failed", log.Any("media", mediaTemp.ID), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
if len(contents) > 0 {
|
||||
_ = mediacontentmod.InsetBatch(nil, contents)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func textNovelContent(url string) (string, error) {
|
||||
decKey := "525202f9149e061d"
|
||||
path := fmt.Sprintf("%s/%s", webg.Conf.URL.LaoSiJiTSCdn, url)
|
||||
resp, err := httputil.DefaultClientGet(path, make(map[string]string))
|
||||
if err != nil {
|
||||
log.Warn("textNovelContent http client get failed", log.Any("url", path), log.Any("err", err))
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Warn("textNovelContent read response body failed", log.Any("err", err))
|
||||
return "", err
|
||||
}
|
||||
b := crypt.AESECBDecrypt(body, []byte(decKey))
|
||||
return string(b), nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package laosijiser
|
||||
|
||||
import (
|
||||
"91porn-server/common/crypt"
|
||||
"91porn-server/common/httputil"
|
||||
"91porn-server/common/log"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNovelDetail(t *testing.T) {
|
||||
url := "laosiji/media/novel/cbd/cbd5b05ea5677797958895694211582c.bnc"
|
||||
key := "525202f9149e061d"
|
||||
domain := "https://tssdaa.hlvwcn.cn"
|
||||
//req, err := http.NewRequest("GET", url, nil)
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
// return
|
||||
//}
|
||||
resp, err := httputil.ClientGet(30, fmt.Sprintf("%s/%s", domain, url), make(map[string]string))
|
||||
if err != nil {
|
||||
log.Warn("http client get failed", log.Any("err", err))
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Warn("read response body failed", log.Any("err", err))
|
||||
return
|
||||
}
|
||||
b := crypt.AESECBDecrypt(body, []byte(key))
|
||||
log.Info(string(b))
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package laosijiser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/laosiji"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
"91porn-server/web/service/vidser"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func PostSearch(ctx context.Context, param laosiji.PostSearchListReq) (resp laosiji.PostSearchListResp, err error) {
|
||||
postResp, err := laosiji.PostSearch(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("MovieSearch", log.E(err))
|
||||
return
|
||||
}
|
||||
resp = laosiji.PostSearchListResp{
|
||||
Data: make([]laosiji.PostSearchInfo, 0),
|
||||
Total: postResp.Total,
|
||||
Current_page: postResp.Current_page,
|
||||
Page_size: postResp.Page_size,
|
||||
Last_page: postResp.Last_page,
|
||||
}
|
||||
|
||||
//是否导入
|
||||
taskIds := make([]string, 0)
|
||||
for _, v := range postResp.Data {
|
||||
taskIds = append(taskIds, v.Id)
|
||||
}
|
||||
postList, _ := vidmod.GetList(bson.M{"sourceID": bson.M{"$in": taskIds}})
|
||||
|
||||
// 处理域名
|
||||
for _, postInfo := range postResp.Data {
|
||||
//postInfo.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, postInfo.Img)
|
||||
postInfo.Img = common.JoinUrlPath("laosiji", postInfo.Img)
|
||||
|
||||
for _, postIn := range postList {
|
||||
if postIn.SourceID == postInfo.Id {
|
||||
postInfo.IsAdd = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resp.Data = append(resp.Data, postInfo)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
func PostDetail(ctx context.Context, param laosiji.PostDetailReq) (resp laosiji.PostDetailResp, err error) {
|
||||
resp, err = laosiji.PostDetail(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("MovieSearch", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 处理域名
|
||||
//resp.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, resp.Img)
|
||||
resp.Img = common.JoinUrlPath("laosiji", resp.Img)
|
||||
if resp.Files != nil && len(resp.Files) > 0 {
|
||||
for i, file := range resp.Files {
|
||||
if len(file.Image) > 0 {
|
||||
//resp.Files[i].Image = common.JoinUrlPath(laosiji.IMAGEYUAN, file.Image)
|
||||
resp.Files[i].Image = common.JoinUrlPath("laosiji", file.Image)
|
||||
}
|
||||
if len(file.Video_link) > 0 {
|
||||
resp.Files[i].Video_link = common.JoinUrlPath(laosiji.APIUrl+"/m3m", file.Video_link)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PostAddList(ctx context.Context, param laosiji.PostAddListReq, manager string) (hits []vidser.SensitiveHit, err error) {
|
||||
if param.Ids == nil || len(param.Ids) <= 0 {
|
||||
return
|
||||
}
|
||||
// 批量查询所有详情
|
||||
postDetails := make([]laosiji.PostDetailResp, 0)
|
||||
for _, id := range param.Ids {
|
||||
var postDetail laosiji.PostDetailResp
|
||||
req := laosiji.PostDetailReq{
|
||||
Id: id,
|
||||
}
|
||||
postDetail, err = laosiji.PostDetail(ctx, req)
|
||||
if err != nil {
|
||||
log.Warn("PostAddList laosiji.PostDetail failed", log.Any("id", id), log.E(err))
|
||||
continue
|
||||
}
|
||||
//postDetail.Img = common.JoinUrlPath(laosiji.IMAGEYUAN, postDetail.Img)
|
||||
postDetail.Img = common.JoinUrlPath("laosiji", postDetail.Img)
|
||||
if postDetail.Files != nil && len(postDetail.Files) > 0 {
|
||||
for i, file := range postDetail.Files {
|
||||
if len(file.Image) > 0 {
|
||||
postDetail.Files[i].Image = common.JoinUrlPath("laosiji", file.Image)
|
||||
}
|
||||
if len(file.Video_link) > 0 {
|
||||
postDetail.Files[i].Video_link = common.JoinUrlPath("laosiji/m3m/", file.Video_link)
|
||||
}
|
||||
}
|
||||
}
|
||||
postDetails = append(postDetails, postDetail)
|
||||
}
|
||||
if len(postDetails) <= 0 {
|
||||
log.Warn("PostAddList laosiji.PostDetailResp len is 0")
|
||||
err = errors.New("laosiji.PostDetailResp len is 0")
|
||||
return
|
||||
}
|
||||
|
||||
users, err := usermod.FindMany(bson.M{"uid": bson.M{"$gt": 300000}}, &options.FindOptions{}, 0, 300)
|
||||
if err != nil {
|
||||
log.Warn("PostAddList FindManyUser failed", log.E(err))
|
||||
return
|
||||
}
|
||||
terms := vidser.LoadEnabledSensitiveTerms()
|
||||
hits = make([]vidser.SensitiveHit, 0)
|
||||
failedIds := make([]string, 0)
|
||||
for _, postDetail := range postDetails {
|
||||
hit, oneErr := addOnePost(postDetail, nil, users, manager, terms)
|
||||
if oneErr != nil {
|
||||
log.Warn("PostAddList addOnePost failed", log.Any("id", postDetail.Id), log.E(oneErr))
|
||||
failedIds = append(failedIds, postDetail.Id)
|
||||
continue
|
||||
}
|
||||
if hit != nil {
|
||||
hits = append(hits, *hit)
|
||||
}
|
||||
}
|
||||
if len(failedIds) > 0 {
|
||||
err = errors.New(fmt.Sprintf("需要同步%d个帖子,失败%d,失败Id[%s]", len(param.Ids), len(failedIds), strings.Join(failedIds, ",")))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addOnePost(postDetail laosiji.PostDetailResp, tagMaps map[string]primitive.ObjectID, upUsers []*usermod.User, manager string, terms []string) (*vidser.SensitiveHit, error) {
|
||||
//
|
||||
// 有的帖子是图文节点 前端格式不支持 这里屏蔽
|
||||
//
|
||||
if len(postDetail.Files) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
postList, err := vidmod.GetList(bson.M{"sourceID": bson.M{"$in": []string{postDetail.Id}}})
|
||||
if err != nil {
|
||||
log.Warn("addOnePost GetList failed", log.Any("id", postDetail.Id), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
if postList != nil && len(postList) > 0 {
|
||||
return nil, nil
|
||||
}
|
||||
postInfo := vidmod.VideoModel{
|
||||
ID: primitive.NewObjectID(),
|
||||
NewsType: vidmod.COVER,
|
||||
Title: postDetail.Title,
|
||||
Content: postDetail.Content,
|
||||
Tags: make([]primitive.ObjectID, 0),
|
||||
MimeType: "video/mp4",
|
||||
Cover: postDetail.Img,
|
||||
CoverThumb: postDetail.Img,
|
||||
SeriesCover: make([]string, 0),
|
||||
Via: "laosiji",
|
||||
Status: vidmod.CheckPass,
|
||||
Location: primitive.ObjectID{},
|
||||
NewUpdatedAt: "laosiji",
|
||||
ReviewAt: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ReviewAccount: manager,
|
||||
SourceID: postDetail.Id,
|
||||
VerticalCover: postDetail.Img,
|
||||
}
|
||||
postInfo.SeriesNum, _ = strconv.Atoi(postDetail.Img_count)
|
||||
postInfo.Coins, _ = strconv.ParseInt(postDetail.Money, 10, 64)
|
||||
uIndex := rand.Intn(len(upUsers))
|
||||
u := upUsers[uIndex]
|
||||
postInfo.PublisherID = u.UID
|
||||
//if postInfo.Coins > 0 {
|
||||
// postInfo.Permission = 1
|
||||
//} else {
|
||||
// postInfo.Permission = 2
|
||||
// postInfo.FreeArea = true
|
||||
// postInfo.FreeAreaDate = now
|
||||
//}
|
||||
//节点 处理
|
||||
if postDetail.Files != nil && len(postDetail.Files) > 0 {
|
||||
for _, file := range postDetail.Files {
|
||||
if file.Type == "image" {
|
||||
if len(file.Image) > 0 {
|
||||
postInfo.SeriesCover = append(postInfo.SeriesCover, file.Image)
|
||||
}
|
||||
}
|
||||
if file.Type == "video" {
|
||||
postInfo.SourceURL = file.Video_link
|
||||
postInfo.PlayTime = 1
|
||||
postInfo.Filename = file.Tips
|
||||
postInfo.SourceID = postDetail.Id
|
||||
}
|
||||
}
|
||||
}
|
||||
if postDetail.Rich_content != nil && len(postDetail.Rich_content) > 0 {
|
||||
for _, file := range postDetail.Rich_content {
|
||||
postInfo.RichText += fmt.Sprintf("%s\n", file)
|
||||
}
|
||||
}
|
||||
// 敏感词命中 → 强制为待审核(WaitingCheck=0),写库后由后台逐条告警
|
||||
hit := vidser.CheckTextHits(terms, postDetail.Id, postInfo.Title, postInfo.Content, postInfo.RichText)
|
||||
if hit != nil {
|
||||
postInfo.Status = vidmod.WaitingCheck
|
||||
log.Warn("addOnePost sensitive hit, forced offline",
|
||||
log.Any("sourceId", hit.SourceID),
|
||||
log.Any("title", hit.Title),
|
||||
log.Any("detail", hit.Detail))
|
||||
}
|
||||
if _, err = vidmod.InsertBase(postInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hit, nil
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
package laosijiser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/constant/redisconst"
|
||||
"91porn-server/common/laosiji"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/tagmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
"91porn-server/web/service/vidser"
|
||||
"91porn-server/web/webg"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func MovieSearch(ctx context.Context, param laosiji.MovieSearchReq) (resp laosiji.MovieSearchResp, err error) {
|
||||
var yuanresp laosiji.MovieSearchResp
|
||||
yuanresp, err = laosiji.MovieSearch(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("MovieSearch", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
//是否导入
|
||||
taskIds := make([]string, 0)
|
||||
for _, v := range yuanresp.Data {
|
||||
taskIds = append(taskIds, v.Id)
|
||||
}
|
||||
resp = laosiji.MovieSearchResp{
|
||||
Data: make([]laosiji.MovieInfo, 0),
|
||||
Total: yuanresp.Total,
|
||||
Current_page: yuanresp.Current_page,
|
||||
Page_size: yuanresp.Page_size,
|
||||
Last_page: yuanresp.Last_page,
|
||||
}
|
||||
if param.Position == "guochan" || param.Position == "av" || param.Position == "douyin" || param.Position == "dark" {
|
||||
videoList, _ := vidmod.GetList(bson.M{"sourceID": bson.M{"$in": taskIds}})
|
||||
// 处理域名
|
||||
for _, movieInfo := range yuanresp.Data {
|
||||
//movieInfo.Img_x = common.JoinUrlPath(laosiji.IMAGEYUAN, movieInfo.Img_x)
|
||||
//movieInfo.Img_y = common.JoinUrlPath(laosiji.IMAGEYUAN, movieInfo.Img_y)
|
||||
movieInfo.Img_x = common.JoinUrlPath("laosiji", movieInfo.Img_x)
|
||||
movieInfo.Img_y = common.JoinUrlPath("laosiji", movieInfo.Img_y)
|
||||
|
||||
for _, videoInfo := range videoList {
|
||||
if videoInfo.SourceID == movieInfo.Id {
|
||||
movieInfo.IsAdd = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resp.Data = append(resp.Data, movieInfo)
|
||||
}
|
||||
} else if param.Position == "cartoon" {
|
||||
mediaList, _ := mediamod.GetListByLsjIds(taskIds)
|
||||
for _, movieInfo := range yuanresp.Data {
|
||||
//movieInfo.Img_x = common.JoinUrlPath(laosiji.IMAGEYUAN, movieInfo.Img_x)
|
||||
//movieInfo.Img_y = common.JoinUrlPath(laosiji.IMAGEYUAN, movieInfo.Img_y)
|
||||
movieInfo.Img_x = common.JoinUrlPath("laosiji", movieInfo.Img_x)
|
||||
movieInfo.Img_y = common.JoinUrlPath("laosiji", movieInfo.Img_y)
|
||||
for _, videoInfo := range mediaList {
|
||||
if videoInfo.LsjId == movieInfo.Id {
|
||||
movieInfo.IsAdd = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
resp.Data = append(resp.Data, movieInfo)
|
||||
}
|
||||
|
||||
} else {
|
||||
err = errors.New("该资源暂不支持")
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func MovieDetail(ctx context.Context, param laosiji.MovieDetailReq) (resp laosiji.MovieDetailResp, err error) {
|
||||
resp, err = laosiji.MovieDetail(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("MovieSearch", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 处理域名
|
||||
//resp.Img_x = common.JoinUrlPath(laosiji.IMAGEYUAN, resp.Img_x)
|
||||
//resp.Img_y = common.JoinUrlPath(laosiji.IMAGEYUAN, resp.Img_y)
|
||||
resp.Img_x = common.JoinUrlPath("laosiji", resp.Img_x)
|
||||
resp.Img_y = common.JoinUrlPath("laosiji", resp.Img_y)
|
||||
for key, value := range resp.Preview_images {
|
||||
//resp.Preview_images[key] = common.JoinUrlPath(laosiji.IMAGEYUAN, value)
|
||||
resp.Preview_images[key] = common.JoinUrlPath("laosiji", value)
|
||||
}
|
||||
for key, value := range resp.Links {
|
||||
resp.Links[key].M3u8_url = laosiji.MovieM3u8OriginURL(value.M3u8_url)
|
||||
resp.Links[key].Preview_m3u8_url = laosiji.MovieM3u8OriginURL(value.Preview_m3u8_url)
|
||||
resp.Links[key].Hevc_m3u8_url = laosiji.MovieM3u8OriginURL(value.Hevc_m3u8_url)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func MovieAddList(ctx context.Context, param laosiji.MovieAddListReq, manager string) ([]vidser.SensitiveHit, error) {
|
||||
if param.Ids == nil || len(param.Ids) <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
movieDetails := make([]laosiji.MovieDetailResp, 0)
|
||||
for _, id := range param.Ids {
|
||||
var movieDetail laosiji.MovieDetailResp
|
||||
req := laosiji.MovieDetailReq{
|
||||
Id: id,
|
||||
}
|
||||
movieDetail, err := laosiji.MovieDetail(ctx, req)
|
||||
if err != nil {
|
||||
log.Warn("MovieAddList laosiji.MovieDetail failed", log.Any("id", id), log.E(err))
|
||||
continue
|
||||
}
|
||||
for i, linkInfo := range movieDetail.Links {
|
||||
linkInfo.M3u8_url = laosiji.MovieM3u8SourcePath(linkInfo.M3u8_url)
|
||||
linkInfo.Preview_m3u8_url = laosiji.MovieM3u8SourcePath(linkInfo.Preview_m3u8_url)
|
||||
linkInfo.Hevc_m3u8_url = laosiji.MovieM3u8SourcePath(linkInfo.Hevc_m3u8_url)
|
||||
movieDetail.Links[i] = linkInfo
|
||||
}
|
||||
if len(movieDetail.Img_y) > 0 {
|
||||
movieDetail.Img_y = common.JoinUrlPath("laosiji/", movieDetail.Img_y)
|
||||
}
|
||||
if len(movieDetail.Img_x) > 0 {
|
||||
movieDetail.Img_x = common.JoinUrlPath("laosiji/", movieDetail.Img_x)
|
||||
}
|
||||
movieDetails = append(movieDetails, movieDetail)
|
||||
}
|
||||
if len(movieDetails) <= 0 {
|
||||
log.Warn("MovieAddList laosiji.MovieDetailResp len is 0")
|
||||
return nil, errors.New("laosiji.MovieDetailResp len is 0")
|
||||
}
|
||||
// 批量处理标签
|
||||
tags := make(map[string]struct{})
|
||||
for _, comicsDetail := range movieDetails {
|
||||
if comicsDetail.Tags != nil && len(comicsDetail.Tags) > 0 {
|
||||
for _, tag := range comicsDetail.Tags {
|
||||
tags[tag.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
terms := vidser.LoadEnabledSensitiveTerms()
|
||||
hits := make([]vidser.SensitiveHit, 0)
|
||||
if param.Position == "cartoon" {
|
||||
return syncCartoonList(movieDetails, tags, manager, terms)
|
||||
} else if param.Position == "av" || param.Position == "guochan" || param.Position == "douyin" || param.Position == "dark" {
|
||||
tagMaps := make(map[string]primitive.ObjectID)
|
||||
var err error
|
||||
if len(tags) > 0 {
|
||||
tagList := make([]string, 0)
|
||||
for k, _ := range tags {
|
||||
tagList = append(tagList, k)
|
||||
}
|
||||
tagMaps, err = checkInsertVidTags(tagList)
|
||||
if err != nil {
|
||||
log.Error("MovieAddList checkInsertVidTags failed", log.E(err))
|
||||
return hits, err
|
||||
}
|
||||
}
|
||||
users, err := usermod.FindMany(bson.M{"uid": bson.M{"$gt": 300000}}, &options.FindOptions{}, 0, 300)
|
||||
if err != nil {
|
||||
log.Warn("PostAddList FindManyUser failed", log.E(err))
|
||||
return hits, err
|
||||
}
|
||||
failedIds := make([]string, 0)
|
||||
for _, comicsDetail := range movieDetails {
|
||||
hit, oneErr := syncOneMovie(comicsDetail, tagMaps, users, manager, terms)
|
||||
if oneErr != nil {
|
||||
log.Warn("MovieAddList syncOneMovie failed", log.Any("id", comicsDetail.Id), log.E(oneErr))
|
||||
failedIds = append(failedIds, comicsDetail.Id)
|
||||
continue
|
||||
}
|
||||
if hit != nil {
|
||||
hits = append(hits, *hit)
|
||||
}
|
||||
}
|
||||
if len(failedIds) > 0 {
|
||||
return hits, errors.New(fmt.Sprintf("需要同步%d部资源,失败%d,失败Id[%s]", len(param.Ids), len(failedIds), strings.Join(failedIds, ",")))
|
||||
}
|
||||
return hits, nil
|
||||
}
|
||||
return hits, errors.New("该资源暂不支持")
|
||||
}
|
||||
|
||||
func syncOneMovie(movieDetail laosiji.MovieDetailResp, tags map[string]primitive.ObjectID, upUsers []*usermod.User, manager string, terms []string) (*vidser.SensitiveHit, error) {
|
||||
now := time.Now()
|
||||
postList, err := vidmod.GetList(bson.M{"sourceID": bson.M{"$in": []string{movieDetail.Id}}})
|
||||
if err != nil {
|
||||
log.Warn("syncOneMovie GetList failed", log.Any("id", movieDetail.Id), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
if postList != nil && len(postList) > 0 {
|
||||
h265URL := movieH265SourceURL(movieDetail.Links)
|
||||
if h265URL == "" {
|
||||
return nil, nil
|
||||
}
|
||||
video := postList[0]
|
||||
if err = vidmod.MarkH265Success(video.ID, h265URL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if webg.Redis != nil {
|
||||
cacheKey := fmt.Sprintf(redisconst.VideoInfoKey(), video.ID.Hex())
|
||||
if _, err = webg.Redis.Del(cacheKey); err != nil {
|
||||
log.Warn("syncOneMovie clear video cache failed",
|
||||
log.Any("id", video.ID),
|
||||
log.Any("cacheKey", cacheKey),
|
||||
log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
vidInfo := vidmod.VideoModel{
|
||||
ID: primitive.NewObjectID(),
|
||||
Title: movieDetail.Name,
|
||||
Content: movieDetail.Description,
|
||||
Tags: make([]primitive.ObjectID, 0),
|
||||
MimeType: "video/mp4",
|
||||
SeriesCover: make([]string, 0),
|
||||
Via: "laosiji",
|
||||
Status: vidmod.CheckPass,
|
||||
Location: primitive.ObjectID{},
|
||||
Actor: movieDetail.Actor,
|
||||
NewUpdatedAt: "laosiji",
|
||||
ReviewAt: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ReviewAccount: manager,
|
||||
VerticalCover: movieDetail.Img_y,
|
||||
}
|
||||
|
||||
if movieDetail.Img_type == "short" && len(movieDetail.Img_y) > 0 {
|
||||
vidInfo.NewsType = vidmod.SHORT
|
||||
vidInfo.Cover = movieDetail.Img_y
|
||||
vidInfo.CoverThumb = movieDetail.Img_y
|
||||
vidInfo.Direction = "vertical"
|
||||
} else {
|
||||
vidInfo.NewsType = vidmod.SP
|
||||
vidInfo.Cover = movieDetail.Img_x
|
||||
vidInfo.CoverThumb = movieDetail.Img_x
|
||||
vidInfo.Direction = "horizontal"
|
||||
}
|
||||
playTime, _ := strconv.Atoi(movieDetail.Duration)
|
||||
vidInfo.PlayTime = uint(playTime)
|
||||
uIndex := rand.Intn(len(upUsers))
|
||||
u := upUsers[uIndex]
|
||||
vidInfo.PublisherID = u.UID
|
||||
if movieDetail.Links != nil && len(movieDetail.Links) > 0 {
|
||||
vidInfo.SourceURL = movieDetail.Links[0].M3u8_url
|
||||
vidInfo.Filename = movieDetail.Links[0].Name
|
||||
vidInfo.PreviewURL = movieDetail.Links[0].Preview_m3u8_url
|
||||
vidInfo.H265Url = movieH265SourceURL(movieDetail.Links)
|
||||
if vidInfo.H265Url != "" {
|
||||
vidInfo.H265Status = vidmod.H265StatusSuccess
|
||||
}
|
||||
}
|
||||
vidInfo.SourceID = movieDetail.Id
|
||||
|
||||
if movieDetail.Tags != nil && len(movieDetail.Tags) > 0 {
|
||||
for _, tag := range movieDetail.Tags {
|
||||
if k, ok := tags[tag.Name]; ok {
|
||||
vidInfo.Tags = append(vidInfo.Tags, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 敏感词命中 → 强制为待审核(WaitingCheck=0)
|
||||
hit := vidser.CheckTextHits(terms, movieDetail.Id, vidInfo.Title, vidInfo.Content, "")
|
||||
if hit != nil {
|
||||
vidInfo.Status = vidmod.WaitingCheck
|
||||
log.Warn("syncOneMovie sensitive hit, forced offline",
|
||||
log.Any("sourceId", hit.SourceID),
|
||||
log.Any("title", hit.Title),
|
||||
log.Any("detail", hit.Detail))
|
||||
}
|
||||
if vidInfo.H265Url == "" && vidInfo.SourceURL != "" && vidInfo.Status == vidmod.CheckPass && vidInfo.NewsType == vidmod.SP {
|
||||
vidInfo.H265Status = vidmod.H265StatusQueued
|
||||
vidInfo.H265QueuedAt = now
|
||||
}
|
||||
if _, err = vidmod.InsertBase(vidInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hit, nil
|
||||
}
|
||||
|
||||
// movieH265SourceURL 返回首个播放链接对应的 H.265 地址。
|
||||
// syncOneMovie 的 H.264 也固定使用 links[0],不能拿后续分集的 H.265 与首集混用。
|
||||
func movieH265SourceURL(links []laosiji.LinkInfo) string {
|
||||
if len(links) == 0 {
|
||||
return ""
|
||||
}
|
||||
return laosiji.MovieM3u8SourcePath(links[0].Hevc_m3u8_url)
|
||||
}
|
||||
|
||||
func checkInsertVidTags(names []string) (data map[string]vidmod.ObjectID, err error) {
|
||||
data = make(map[string]vidmod.ObjectID)
|
||||
tags, err := tagmod.FindOneTagByNames(names)
|
||||
if err != nil {
|
||||
log.Warn("checkInsertVidTags tagmod.FindOneTagByNames", log.E(err), log.Any("name", names))
|
||||
return
|
||||
}
|
||||
for _, tag := range tags {
|
||||
data[tag.TagName] = tag.ID
|
||||
}
|
||||
insertTags := make([]tagmod.Tag, 0)
|
||||
for _, tag := range tags {
|
||||
if _, ok := data[tag.TagName]; !ok {
|
||||
insertTag := tagmod.Tag{
|
||||
ID: primitive.NewObjectID(),
|
||||
TagName: tag.TagName,
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
insertTags = append(insertTags, insertTag)
|
||||
data[tag.TagName] = insertTag.ID
|
||||
}
|
||||
}
|
||||
if len(insertTags) > 0 {
|
||||
err = tagmod.InsertMany(insertTags)
|
||||
if err != nil {
|
||||
log.Warn("checkInsertVidTags InsertMany failed", log.Any("tags", insertTags), log.E(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package laosijiser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"91porn-server/common/laosiji"
|
||||
)
|
||||
|
||||
func TestMovieH265SourceURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
links []laosiji.LinkInfo
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
links: []laosiji.LinkInfo{
|
||||
{Hevc_m3u8_url: " "},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "does not mix a later episode with first H264 link",
|
||||
links: []laosiji.LinkInfo{
|
||||
{Hevc_m3u8_url: ""},
|
||||
{Hevc_m3u8_url: "https://cdn.example.com/m3m/movie/hevc.m3u8?token=ignored"},
|
||||
{Hevc_m3u8_url: "other.m3u8"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "first link normalized",
|
||||
links: []laosiji.LinkInfo{
|
||||
{Hevc_m3u8_url: "https://cdn.example.com/m3m/movie/hevc.m3u8?token=ignored"},
|
||||
{Hevc_m3u8_url: "other.m3u8"},
|
||||
},
|
||||
want: "laosiji/m3m/movie/hevc.m3u8",
|
||||
},
|
||||
{
|
||||
name: "already normalized",
|
||||
links: []laosiji.LinkInfo{
|
||||
{Hevc_m3u8_url: "laosiji/rk130/hevc/movie/index.m3u8"},
|
||||
},
|
||||
want: "laosiji/rk130/hevc/movie/index.m3u8",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := movieH265SourceURL(test.links); got != test.want {
|
||||
t.Fatalf("movieH265SourceURL() = %q, want %q", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package locationser
|
||||
|
||||
import (
|
||||
"91porn-server/models/v/locmod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// CityPages CityPages
|
||||
func CityPages(city string, page, size uint64) (locmod.CityPageResp, error) {
|
||||
var resp locmod.CityPageResp
|
||||
lModel, total, err := locmod.GetLocationList(city, page, size)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
lmodelLen := len(lModel)
|
||||
tids := make([]primitive.ObjectID, lmodelLen)
|
||||
for i, l := range lModel {
|
||||
tids[i] = l.ID
|
||||
}
|
||||
mPlayCnt, err := vidmod.GetCityPlayCount2Map(tids)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
mVideosCnt, _ := vidmod.GetCityPublishedVideoCount2Map(tids)
|
||||
resp.List = make([]locmod.LocInfo, lmodelLen)
|
||||
for i, l := range lModel {
|
||||
resp.List[i] = locmod.LocInfo{
|
||||
ID: l.ID.Hex(),
|
||||
City: l.City,
|
||||
Cover: l.Cover,
|
||||
Visit: l.Visit,
|
||||
FakeVisit: l.FakeVisit,
|
||||
SortKey: l.SortKey,
|
||||
CreatedAt: l.CreatedAt,
|
||||
VideoCount: mVideosCnt[l.ID],
|
||||
PlayCount: mPlayCnt[l.ID],
|
||||
}
|
||||
}
|
||||
resp.Total = int(total)
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package logser
|
||||
|
||||
import (
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/txnmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// MAP 别名
|
||||
type MAP map[string]interface{}
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
// GetRechargeGoldLog 获取充值金币日志
|
||||
func GetRechargeGoldLog(cond MAP, page, size uint64) (code stderr.Code, data interface{}) {
|
||||
infos, _, total, err := txnmod.GetCoinLogs(page, size, cond)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError, nil
|
||||
}
|
||||
return stderr.Success, txnmod.CoinLogResp{Logs: infos, Total: total}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package mediacontentser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
func TestValidateDramaEpisodeState(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
episodeNumber int
|
||||
listenPermission int
|
||||
price int64
|
||||
videoURL string
|
||||
active bool
|
||||
status int
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "free", episodeNumber: 1, listenPermission: 2, videoURL: "1.m3u8", active: true, status: 1},
|
||||
{name: "coin", episodeNumber: 2, listenPermission: 1, price: 30, videoURL: "2.m3u8", active: true, status: 1},
|
||||
{name: "free with price", episodeNumber: 1, listenPermission: 2, price: 30, wantErr: true},
|
||||
{name: "coin without price", episodeNumber: 2, listenPermission: 1, wantErr: true},
|
||||
{name: "active while transcoding", episodeNumber: 1, listenPermission: 2, videoURL: "1.m3u8", active: true, status: 0, wantErr: true},
|
||||
{name: "active without resource", episodeNumber: 1, listenPermission: 2, active: true, status: 1, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateDramaEpisodeState(tt.episodeNumber, tt.listenPermission, tt.price, "第1集", tt.videoURL, "", tt.active, tt.status)
|
||||
if gotErr := err != nil; gotErr != tt.wantErr {
|
||||
t.Fatalf("validateDramaEpisodeState() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDramaEpisodeStateAllowsNonContinuousFreeEpisodes(t *testing.T) {
|
||||
episodes := []struct {
|
||||
episodeNumber int
|
||||
listenPermission int
|
||||
price int64
|
||||
}{
|
||||
{episodeNumber: 2, listenPermission: 1, price: 30},
|
||||
{episodeNumber: 3, listenPermission: 2, price: 0},
|
||||
}
|
||||
for _, episode := range episodes {
|
||||
if err := validateDramaEpisodeState(
|
||||
episode.episodeNumber,
|
||||
episode.listenPermission,
|
||||
episode.price,
|
||||
"剧集",
|
||||
"episode.m3u8",
|
||||
"",
|
||||
true,
|
||||
1,
|
||||
); err != nil {
|
||||
t.Fatalf("episode %d validation failed: %v", episode.episodeNumber, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileStatusToEpisodeStatus(t *testing.T) {
|
||||
tests := map[string]int{
|
||||
vidmod.Converting: 0,
|
||||
vidmod.ConvertCompleted: 1,
|
||||
vidmod.Completed: 1,
|
||||
vidmod.ConvertError: 2,
|
||||
vidmod.MergeError: 2,
|
||||
}
|
||||
for input, want := range tests {
|
||||
if got := fileStatusToEpisodeStatus(input); got != want {
|
||||
t.Fatalf("fileStatusToEpisodeStatus(%q) = %d, want %d", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebBatchUpdateReqValidatedIDs(t *testing.T) {
|
||||
id := "507f1f77bcf86cd799439011"
|
||||
price := int64(30)
|
||||
permission := 1
|
||||
freeEpisode := 1
|
||||
req := WebBatchUpdateReq{
|
||||
IDS: []string{id, id},
|
||||
Price: &price,
|
||||
Permission: &permission,
|
||||
FreeEpisode: &freeEpisode,
|
||||
}
|
||||
ids, err := req.ValidatedIDs()
|
||||
if err != nil {
|
||||
t.Fatalf("ValidatedIDs() error: %v", err)
|
||||
}
|
||||
if len(ids) != 1 {
|
||||
t.Fatalf("ValidatedIDs() length = %d, want 1", len(ids))
|
||||
}
|
||||
if _, err = (&WebBatchUpdateReq{IDS: []string{id}}).ValidatedIDs(); err == nil {
|
||||
t.Fatal("request without update fields unexpectedly accepted")
|
||||
}
|
||||
req.IDS = []string{"bad-id"}
|
||||
if _, err = req.ValidatedIDs(); err == nil {
|
||||
t.Fatal("invalid ObjectID unexpectedly accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebBatchUpdateReqAppliesPricingWithoutIsActive(t *testing.T) {
|
||||
price := int64(30)
|
||||
permission := 1
|
||||
req := WebBatchUpdateReq{Price: &price, Permission: &permission}
|
||||
content := mediacontentmod.MediaContent{IsActive: true, Price: 10, ListenPermission: 2}
|
||||
|
||||
next := req.applyContentFields(content)
|
||||
if !next.IsActive {
|
||||
t.Fatal("isActive changed when omitted")
|
||||
}
|
||||
if next.Price != price {
|
||||
t.Fatalf("price = %d, want %d", next.Price, price)
|
||||
}
|
||||
if next.ListenPermission != permission {
|
||||
t.Fatalf("listenPermission = %d, want %d", next.ListenPermission, permission)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,723 @@
|
||||
package mediacontentser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
"91porn-server/web/service/updownloadser"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
ID *string `form:"id" bson:"id,omitempty"` // ID
|
||||
Name *string `form:"name" bson:"name,omitempty"` // 名称
|
||||
Type *int `form:"type" bson:"type,omitempty"` // 类型 1:动漫,2:漫画
|
||||
Status *int `form:"status" bson:"status,omitempty"` // 状态
|
||||
IsActive *bool `form:"isActive" json:"isActive"` // 是否激活
|
||||
MID *string `form:"mId" json:"mId" bson:"mId" binding:"required"` // 媒体资源ID
|
||||
PageNumber uint64 `form:"pageNumber" json:"pageNumber" binding:"required,min=1"` // 当前页
|
||||
PageSize uint64 `form:"pageSize" json:"pageSize" binding:"required,min=1,max=100"` // 每页条数
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
List []*mediacontentmod.MediaContent `json:"list"`
|
||||
}
|
||||
|
||||
func (q *WebListReq) Filter() bson.M {
|
||||
var query = bson.M{}
|
||||
if q.Name != nil {
|
||||
query["name"] = primitive.Regex{Pattern: *q.Name, Options: "i"}
|
||||
}
|
||||
if q.Status != nil {
|
||||
query["status"] = *q.Status
|
||||
}
|
||||
if q.MID != nil {
|
||||
mid, _ := primitive.ObjectIDFromHex(*q.MID)
|
||||
query["mediaId"] = mid
|
||||
}
|
||||
if q.Type != nil {
|
||||
query["type"] = *q.Type
|
||||
}
|
||||
if q.IsActive != nil {
|
||||
query["isActive"] = *q.IsActive
|
||||
}
|
||||
if q.ID != nil {
|
||||
id, _ := primitive.ObjectIDFromHex(*q.ID)
|
||||
query["_id"] = id
|
||||
}
|
||||
query["isDelete"] = false
|
||||
return query
|
||||
}
|
||||
func (q *WebListReq) Options() *options.FindOptions {
|
||||
return options.Find().SetSkip(int64((q.PageNumber - 1) * q.PageSize)).SetLimit(int64(q.PageSize)).SetSort(bson.M{"episodeNumber": 1})
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res *WebListRes, err error) {
|
||||
var out = &WebListRes{}
|
||||
count, err := mediacontentmod.QueryAllCount(q.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*mediacontentmod.MediaContent{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = mediacontentmod.QueryAllList(q.Filter(), q.Options())
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("mediacontentmod queryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
MediaID primitive.ObjectID `json:"mediaId" form:"mediaId" binding:"required"` // 媒体资源ID
|
||||
MediaType string `json:"mediaType" form:"mediaType" binding:"required"` // 媒体类型
|
||||
EpisodeNumber uint `json:"episodeNumber" form:"episodeNumber" ` // 第几集
|
||||
ListenPermission int `json:"listenPermission" form:"listenPermission" ` // 收听权限 0:会员 1:金币购买 2:免费
|
||||
Price int64 `json:"price" form:"price" ` // 购买价格
|
||||
Cover string `json:"cover" form:"cover"` //封面
|
||||
Name string `json:"name" form:"name" ` // 章节名
|
||||
Text string `json:"text" form:"text" ` // 内容(小说专用)
|
||||
Md5 string `json:"md5" form:"md5" ` // 媒体md5
|
||||
SourceID string `json:"sourceID" form:"sourceID"` // 视频唯一id,对应文件服
|
||||
VideoUrl string `json:"videoUrl" form:"videoUrl" ` // 视频地址
|
||||
AudioUrl string `json:"audioUrl" form:"audioUrl"` // 有声小说地址
|
||||
UrlSet []string `json:"urlSet" form:"urlSet"` // 地址集(多个资源地址用 例如漫画)
|
||||
Filename string `json:"filename" form:"filename"` // 文件名称
|
||||
IsActive bool `json:"isActive" form:"isActive" ` // 是否激活
|
||||
PreviewEnabled bool `json:"previewEnabled" form:"previewEnabled"` // 是否允许试看
|
||||
PreviewStart int `json:"previewStart" form:"previewStart"` // 试看起始时间(秒)
|
||||
PreviewSeconds int `json:"previewSeconds" form:"previewSeconds"` // 试看时长(秒)
|
||||
}
|
||||
|
||||
const defaultDramaPreviewSeconds = 60
|
||||
|
||||
func normalizeDramaPreview(enabled bool, start, seconds int, playTime uint) (bool, int, int, error) {
|
||||
if !enabled {
|
||||
return false, 0, 0, nil
|
||||
}
|
||||
if start < 0 {
|
||||
return false, 0, 0, errors.New("试看起始时间不能小于0")
|
||||
}
|
||||
if seconds < 0 {
|
||||
return false, 0, 0, errors.New("试看时长不能小于0")
|
||||
}
|
||||
if seconds == 0 {
|
||||
seconds = defaultDramaPreviewSeconds
|
||||
}
|
||||
if playTime > 0 && uint64(start)+uint64(seconds) > uint64(playTime) {
|
||||
return false, 0, 0, errors.New("试看起始时间与时长之和不能超过视频总时长")
|
||||
}
|
||||
return true, start, seconds, nil
|
||||
}
|
||||
|
||||
func validateDramaEpisodeState(episodeNumber int, listenPermission int, price int64, name, videoURL, h265URL string, active bool, status int) error {
|
||||
if episodeNumber < 1 {
|
||||
return errors.New("剧集编号必须大于0")
|
||||
}
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return errors.New("剧集名称不能为空")
|
||||
}
|
||||
if listenPermission != 1 && listenPermission != 2 {
|
||||
return errors.New("短剧剧集listenPermission只支持1或2")
|
||||
}
|
||||
if listenPermission == 2 && price != 0 {
|
||||
return errors.New("免费剧集价格必须为0")
|
||||
}
|
||||
if listenPermission == 1 && price <= 0 {
|
||||
return errors.New("金币剧集价格必须大于0")
|
||||
}
|
||||
if active {
|
||||
if status != 1 {
|
||||
return errors.New("视频仍在转码,暂不能上架")
|
||||
}
|
||||
if strings.TrimSpace(videoURL) == "" && strings.TrimSpace(h265URL) == "" {
|
||||
return errors.New("剧集没有播放资源,暂不能上架")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureEpisodeNumberUnique(mediaID primitive.ObjectID, episodeNumber int, excludeID primitive.ObjectID) error {
|
||||
filter := bson.M{
|
||||
"mediaId": mediaID,
|
||||
"episodeNumber": episodeNumber,
|
||||
"isDelete": false,
|
||||
}
|
||||
if !excludeID.IsZero() {
|
||||
filter["_id"] = bson.M{"$ne": excludeID}
|
||||
}
|
||||
count, err := mediacontentmod.QueryAllCount(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("剧集编号已存在")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fileStatusToEpisodeStatus(status string) int {
|
||||
switch status {
|
||||
case vidmod.ConvertCompleted, vidmod.Completed:
|
||||
return 1
|
||||
case vidmod.FileUploadError, vidmod.MergeError, vidmod.ConvertError:
|
||||
return 2
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func mediaMetadataFromFs(id string) (vidmod.AwsPullResp, int, error) {
|
||||
resp, err := updownloadser.PullFileInfo(id)
|
||||
if err != nil {
|
||||
return vidmod.AwsPullResp{}, 0, err
|
||||
}
|
||||
status := fileStatusToEpisodeStatus(resp.Data.Status)
|
||||
return resp.Data, status, nil
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create(manager string) (err error, vid primitive.ObjectID) {
|
||||
now := time.Now()
|
||||
media, err := mediamod.QueryMediaByID(bson.M{"_id": p.MediaID})
|
||||
if err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
if media == nil || media.ID.IsZero() {
|
||||
return errors.New("media info is nll"), primitive.ObjectID{}
|
||||
}
|
||||
if media.IsDelete || p.MediaType != media.MediaType {
|
||||
return errors.New("剧集mediaType与父媒体不一致"), primitive.ObjectID{}
|
||||
}
|
||||
p.Name = strings.TrimSpace(p.Name)
|
||||
|
||||
if p.MediaType == mediamod.MediaTypeVideo || p.MediaType == mediamod.MediaTypeDrama {
|
||||
vid, err = primitive.ObjectIDFromHex(p.SourceID)
|
||||
if err != nil || vid.IsZero() {
|
||||
return errors.New("sourceID不是有效的资源ID"), primitive.ObjectID{}
|
||||
}
|
||||
} else {
|
||||
vid = primitive.NewObjectID()
|
||||
}
|
||||
if err = ensureEpisodeNumberUnique(media.ID, int(p.EpisodeNumber), primitive.NilObjectID); err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
if media.MediaType == mediamod.MediaTypeDrama {
|
||||
if err = validateDramaEpisodeState(int(p.EpisodeNumber), p.ListenPermission, p.Price, p.Name, p.VideoUrl, "", false, 0); err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
if strings.TrimSpace(p.VideoUrl) == "" {
|
||||
return errors.New("短剧剧集必须提供videoUrl"), primitive.ObjectID{}
|
||||
}
|
||||
}
|
||||
previewEnabled, previewStart, previewSeconds := false, 0, 0
|
||||
if media.MediaType == mediamod.MediaTypeDrama {
|
||||
previewEnabled, previewStart, previewSeconds, err = normalizeDramaPreview(p.PreviewEnabled, p.PreviewStart, p.PreviewSeconds, 0)
|
||||
if err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
}
|
||||
mc := mediacontentmod.MediaContent{
|
||||
ID: vid,
|
||||
MediaID: p.MediaID,
|
||||
MediaType: media.MediaType,
|
||||
Cover: p.Cover,
|
||||
EpisodeNumber: int(p.EpisodeNumber),
|
||||
ListenPermission: p.ListenPermission,
|
||||
Price: p.Price,
|
||||
Name: p.Name,
|
||||
IsActive: p.IsActive,
|
||||
IsDelete: false,
|
||||
UpdatedAct: manager,
|
||||
UpdateTime: now,
|
||||
CreatedAt: now,
|
||||
PreviewEnabled: previewEnabled,
|
||||
PreviewStart: previewStart,
|
||||
PreviewSeconds: previewSeconds,
|
||||
}
|
||||
// 视频和短剧都使用视频资源。
|
||||
if media.MediaType == mediamod.MediaTypeVideo || media.MediaType == mediamod.MediaTypeDrama {
|
||||
mc.Md5 = p.Md5
|
||||
mc.VideoUrl = strings.TrimSpace(p.VideoUrl)
|
||||
mc.Status = 0
|
||||
if p.IsActive {
|
||||
metadata, status, metadataErr := mediaMetadataFromFs(vid.Hex())
|
||||
if metadataErr != nil {
|
||||
return errors.New("文件服务连接失败,暂不能上架剧集"), primitive.ObjectID{}
|
||||
}
|
||||
mc.Status = status
|
||||
mc.PlayTime = metadata.PlayTime
|
||||
mc.Weight = metadata.Width
|
||||
mc.Height = metadata.Height
|
||||
mc.MediaSize = int64(metadata.Size)
|
||||
if metadata.CheckSum != "" {
|
||||
mc.Md5 = metadata.CheckSum
|
||||
}
|
||||
mc.Ratio = metadata.Ratio
|
||||
if mc.Ratio == 0 && mc.Height > 0 {
|
||||
mc.Ratio = float64(mc.Weight) / float64(mc.Height)
|
||||
}
|
||||
if media.MediaType == mediamod.MediaTypeDrama {
|
||||
mc.PreviewEnabled, mc.PreviewStart, mc.PreviewSeconds, err = normalizeDramaPreview(mc.PreviewEnabled, mc.PreviewStart, mc.PreviewSeconds, mc.PlayTime)
|
||||
if err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
if err = validateDramaEpisodeState(mc.EpisodeNumber, mc.ListenPermission, mc.Price, mc.Name, mc.VideoUrl, mc.H265Url, true, mc.Status); err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 图集
|
||||
if media.MediaType == mediamod.MediaTypeImage {
|
||||
mc.UrlSet = p.UrlSet
|
||||
}
|
||||
// 小说
|
||||
if media.MediaType == mediamod.MediaTypeText {
|
||||
mc.AudioUrl = p.AudioUrl
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := mediacontentmod.Insert(nil, mc); err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
|
||||
err = OnMediaContentChange(media.ID, media.MediaType, true, mc.IsActive)
|
||||
if err != nil {
|
||||
return err, primitive.ObjectID{}
|
||||
}
|
||||
return nil, vid
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
EpisodeNumber *int `json:"episodeNumber"` // 第几集
|
||||
ListenPermission *int `json:"listenPermission"` // 收听权限 0:会员 1:金币购买 2:免费
|
||||
Price *int64 `json:"price"` // 购买价格
|
||||
Name *string `json:"name"` // 章节名
|
||||
Cover *string `json:"cover"` // 封面
|
||||
Text *string `json:"text"` // 内容(小说专用)
|
||||
Md5 *string `json:"md5"` // 媒体md5
|
||||
VideoUrl *string `json:"videoUrl"` // 视频地址
|
||||
H265Url *string `json:"h265Url"` // H.265视频地址
|
||||
AudioUrl *string `json:"audioUrl"` // 有声小说地址
|
||||
UrlSet *[]string `json:"urlSet"` // 地址集(多个资源地址用 例如漫画)
|
||||
IsActive *bool `json:"isActive"` // 是否激活
|
||||
PreviewEnabled *bool `json:"previewEnabled"` // 是否允许试看
|
||||
PreviewStart *int `json:"previewStart"` // 试看起始时间(秒)
|
||||
PreviewSeconds *int `json:"previewSeconds"` // 试看时长(秒)
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update(manager string) error {
|
||||
mediaContent, err := mediacontentmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mediaContent.ID.IsZero() {
|
||||
return errors.New("media content is null")
|
||||
}
|
||||
media, err := mediamod.GetInfo(mediaContent.MediaID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
next := mediaContent
|
||||
if p.EpisodeNumber != nil {
|
||||
next.EpisodeNumber = *p.EpisodeNumber
|
||||
}
|
||||
if p.ListenPermission != nil {
|
||||
next.ListenPermission = *p.ListenPermission
|
||||
}
|
||||
if p.Price != nil {
|
||||
next.Price = *p.Price
|
||||
}
|
||||
if p.Name != nil {
|
||||
next.Name = strings.TrimSpace(*p.Name)
|
||||
}
|
||||
if p.VideoUrl != nil {
|
||||
next.VideoUrl = strings.TrimSpace(*p.VideoUrl)
|
||||
}
|
||||
if p.H265Url != nil {
|
||||
next.H265Url = strings.TrimSpace(*p.H265Url)
|
||||
}
|
||||
if p.IsActive != nil {
|
||||
next.IsActive = *p.IsActive
|
||||
}
|
||||
if p.PreviewEnabled != nil {
|
||||
next.PreviewEnabled = *p.PreviewEnabled
|
||||
}
|
||||
if p.PreviewStart != nil {
|
||||
next.PreviewStart = *p.PreviewStart
|
||||
}
|
||||
if p.PreviewSeconds != nil {
|
||||
next.PreviewSeconds = *p.PreviewSeconds
|
||||
}
|
||||
var metadata *vidmod.AwsPullResp
|
||||
if next.IsActive && (media.MediaType == mediamod.MediaTypeVideo || media.MediaType == mediamod.MediaTypeDrama) && next.Status != 1 {
|
||||
loaded, status, metadataErr := mediaMetadataFromFs(mediaContent.ID.Hex())
|
||||
if metadataErr != nil {
|
||||
return errors.New("文件服务连接失败,暂不能上架剧集")
|
||||
}
|
||||
metadata = &loaded
|
||||
next.Status = status
|
||||
next.PlayTime = loaded.PlayTime
|
||||
next.Weight = loaded.Width
|
||||
next.Height = loaded.Height
|
||||
next.MediaSize = int64(loaded.Size)
|
||||
next.Ratio = loaded.Ratio
|
||||
if next.Ratio == 0 && next.Height > 0 {
|
||||
next.Ratio = float64(next.Weight) / float64(next.Height)
|
||||
}
|
||||
}
|
||||
if media.MediaType == mediamod.MediaTypeDrama {
|
||||
next.PreviewEnabled, next.PreviewStart, next.PreviewSeconds, err = normalizeDramaPreview(next.PreviewEnabled, next.PreviewStart, next.PreviewSeconds, next.PlayTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = validateDramaEpisodeState(next.EpisodeNumber, next.ListenPermission, next.Price, next.Name, next.VideoUrl, next.H265Url, next.IsActive, next.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ensureEpisodeNumberUnique(media.ID, next.EpisodeNumber, mediaContent.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
data := make(map[string]interface{})
|
||||
if p.EpisodeNumber != nil {
|
||||
data["episodeNumber"] = *p.EpisodeNumber
|
||||
}
|
||||
if p.ListenPermission != nil {
|
||||
data["listenPermission"] = *p.ListenPermission
|
||||
}
|
||||
if p.Price != nil {
|
||||
data["price"] = *p.Price
|
||||
}
|
||||
if p.Name != nil {
|
||||
data["name"] = next.Name
|
||||
}
|
||||
if p.Cover != nil {
|
||||
data["cover"] = *p.Cover
|
||||
}
|
||||
if p.Text != nil {
|
||||
data["text"] = *p.Text
|
||||
}
|
||||
if p.Md5 != nil {
|
||||
data["md5"] = *p.Md5
|
||||
}
|
||||
if p.VideoUrl != nil {
|
||||
data["videoUrl"] = next.VideoUrl
|
||||
}
|
||||
if p.H265Url != nil {
|
||||
data["h265Url"] = next.H265Url
|
||||
}
|
||||
if p.AudioUrl != nil {
|
||||
data["audioUrl"] = *p.AudioUrl
|
||||
}
|
||||
if p.UrlSet != nil {
|
||||
data["urlSet"] = *p.UrlSet
|
||||
}
|
||||
|
||||
if p.IsActive != nil {
|
||||
data["isActive"] = *p.IsActive
|
||||
}
|
||||
if media.MediaType == mediamod.MediaTypeDrama && (p.PreviewEnabled != nil || p.PreviewStart != nil || p.PreviewSeconds != nil) {
|
||||
data["previewEnabled"] = next.PreviewEnabled
|
||||
data["previewStart"] = next.PreviewStart
|
||||
data["previewSeconds"] = next.PreviewSeconds
|
||||
}
|
||||
if metadata != nil {
|
||||
data["playTime"] = metadata.PlayTime
|
||||
data["weight"] = metadata.Width
|
||||
data["height"] = metadata.Height
|
||||
data["mediaSize"] = metadata.Size
|
||||
data["status"] = next.Status
|
||||
data["ratio"] = next.Ratio
|
||||
if metadata.CheckSum != "" {
|
||||
data["md5"] = metadata.CheckSum
|
||||
}
|
||||
}
|
||||
|
||||
data["updatedAct"] = manager
|
||||
data["updateTime"] = time.Now()
|
||||
if _, err = mediacontentmod.UpdateByID(nil, p.ID, data); err != nil {
|
||||
return err
|
||||
}
|
||||
published := p.IsActive != nil && *p.IsActive && !mediaContent.IsActive
|
||||
err = OnMediaContentChange(media.ID, media.MediaType, false, published)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebBatchUpdateReq struct {
|
||||
IDS []string `json:"ids" binding:"required"`
|
||||
IsActive *bool `json:"isActive"` // 是否激活
|
||||
Price *int64 `json:"price"` // 价格
|
||||
Permission *int `json:"permission"` // 收听权限 0:会员 1:金币购买 2:免费
|
||||
FreeEpisode *int `json:"freeEpisode"` // 前几集免费
|
||||
}
|
||||
|
||||
func (p *WebBatchUpdateReq) Filter() []primitive.ObjectID {
|
||||
ids, _ := p.ValidatedIDs()
|
||||
return ids
|
||||
}
|
||||
|
||||
func (p *WebBatchUpdateReq) ValidatedIDs() ([]primitive.ObjectID, error) {
|
||||
if len(p.IDS) == 0 || len(p.IDS) > 100 {
|
||||
return nil, errors.New("ids数量必须在1到100之间")
|
||||
}
|
||||
if p.IsActive == nil && p.Price == nil && p.Permission == nil && p.FreeEpisode == nil {
|
||||
return nil, errors.New("至少需要一个批量更新字段")
|
||||
}
|
||||
if p.FreeEpisode != nil && *p.FreeEpisode < 0 {
|
||||
return nil, errors.New("freeEpisode不能小于0")
|
||||
}
|
||||
ids := make([]primitive.ObjectID, 0, len(p.IDS))
|
||||
seen := make(map[primitive.ObjectID]struct{}, len(p.IDS))
|
||||
for _, raw := range p.IDS {
|
||||
id, err := primitive.ObjectIDFromHex(raw)
|
||||
if err != nil || id.IsZero() {
|
||||
return nil, errors.New("ids包含无效的剧集ID")
|
||||
}
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (p *WebBatchUpdateReq) applyContentFields(content mediacontentmod.MediaContent) mediacontentmod.MediaContent {
|
||||
if p.IsActive != nil {
|
||||
content.IsActive = *p.IsActive
|
||||
}
|
||||
if p.Price != nil {
|
||||
content.Price = *p.Price
|
||||
}
|
||||
if p.Permission != nil {
|
||||
content.ListenPermission = *p.Permission
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebBatchUpdateReq) Update(manager string) error {
|
||||
ids, err := p.ValidatedIDs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contents, err := mediacontentmod.QueryAllList(bson.M{
|
||||
"_id": bson.M{"$in": ids},
|
||||
"isDelete": false,
|
||||
}, options.Find().SetLimit(int64(len(ids))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(contents) != len(ids) {
|
||||
return errors.New("部分剧集不存在或已删除")
|
||||
}
|
||||
mediaID := contents[0].MediaID
|
||||
mediaType := contents[0].MediaType
|
||||
published := false
|
||||
for _, content := range contents {
|
||||
if content.MediaID != mediaID {
|
||||
return errors.New("批量操作的剧集必须属于同一部短剧")
|
||||
}
|
||||
next := p.applyContentFields(*content)
|
||||
if p.IsActive != nil && *p.IsActive && !content.IsActive {
|
||||
published = true
|
||||
}
|
||||
validateState := p.Price != nil || p.Permission != nil || (p.IsActive != nil && *p.IsActive)
|
||||
if validateState && mediaType == mediamod.MediaTypeDrama {
|
||||
if err = validateDramaEpisodeState(next.EpisodeNumber, next.ListenPermission, next.Price, next.Name, next.VideoUrl, next.H265Url, next.IsActive, next.Status); err != nil {
|
||||
return fmt.Errorf("第%d集:%w", content.EpisodeNumber, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
data := make(map[string]interface{})
|
||||
if p.IsActive != nil {
|
||||
data["isActive"] = *p.IsActive
|
||||
}
|
||||
if p.Price != nil {
|
||||
data["price"] = *p.Price
|
||||
}
|
||||
if p.Permission != nil {
|
||||
data["listenPermission"] = *p.Permission
|
||||
}
|
||||
data["updatedAct"] = manager
|
||||
data["updateTime"] = time.Now()
|
||||
|
||||
contentChanged := p.IsActive != nil || p.Price != nil || p.Permission != nil
|
||||
if contentChanged {
|
||||
if _, err := mediacontentmod.UpdateByIDS(nil, ids, data); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = OnMediaContentChange(mediaID, mediaType, false, published); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if p.FreeEpisode != nil {
|
||||
if _, err = mediamod.UpdateByID(nil, mediaID, map[string]interface{}{
|
||||
"freeEpisode": *p.FreeEpisode,
|
||||
"updatedAct": manager,
|
||||
"updateTime": time.Now(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
mediaContent, err := mediacontentmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mediacontentmod.DeleteByID(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = OnMediaContentChange(mediaContent.MediaID, mediaContent.MediaType, false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func OnMediaContentChange(mediaId primitive.ObjectID, mediaType string, contentAdded, published bool) (err error) {
|
||||
// 获取当前最小的章节,重新update
|
||||
defaultContent := mediamod.DefaultContent{}
|
||||
if mediaType == mediamod.MediaTypeImage {
|
||||
contentList, _, _, err := mediacontentmod.GetList(bson.M{"mediaId": mediaId, "isActive": true, "isDelete": false}, 0, 1, bson.D{{Key: "episodeNumber", Value: 1}, {Key: "createdAt", Value: 1}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(contentList) > 0 {
|
||||
defaultContent.Id = contentList[0].ID
|
||||
defaultContent.Name = contentList[0].Name
|
||||
defaultContent.UrlSet = contentList[0].UrlSet
|
||||
defaultContent.Cover = contentList[0].Cover
|
||||
}
|
||||
}
|
||||
// 子集总数
|
||||
count, err := mediacontentmod.QueryAllCount(bson.M{"mediaId": mediaId, "isActive": true, "isDelete": false})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 获取子集免费章节总数
|
||||
freeCount, err := mediacontentmod.QueryAllCount(bson.M{"mediaId": mediaId, "listenPermission": 2, "isActive": true, "isDelete": false})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contentsPrice, err := mediacontentmod.QueryContentsPrice(mediaId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
updateData := bson.M{
|
||||
"currentEpisode": count,
|
||||
"contentsPrice": contentsPrice,
|
||||
"freeEpisode": freeCount,
|
||||
"updateTime": time.Now(),
|
||||
"defaultContent": defaultContent, // 默认展示的子集内容,冗余一个字段,减少一次查询
|
||||
}
|
||||
now := time.Now()
|
||||
if contentAdded {
|
||||
updateData["contentUpdateTime"] = now
|
||||
}
|
||||
if published {
|
||||
updateData["latestPublishedAt"] = now
|
||||
}
|
||||
//修改当前父级的记录
|
||||
_, err = mediamod.UpdateByID(nil, mediaId, updateData)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ViewMediaFromFs 查看视频转码状态
|
||||
func ViewMediaFromFs(id string) (code stderr.Code, data interface{}) {
|
||||
resp, err := updownloadser.PullFileInfo(id)
|
||||
if err != nil {
|
||||
return stderr.ErrConnectToFs, nil
|
||||
}
|
||||
status := fileStatusToEpisodeStatus(resp.Data.Status)
|
||||
ratio := resp.Data.Ratio
|
||||
if ratio == 0 && resp.Data.Height > 0 {
|
||||
ratio = float64(resp.Data.Width) / float64(resp.Data.Height)
|
||||
}
|
||||
set := map[string]interface{}{
|
||||
"status": status,
|
||||
"playTime": resp.Data.PlayTime,
|
||||
"weight": resp.Data.Width,
|
||||
"height": resp.Data.Height,
|
||||
"mediaSize": int64(resp.Data.Size),
|
||||
"ratio": ratio,
|
||||
"updateTime": time.Now(),
|
||||
}
|
||||
if resp.Data.CheckSum != "" {
|
||||
set["md5"] = resp.Data.CheckSum
|
||||
}
|
||||
objId, _ := primitive.ObjectIDFromHex(id)
|
||||
if _, err = mediacontentmod.UpdateByID(nil, objId, set); err != nil {
|
||||
return stderr.ErrDbUpdateError, nil
|
||||
}
|
||||
back := make(map[string]interface{}, 10)
|
||||
back["playTime"] = resp.Data.PlayTime
|
||||
back["size"] = resp.Data.Size
|
||||
back["width"] = resp.Data.Width
|
||||
back["height"] = resp.Data.Height
|
||||
back["status"] = resp.Data.Status
|
||||
back["ratio"] = ratio
|
||||
fileName := resp.Data.Filename
|
||||
fieldNameFs := resp.Data.FieldNameFs
|
||||
back["fileName"] = filepath.Join(filepath.Dir(fieldNameFs), fileName)
|
||||
switch resp.Data.Status {
|
||||
case vidmod.UploadLoadingToFs:
|
||||
back["statusDesc"] = "正在上传中"
|
||||
case vidmod.UploadCompleted:
|
||||
back["statusDesc"] = "上传完成"
|
||||
case vidmod.FileUploadError:
|
||||
back["statusDesc"] = "上传错误"
|
||||
case vidmod.Merging:
|
||||
back["statusDesc"] = "正在合并中"
|
||||
case vidmod.MergeCompleted:
|
||||
back["statusDesc"] = "合并完成"
|
||||
case vidmod.MergeError:
|
||||
back["statusDesc"] = "合并失败"
|
||||
case vidmod.Converting:
|
||||
back["statusDesc"] = "正在转码中"
|
||||
case vidmod.ConvertError:
|
||||
back["statusDesc"] = "转码失败"
|
||||
case vidmod.ConvertCompleted, vidmod.Completed:
|
||||
back["statusDesc"] = "完成状态"
|
||||
}
|
||||
return stderr.Success, back
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package mediaresourceser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
mediaService "91porn-server/common/media"
|
||||
"91porn-server/common/mediaCenter"
|
||||
"91porn-server/web/webg"
|
||||
"errors"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type MediaListReq struct {
|
||||
MediaType int `json:"media_type" form:"media_type"` // 1-动画 2-漫画 3-文本小说 4-有声小说 5-短剧
|
||||
MediaCenterId *uint `json:"media_center_id" form:"media_center_id"` // 媒资中心的中资源唯一id
|
||||
VloggerId *uint `json:"vlogger_id" form:"vlogger_id"` // up主id
|
||||
IsUp *int `json:"is_up" form:"is_up"` // 是否是up主 0-不是 1-是 不传则是所有
|
||||
Title *string `json:"title" form:"title"` // 标题
|
||||
Tag *string `json:"tag" form:"tag"` // 标签
|
||||
TerminalCdo *string `json:"terminal_cdo" form:"terminal_cdo"` // 上传者
|
||||
SyncType int `json:"sync_type" form:"sync_type"` // 同步类型 1-全量同步 2-增量同步
|
||||
Page uint `json:"page" form:"page"`
|
||||
Size uint `json:"size" form:"size"`
|
||||
}
|
||||
|
||||
type MediaListResp struct {
|
||||
List []*mediaCenter.Media `json:"list"`
|
||||
Total uint `json:"total"`
|
||||
}
|
||||
|
||||
func (p *MediaListReq) Validate() error {
|
||||
if p.MediaType < 1 || p.MediaType > 5 {
|
||||
return errors.New("media_type只支持1到5")
|
||||
}
|
||||
if p.Page < 1 || p.Size < 1 || p.Size > 100 {
|
||||
return errors.New("page必须大于0且size必须在1到100之间")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MediaListReq) GetList() (res MediaListResp, err error) {
|
||||
ms := mediaCenter.NewMediaCenterService(mediaCenter.Config{
|
||||
Url: webg.Conf.MediaResourceDBCfg.BaseUrl,
|
||||
ProId: webg.Conf.MediaResourceDBCfg.Merchat,
|
||||
Secret: webg.Conf.MediaResourceDBCfg.Secret,
|
||||
})
|
||||
data, err := ms.MediaList(&mediaCenter.MediaListReq{
|
||||
MediaType: p.MediaType,
|
||||
Id: p.MediaCenterId,
|
||||
VloggerId: p.VloggerId,
|
||||
IsUp: p.IsUp,
|
||||
Title: p.Title,
|
||||
Tag: p.Tag,
|
||||
TerminalCdo: p.TerminalCdo,
|
||||
SyncType: p.SyncType,
|
||||
Page: p.Page,
|
||||
Size: p.Size,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res.List = data.Data
|
||||
res.Total = data.Pagination.Count
|
||||
return
|
||||
}
|
||||
|
||||
type MediaContentListReq struct {
|
||||
MediaId int `json:"media_id" form:"media_id"`
|
||||
Page uint `json:"page" form:"page"`
|
||||
Size uint `json:"size" form:"size"`
|
||||
}
|
||||
|
||||
type MediaContentListResp struct {
|
||||
List []*mediaCenter.MediaContent `json:"list"`
|
||||
Total uint `json:"total"`
|
||||
}
|
||||
|
||||
func (p *MediaContentListReq) Validate() error {
|
||||
if p.MediaId <= 0 {
|
||||
return errors.New("media_id必须大于0")
|
||||
}
|
||||
if p.Page < 1 || p.Size < 1 || p.Size > 100 {
|
||||
return errors.New("page必须大于0且size必须在1到100之间")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MediaContentListReq) GetList() (res MediaContentListResp, err error) {
|
||||
ms := mediaCenter.NewMediaCenterService(mediaCenter.Config{
|
||||
Url: webg.Conf.MediaResourceDBCfg.BaseUrl,
|
||||
ProId: webg.Conf.MediaResourceDBCfg.Merchat,
|
||||
Secret: webg.Conf.MediaResourceDBCfg.Secret,
|
||||
})
|
||||
data, err := ms.MediaContentList(&mediaCenter.MediaContentListReq{
|
||||
MediaId: p.MediaId,
|
||||
Page: p.Page,
|
||||
Size: p.Size,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res.List = data.Data
|
||||
res.Total = data.Pagination.Count
|
||||
return
|
||||
}
|
||||
|
||||
type MediaUseReq struct {
|
||||
Ids []uint `json:"ids" form:"ids"`
|
||||
Kind int `json:"kind" form:"kind" binding:"required"` //种类(1、哩番,2、3D,3、同人动画,4、单行本,5、连载漫画,6、韩漫 7、小说 8剧场
|
||||
Type int `json:"type" form:"type" binding:"required"` //维度筛选 1-动漫 2-漫画
|
||||
Dimension string `json:"dimension" form:"dimension" binding:"required"` //维度
|
||||
SectionId primitive.ObjectID `json:"section_id" form:"section_id"` //专题id
|
||||
}
|
||||
|
||||
func (p *MediaUseReq) Validate() error {
|
||||
if len(p.Ids) == 0 || len(p.Ids) > 100 {
|
||||
return errors.New("ids数量必须在1到100之间")
|
||||
}
|
||||
if strings.EqualFold(strings.TrimSpace(p.Dimension), "drama") {
|
||||
if p.Kind != 8 || p.Type != 1 {
|
||||
return errors.New("短剧导入时kind必须为8且type必须为1")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MediaUseReq) Use(admin string) (err error) {
|
||||
if err = p.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
workerCount := 5
|
||||
if len(p.Ids) < workerCount {
|
||||
workerCount = len(p.Ids)
|
||||
}
|
||||
jobs := make(chan uint)
|
||||
errs := make(chan error, len(p.Ids))
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < workerCount; i++ {
|
||||
wg.Add(1)
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
for id := range jobs {
|
||||
if useErr := use(id, admin, p.Dimension, p.Type, p.Kind, p.SectionId); useErr != nil {
|
||||
log.Error("MediaUse Fail", log.Any("id", id), log.E(useErr))
|
||||
errs <- useErr
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, id := range p.Ids {
|
||||
jobs <- id
|
||||
}
|
||||
close(jobs)
|
||||
wg.Wait()
|
||||
close(errs)
|
||||
for useErr := range errs {
|
||||
if useErr != nil {
|
||||
return useErr
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func use(id uint, admin string, dimension string, dimensionType, kind int, sectionId primitive.ObjectID) (err error) {
|
||||
mediaService := mediaService.NewMediaSync(webg.Redis, mediaCenter.NewMediaCenterService(mediaCenter.Config{
|
||||
Url: webg.Conf.MediaResourceDBCfg.BaseUrl,
|
||||
ProId: webg.Conf.MediaResourceDBCfg.Merchat,
|
||||
Secret: webg.Conf.MediaResourceDBCfg.Secret,
|
||||
}))
|
||||
// 调用use接口,在媒资库打上标记,避免还出现在增量数据里
|
||||
err = mediaService.Ms.MediaUse(&mediaCenter.MediaUseReq{
|
||||
Ids: []uint{id},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 因为需要同步的东西特别多,所以需要异步处理
|
||||
mediaService.Worker.Exec(func() {
|
||||
mediaService.UseMediaData(admin, id, dimension, dimensionType, kind, sectionId)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
type MediaIgnoreReq struct {
|
||||
Ids []uint `json:"ids" form:"ids"`
|
||||
}
|
||||
|
||||
func (p *MediaIgnoreReq) Ignore() (err error) {
|
||||
ms := mediaCenter.NewMediaCenterService(mediaCenter.Config{
|
||||
Url: webg.Conf.MediaResourceDBCfg.BaseUrl,
|
||||
ProId: webg.Conf.MediaResourceDBCfg.Merchat,
|
||||
Secret: webg.Conf.MediaResourceDBCfg.Secret,
|
||||
})
|
||||
// 在媒资库打上标记,避免还出现在增量数据里
|
||||
return ms.MediaIgnore(&mediaCenter.MediaIgnoreReq{
|
||||
Ids: p.Ids,
|
||||
})
|
||||
}
|
||||
|
||||
type MediaSyncReq struct {
|
||||
}
|
||||
|
||||
// 执行同步的请求
|
||||
func (p *MediaSyncReq) Sync(admin string) {
|
||||
common.Go(func() {
|
||||
mediaService := mediaService.NewMediaSync(webg.Redis, mediaCenter.NewMediaCenterService(mediaCenter.Config{
|
||||
Url: webg.Conf.MediaResourceDBCfg.BaseUrl,
|
||||
ProId: webg.Conf.MediaResourceDBCfg.Merchat,
|
||||
Secret: webg.Conf.MediaResourceDBCfg.Secret,
|
||||
}))
|
||||
mediaService.SyncAll(admin)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mediaresourceser
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMediaListReqValidate(t *testing.T) {
|
||||
valid := MediaListReq{MediaType: 5, Page: 1, Size: 20}
|
||||
if err := valid.Validate(); err != nil {
|
||||
t.Fatalf("valid request rejected: %v", err)
|
||||
}
|
||||
invalidType := valid
|
||||
invalidType.MediaType = 6
|
||||
if err := invalidType.Validate(); err == nil {
|
||||
t.Fatal("invalid media_type unexpectedly accepted")
|
||||
}
|
||||
invalidPage := valid
|
||||
invalidPage.Page = 0
|
||||
if err := invalidPage.Validate(); err == nil {
|
||||
t.Fatal("page 0 unexpectedly accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMediaUseReqValidateDrama(t *testing.T) {
|
||||
valid := MediaUseReq{Ids: []uint{1}, Dimension: "drama", Kind: 8, Type: 1}
|
||||
if err := valid.Validate(); err != nil {
|
||||
t.Fatalf("valid drama import rejected: %v", err)
|
||||
}
|
||||
invalid := valid
|
||||
invalid.Kind = 1
|
||||
if err := invalid.Validate(); err == nil {
|
||||
t.Fatal("invalid drama kind unexpectedly accepted")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package mediaser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func validDramaCreateReq() WebCreateReq {
|
||||
return WebCreateReq{
|
||||
Title: "测试短剧",
|
||||
HorizontalCover: "https://example.com/h.jpg",
|
||||
VerticalCover: "https://example.com/v.jpg",
|
||||
TotalEpisode: 10,
|
||||
UpdateStatus: mediamod.Update,
|
||||
MediaType: mediamod.MediaTypeDrama,
|
||||
Kind: mediamod.Theater,
|
||||
Permission: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebCreateReqValidateDrama(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mutate func(*WebCreateReq)
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "valid"},
|
||||
{name: "missing cover", mutate: func(req *WebCreateReq) { req.VerticalCover = "" }, wantErr: true},
|
||||
{name: "published before first episode", mutate: func(req *WebCreateReq) { req.Status = 1 }, wantErr: true},
|
||||
{name: "wrong kind", mutate: func(req *WebCreateReq) { req.Kind = 1 }, wantErr: true},
|
||||
{name: "wrong permission", mutate: func(req *WebCreateReq) { req.Permission = 2 }, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := validDramaCreateReq()
|
||||
if tt.mutate != nil {
|
||||
tt.mutate(&req)
|
||||
}
|
||||
if gotErr := req.Validate() != nil; gotErr != tt.wantErr {
|
||||
t.Fatalf("Validate() error = %v, wantErr %v", gotErr, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDramaMediaForPublish(t *testing.T) {
|
||||
media := mediamod.Media{
|
||||
ID: primitive.NewObjectID(),
|
||||
MediaType: mediamod.MediaTypeDrama,
|
||||
Title: "测试短剧",
|
||||
HorizontalCover: "h.jpg",
|
||||
VerticalCover: "v.jpg",
|
||||
TotalEpisode: 10,
|
||||
UpdateStatus: mediamod.Update,
|
||||
Kind: mediamod.Theater,
|
||||
Permission: 1,
|
||||
}
|
||||
first := mediacontentmod.MediaContent{
|
||||
ID: primitive.NewObjectID(),
|
||||
MediaID: media.ID,
|
||||
MediaType: mediamod.MediaTypeDrama,
|
||||
EpisodeNumber: 1,
|
||||
Name: "第1集",
|
||||
Status: 1,
|
||||
IsActive: true,
|
||||
VideoUrl: "https://example.com/1.m3u8",
|
||||
}
|
||||
if err := validateDramaMediaForPublish(media, first); err != nil {
|
||||
t.Fatalf("valid drama cannot publish: %v", err)
|
||||
}
|
||||
first.Status = 0
|
||||
if err := validateDramaMediaForPublish(media, first); err == nil {
|
||||
t.Fatal("transcoding first episode unexpectedly allowed to publish")
|
||||
}
|
||||
first.Status = 1
|
||||
first.VideoUrl = ""
|
||||
if err := validateDramaMediaForPublish(media, first); err == nil {
|
||||
t.Fatal("first episode without play URL unexpectedly allowed to publish")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebListReqFilterUsesScalarValues(t *testing.T) {
|
||||
status, kind, mediaCenterID := 1, mediamod.Theater, uint(123)
|
||||
req := WebListReq{Status: &status, Kind: &kind, MediaCenterID: &mediaCenterID}
|
||||
filter := req.Filter()
|
||||
if filter["status"] != status || filter["kind"] != kind || filter["mediaCenterId"] != mediaCenterID {
|
||||
t.Fatalf("unexpected filter: %#v", filter)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebBatchUpdateReqRejectsDramaParentPricing(t *testing.T) {
|
||||
price := int64(30)
|
||||
req := WebBatchUpdateReq{Price: &price}
|
||||
medias := []*mediamod.Media{{MediaType: mediamod.MediaTypeDrama}}
|
||||
if err := req.validateDramaBatchFields(medias); err == nil {
|
||||
t.Fatal("drama parent pricing unexpectedly accepted")
|
||||
}
|
||||
req.Price = nil
|
||||
if err := req.validateDramaBatchFields(medias); err != nil {
|
||||
t.Fatalf("drama batch without parent pricing rejected: %v", err)
|
||||
}
|
||||
}
|
||||
Executable
+1095
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
package mediaser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/sensitivewordmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// CheckSensitive 单条媒资编辑的上架敏感词校验,规则对齐 vid/update:
|
||||
//
|
||||
// 1. status 被切到上架(1) —— 上架转换
|
||||
// 2. 当前已上架(status==1) 且本次更新涉及 Title/Summary —— 已上架内容改动
|
||||
//
|
||||
// 校验文本以请求字段为准(提供则用新值,否则用 DB 现值)。
|
||||
// 返回 (命中详情, true) 时调用方应拒绝本次更新;未命中或与上架无关返回 ("", false)。
|
||||
func (p *WebUpdateReq) CheckSensitive() (string, bool) {
|
||||
media, err := mediamod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
// 查不到交给后续 Update 流程报错,这里不拦
|
||||
return "", false
|
||||
}
|
||||
// mediamod.Media.Status: 1 上架 / 0 下架
|
||||
const statusUp = 1
|
||||
shelvingTransition := p.Status != nil && *p.Status == statusUp
|
||||
currentlyShelved := media.Status == statusUp
|
||||
touchesText := p.Title != nil || p.Summary != nil
|
||||
if !shelvingTransition && !(currentlyShelved && touchesText) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
title := media.Title
|
||||
summary := media.Summary
|
||||
if p.Title != nil {
|
||||
title = *p.Title
|
||||
}
|
||||
if p.Summary != nil {
|
||||
summary = *p.Summary
|
||||
}
|
||||
|
||||
terms := sensitivewordmod.LoadEnabledTerms()
|
||||
if len(terms) == 0 {
|
||||
return "", false
|
||||
}
|
||||
titleHits := sensitivewordmod.MatchHits(title, terms)
|
||||
summaryHits := sensitivewordmod.MatchHits(summary, terms)
|
||||
if len(titleHits) == 0 && len(summaryHits) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return formatMediaHitLine(p.ID, title, titleHits, summaryHits), true
|
||||
}
|
||||
|
||||
// CheckSensitive 批量媒资更新的上架敏感词校验。
|
||||
// 批量请求体不含文本字段,因此仅当 IsActive=true(批量上架)时,
|
||||
// 对每个 id 的 DB 现值 Title/Summary 做命中检测。任一命中即拒绝,多条按行聚合。
|
||||
func (p *WebBatchUpdateReq) CheckSensitive() (string, bool) {
|
||||
if p.IsActive == nil || !*p.IsActive {
|
||||
return "", false
|
||||
}
|
||||
terms := sensitivewordmod.LoadEnabledTerms()
|
||||
if len(terms) == 0 {
|
||||
return "", false
|
||||
}
|
||||
lines := make([]string, 0)
|
||||
for _, id := range p.Filter() {
|
||||
if id.IsZero() {
|
||||
continue
|
||||
}
|
||||
media, err := mediamod.GetInfo(id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
titleHits := sensitivewordmod.MatchHits(media.Title, terms)
|
||||
summaryHits := sensitivewordmod.MatchHits(media.Summary, terms)
|
||||
if len(titleHits) == 0 && len(summaryHits) == 0 {
|
||||
continue
|
||||
}
|
||||
lines = append(lines, formatMediaHitLine(media.ID, media.Title, titleHits, summaryHits))
|
||||
}
|
||||
if len(lines) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return strings.Join(lines, "\n"), true
|
||||
}
|
||||
|
||||
// formatMediaHitLine 媒资命中描述:
|
||||
// "id:xxxx 对象标题 XXXXXX。 标题命中 XX 简介命中 YY,不予上架成功"
|
||||
func formatMediaHitLine(id primitive.ObjectID, title string, titleHits, summaryHits []string) string {
|
||||
parts := make([]string, 0, 2)
|
||||
if len(titleHits) > 0 {
|
||||
parts = append(parts, "标题命中 "+strings.Join(titleHits, "、"))
|
||||
}
|
||||
if len(summaryHits) > 0 {
|
||||
parts = append(parts, "简介命中 "+strings.Join(summaryHits, "、"))
|
||||
}
|
||||
return fmt.Sprintf("id:%s 对象标题 %s。 %s,不予上架成功", id.Hex(), title, strings.Join(parts, " "))
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
package mediatagdimensionser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"91porn-server/models/cache/mediatagdimensiondata"
|
||||
"91porn-server/models/v/mediatagdimensionmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
ID *string `form:"id" bson:"id,omitempty"` // ID
|
||||
Name *string `form:"name" bson:"name,omitempty"` // 名称
|
||||
Type *int `form:"type" bson:"type,omitempty"` // 类型 1:动漫,2:漫画
|
||||
Status *int `form:"status" bson:"status,omitempty"` // 是否开启
|
||||
PageNumber uint64 `form:"pageNumber" json:"pageNumber" binding:"required,min=1"` // 当前页
|
||||
PageSize uint64 `form:"pageSize" json:"pageSize" binding:"required,min=1,max=100"` // 每页条数
|
||||
}
|
||||
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
List []*mediatagdimensionmod.MediaTagDimension `json:"list"`
|
||||
}
|
||||
|
||||
func (q *WebListReq) Filter() bson.M {
|
||||
var query = bson.M{}
|
||||
if q.Name != nil {
|
||||
query["name"] = q.Name
|
||||
}
|
||||
if q.Status != nil {
|
||||
query["status"] = q.Status
|
||||
}
|
||||
if q.Type != nil {
|
||||
query["type"] = q.Type
|
||||
}
|
||||
if q.ID != nil {
|
||||
id, _ := primitive.ObjectIDFromHex(*q.ID)
|
||||
query["_id"] = id
|
||||
}
|
||||
query["isDelete"] = false
|
||||
return query
|
||||
}
|
||||
func (q *WebListReq) Options() *options.FindOptions {
|
||||
return options.Find().SetSkip(int64((q.PageNumber - 1) * q.PageSize)).SetLimit(int64(q.PageSize)).SetSort(bson.M{"sortCode": -1})
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res *WebListRes, err error) {
|
||||
var out = &WebListRes{}
|
||||
count, err := mediatagdimensionmod.QueryAllCount(q.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*mediatagdimensionmod.MediaTagDimension{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = mediatagdimensionmod.QueryAllList(q.Filter(), q.Options())
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("mediatagdimensionmod queryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type WebAllListReq struct {
|
||||
Type *int `form:"type" bson:"type,omitempty"` // 类型 1:动漫,2:漫画
|
||||
}
|
||||
|
||||
type WebAllListRes struct {
|
||||
List []*mediatagdimensionmod.WebMediaTagDimension `json:"list"`
|
||||
}
|
||||
|
||||
func (q *WebAllListReq) Filter() bson.M {
|
||||
var query = bson.M{}
|
||||
if q.Type != nil {
|
||||
query["type"] = q.Type
|
||||
}
|
||||
query["status"] = 1
|
||||
query["isDelete"] = false
|
||||
return query
|
||||
}
|
||||
func (q *WebAllListReq) Options() *options.FindOptions {
|
||||
return options.Find().SetSort(bson.M{"sortCode": -1})
|
||||
}
|
||||
|
||||
// GetAllList 获取所有列表
|
||||
func (q *WebAllListReq) GetAllList() (res *WebAllListRes, err error) {
|
||||
var out = &WebAllListRes{}
|
||||
data, err := mediatagdimensionmod.QueryAllList(q.Filter(), q.Options())
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("mediatagdimensionmod queryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
for _, d := range data {
|
||||
mm := &mediatagdimensionmod.WebMediaTagDimension{
|
||||
ID: d.ID,
|
||||
Name: d.Name,
|
||||
Type: d.Type,
|
||||
}
|
||||
out.List = append(out.List, mm)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
Status int `json:"status" form:"status" binding:"required"` // 是否开启
|
||||
Type int `json:"type" form:"type" binding:"required"` // 类型 1:动漫,2:漫画
|
||||
Name string `json:"name" form:"name" binding:"required"` // 名称
|
||||
SortCode int `json:"sortCode" form:"sortCode"` // 排序
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create(manager string) error {
|
||||
now := time.Now()
|
||||
data := mediatagdimensionmod.MediaTagDimension{
|
||||
SortCode: p.SortCode,
|
||||
Status: p.Status,
|
||||
Type: p.Type,
|
||||
Name: p.Name,
|
||||
IsDelete: false,
|
||||
UpdatedAct: manager,
|
||||
UpdateTime: now,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := mediatagdimensiondata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
SortCode *int `json:"sortCode"` // 排序
|
||||
Status *int `json:"status"` // 是否开启
|
||||
Type *int `json:"type"` // 类型
|
||||
Name *string `json:"name"` // 名称
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update(manager string) error {
|
||||
coll, err := mediatagdimensionmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if coll.ID.IsZero() {
|
||||
return errors.New("media tag dimension is null")
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.SortCode != nil {
|
||||
data["sortCode"] = *p.SortCode
|
||||
}
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
if p.Type != nil {
|
||||
data["type"] = *p.Type
|
||||
}
|
||||
if p.Name != nil {
|
||||
data["name"] = *p.Name
|
||||
}
|
||||
data["updatedAct"] = manager
|
||||
data["updateTime"] = time.Now()
|
||||
|
||||
if _, err = mediatagdimensiondata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
if err := mediatagdimensiondata.DeleteData(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Executable
+221
@@ -0,0 +1,221 @@
|
||||
package mediatagser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"91porn-server/models/cache/mediatagdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/mediatagmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
ID *string `form:"id" bson:"id,omitempty"` // ID
|
||||
Name *string `form:"name" bson:"name,omitempty"` // 名称
|
||||
Type *int `form:"type" bson:"type,omitempty"` // 类型
|
||||
Kind *int `form:"kind" bson:"kind,omitempty"` // 种类
|
||||
Dimension *string `form:"dimension" bson:"dimension"` // 维度
|
||||
Status *int `form:"status" bson:"status,omitempty"` // 是否开启
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
List []*mediatagmod.MediaTag `json:"list"`
|
||||
}
|
||||
|
||||
func (q *WebListReq) Filter() bson.M {
|
||||
var query = bson.M{}
|
||||
if q.Name != nil {
|
||||
query["name"] = q.Name
|
||||
}
|
||||
if q.Status != nil {
|
||||
query["status"] = q.Status
|
||||
}
|
||||
if q.Kind != nil {
|
||||
query["kind"] = q.Kind
|
||||
}
|
||||
if q.Type != nil {
|
||||
query["type"] = q.Type
|
||||
}
|
||||
if q.Dimension != nil {
|
||||
query["dimension"] = q.Dimension
|
||||
}
|
||||
if q.ID != nil {
|
||||
id, _ := primitive.ObjectIDFromHex(*q.ID)
|
||||
query["_id"] = id
|
||||
}
|
||||
query["isDelete"] = false
|
||||
return query
|
||||
}
|
||||
func (q *WebListReq) Options() *options.FindOptions {
|
||||
return options.Find().SetSkip(int64((q.PageNumber - 1) * q.PageSize)).SetLimit(int64(q.PageSize)).SetSort(bson.M{"sortCode": -1})
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res *WebListRes, err error) {
|
||||
var out = &WebListRes{}
|
||||
count, err := mediatagmod.QueryAllCount(q.Filter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
out.List = []*mediatagmod.MediaTag{}
|
||||
out.Total = 0
|
||||
return out, nil
|
||||
}
|
||||
out.Total = count
|
||||
out.List, err = mediatagmod.QueryAllList(q.Filter(), q.Options())
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("mediatagmod queryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type WebAllListReq struct {
|
||||
Kind *int `form:"kind" bson:"kind,omitempty"` // 纬度分类 (1:动漫,2:漫画)
|
||||
Dimension *string `form:"dimension" bson:"dimension"` // 维度名称
|
||||
}
|
||||
type WebAllListRes struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` // 文档id
|
||||
Name string `json:"name" bson:"name"` // 标签名称
|
||||
}
|
||||
|
||||
func (q *WebAllListReq) Filter() bson.M {
|
||||
var query = bson.M{}
|
||||
if q.Kind != nil {
|
||||
query["kind"] = q.Kind
|
||||
}
|
||||
if q.Dimension != nil {
|
||||
query["dimension"] = q.Dimension
|
||||
}
|
||||
query["isDelete"] = false
|
||||
return query
|
||||
}
|
||||
func (q *WebAllListReq) Options() *options.FindOptions {
|
||||
return options.Find().SetSort(bson.M{"sortCode": -1})
|
||||
}
|
||||
|
||||
// GetALLList 获取列表
|
||||
func (q *WebAllListReq) GetALLList() (res []*WebAllListRes, err error) {
|
||||
var out []*WebAllListRes
|
||||
data, err := mediatagmod.QueryAllList(q.Filter(), q.Options())
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("mediatagmod GetALLList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
if len(data) > 0 {
|
||||
for _, d := range data {
|
||||
out = append(out, &WebAllListRes{
|
||||
ID: d.ID,
|
||||
Name: d.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
Kind int `json:"kind" form:"kind" binding:"required"` // 种类(1:哩番,2:3D,3:同人动画,4:单行本,5:连载漫画,6:韩漫)
|
||||
Type int `json:"type" form:"type" binding:"required"` // 类型(1:动漫,2:漫画 )
|
||||
Dimension string `json:"dimension" form:"dimension" binding:"required"` // 维度
|
||||
Name string `json:"name" form:"name" binding:"required"` // 标签名称
|
||||
Active bool `json:"active" form:"active" binding:"required"` // 是都激活
|
||||
IsDiscovery bool `json:"isDiscovery" bson:"isDiscovery"` // 是否推荐发现
|
||||
Sort int `json:"sort" form:"sort"` // 排序码
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create(manager string) error {
|
||||
now := time.Now()
|
||||
data := mediatagmod.MediaTag{
|
||||
Kind: p.Kind,
|
||||
Type: p.Type,
|
||||
Dimension: p.Dimension,
|
||||
Name: p.Name,
|
||||
Active: p.Active,
|
||||
Sort: p.Sort,
|
||||
IsDiscovery: p.IsDiscovery,
|
||||
IsDelete: false,
|
||||
UpdatedAct: manager,
|
||||
UpdateTime: now,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := mediatagdata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"` // ID
|
||||
Kind *int `json:"kind"` // 种类(1:哩番,2:3D,3:同人动画,4:单行本,5:连载漫画,6:韩漫)
|
||||
Type *int `json:"type"` // 类型(1:动漫,2:漫画 )
|
||||
Dimension *string `json:"dimension"` // 维度
|
||||
Name *string `json:"name"` // 标签名称
|
||||
Active *bool `json:"active"` // 是否激活
|
||||
IsDiscovery *bool `json:"isDiscovery" bson:"isDiscovery"` // 是否推荐发现
|
||||
Sort *int `json:"sort"` // 排序码
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update(manager string) error {
|
||||
_, err := mediatagmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Kind != nil {
|
||||
data["kind"] = *p.Kind
|
||||
}
|
||||
if p.Type != nil {
|
||||
data["type"] = *p.Type
|
||||
}
|
||||
if p.Dimension != nil {
|
||||
data["dimension"] = *p.Dimension
|
||||
}
|
||||
if p.Name != nil {
|
||||
data["name"] = *p.Name
|
||||
}
|
||||
if p.Active != nil {
|
||||
data["active"] = *p.Active
|
||||
}
|
||||
if p.IsDiscovery != nil {
|
||||
data["isDiscovery"] = *p.IsDiscovery
|
||||
}
|
||||
if p.Sort != nil {
|
||||
data["sort"] = *p.Sort
|
||||
}
|
||||
|
||||
data["updatedAct"] = manager
|
||||
data["updateTime"] = time.Now()
|
||||
|
||||
if _, err = mediatagdata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebDeleteReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"` // 文档ID
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
func (p *WebDeleteReq) Delete() error {
|
||||
if err := mediatagdata.DeleteData(nil, p.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package moduleser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"91porn-server/models/v/moduleconfmod"
|
||||
)
|
||||
|
||||
func TestValidateDramaModuleName(t *testing.T) {
|
||||
for _, name := range []string{"AI短剧", "热门短剧", "Drama Hot"} {
|
||||
if err := validateDramaModuleName(moduleconfmod.Drama, name); err != nil {
|
||||
t.Fatalf("valid module name %q rejected: %v", name, err)
|
||||
}
|
||||
}
|
||||
if err := validateDramaModuleName(moduleconfmod.Drama, "短剧"); err == nil {
|
||||
t.Fatal("ambiguous drama module name unexpectedly accepted")
|
||||
}
|
||||
if err := validateDramaModuleName(1, "任意名称"); err != nil {
|
||||
t.Fatalf("non-drama module was affected: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
package moduleser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/tagmod"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sli "91porn-server/common/slice"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/moduleconfmod"
|
||||
"91porn-server/models/v/modulesectionmod"
|
||||
"91porn-server/models/v/modulevidmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// AddModule 添加模块专题配置
|
||||
func AddModule(mconf moduleconfmod.ModuleConf) error {
|
||||
if err := validateDramaModuleName(mconf.Type, mconf.ModuleName); err != nil {
|
||||
return err
|
||||
}
|
||||
return moduleconfmod.InsertOne(&mconf)
|
||||
}
|
||||
|
||||
func validateDramaModuleName(moduleType int, moduleName string) error {
|
||||
if moduleType != moduleconfmod.Drama {
|
||||
return nil
|
||||
}
|
||||
name := strings.TrimSpace(moduleName)
|
||||
lowerName := strings.ToLower(name)
|
||||
if strings.Contains(lowerName, "ai") || strings.Contains(name, "热门") || strings.Contains(lowerName, "hot") {
|
||||
return nil
|
||||
}
|
||||
return errors.New("短剧模块名称必须包含AI、热门或hot")
|
||||
}
|
||||
|
||||
func AddSection(section modulesectionmod.Section) error {
|
||||
return modulesectionmod.InsertOne(section)
|
||||
}
|
||||
|
||||
// UpdateModule 更新模块专题配置
|
||||
func UpdateModule(editSelector moduleconfmod.EditSelector) error {
|
||||
current, err := moduleconfmod.GetByID(editSelector.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
moduleType := current.Type
|
||||
moduleName := current.ModuleName
|
||||
if editSelector.Type != nil {
|
||||
moduleType = *editSelector.Type
|
||||
}
|
||||
if editSelector.ModuleName != nil {
|
||||
moduleName = *editSelector.ModuleName
|
||||
}
|
||||
if err = validateDramaModuleName(moduleType, moduleName); err != nil {
|
||||
return err
|
||||
}
|
||||
return moduleconfmod.UpdateOne(editSelector)
|
||||
}
|
||||
|
||||
func UpdateSection(editSelector modulesectionmod.EditSelector) error {
|
||||
var tagIds []primitive.ObjectID
|
||||
if editSelector.Tags != nil && len(*editSelector.Tags) > 0 {
|
||||
tags, err := tagmod.FindOneTagByNames(*editSelector.Tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tags) <= 0 {
|
||||
return errors.New("tag is null")
|
||||
}
|
||||
for _, t := range tags {
|
||||
tagIds = append(tagIds, t.ID)
|
||||
}
|
||||
}
|
||||
editSelector.TagIds = tagIds
|
||||
return modulesectionmod.UpdateOne(editSelector)
|
||||
}
|
||||
|
||||
func DeleteSection(id primitive.ObjectID) error {
|
||||
return modulesectionmod.DeleteById(id)
|
||||
}
|
||||
|
||||
// GetModuleList 获取专题下视频列表
|
||||
func GetModuleList(query modulevidmod.QuerySelector, page commod.Page) (resp ModuleVideoList, err error) {
|
||||
list, hasNext, total, err := modulevidmod.Search(&query, page)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
listLen := len(list)
|
||||
sectionIDs := make([]primitive.ObjectID, listLen)
|
||||
videoIDs := make([]primitive.ObjectID, listLen)
|
||||
for i, sec := range list {
|
||||
sectionIDs[i] = sec.SectionID
|
||||
videoIDs[i] = sec.VideoID
|
||||
}
|
||||
sections, err := modulesectionmod.GetByIDs(sli.RemoveRepObjectID(sectionIDs))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sectionModuleMap := make(map[primitive.ObjectID]modulesectionmod.SectionModule)
|
||||
for _, v := range sections {
|
||||
sectionModuleMap[v.ID] = v
|
||||
}
|
||||
videos, err := vidmod.GetVideoListByIDsNoStatus(videoIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
videoMap := make(map[primitive.ObjectID]vidmod.VideoModel)
|
||||
for _, video := range videos {
|
||||
videoMap[video.ID] = *video
|
||||
}
|
||||
resp.List = make([]ModuleVideo, listLen)
|
||||
for i, sec := range list {
|
||||
secVideo := ModuleVideo{
|
||||
ID: sec.ID,
|
||||
SectionID: sec.SectionID,
|
||||
CreatedAt: sec.CreatedAt,
|
||||
SortCode: sec.SortCode,
|
||||
}
|
||||
if module, ok := sectionModuleMap[sec.SectionID]; ok {
|
||||
secVideo.SectionName = module.SectionName
|
||||
if len(module.SectionModule) > 0 {
|
||||
secVideo.ModuleName = module.SectionModule[0].ModuleName
|
||||
secVideo.SubModuleName = module.SectionModule[0].SubModuleName
|
||||
}
|
||||
}
|
||||
secVideo.VideoInfo = VideoInfo{
|
||||
VideoID: videoMap[sec.VideoID].ID,
|
||||
Title: videoMap[sec.VideoID].Title,
|
||||
PublisherID: videoMap[sec.VideoID].PublisherID,
|
||||
Cover: videoMap[sec.VideoID].Cover,
|
||||
}
|
||||
resp.List[i] = secVideo
|
||||
}
|
||||
resp.Total = total
|
||||
resp.HasNext = hasNext
|
||||
return
|
||||
}
|
||||
|
||||
// AddVideoBatch 专题下批量添加视频
|
||||
func AddVideoBatch(req AddVideoBatchReq) error {
|
||||
section, err := modulesectionmod.GetBySectionByID(req.SectionID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if section.ID.IsZero() {
|
||||
return stderr.CodeEmptyData
|
||||
}
|
||||
mId := section.SubModuleID
|
||||
|
||||
videoIdStrs := strings.Split(req.VideoIDs, ",")
|
||||
vids := make([]primitive.ObjectID, len(videoIdStrs))
|
||||
for i, vidIDStr := range videoIdStrs {
|
||||
videoID, err := primitive.ObjectIDFromHex(vidIDStr)
|
||||
if err != nil {
|
||||
return stderr.ErrParamError
|
||||
}
|
||||
vids[i] = videoID
|
||||
}
|
||||
list, err := vidmod.GetVideoListByIDs(vids)
|
||||
if err != nil {
|
||||
return stderr.ErrNetWorkBusy
|
||||
}
|
||||
records := make([]modulevidmod.SectionVideo, len(list))
|
||||
var vIds []primitive.ObjectID
|
||||
var ids []primitive.ObjectID
|
||||
for i := range list {
|
||||
records[i] = modulevidmod.SectionVideo{
|
||||
SectionID: req.SectionID,
|
||||
VideoID: list[i].ID,
|
||||
VideoReviewedAt: list[i].ReviewAt,
|
||||
}
|
||||
vIds = append(vIds, list[i].ID)
|
||||
}
|
||||
sectionVideos, err := modulevidmod.GetSectionsByVideoIds(vIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sectionVideos) > 0 {
|
||||
for i := range sectionVideos {
|
||||
ids = append(ids, sectionVideos[i].ID)
|
||||
}
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
err := modulevidmod.DeleteManyVideo(ids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 批量更新视频信息
|
||||
update := bson.M{}
|
||||
update["mId"] = mId.Hex()
|
||||
update["updatedAt"] = time.Now()
|
||||
|
||||
result, err := vidmod.UpdateManyVideo(vIds, update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result == 0 {
|
||||
return errors.New("video update failed")
|
||||
}
|
||||
|
||||
return modulevidmod.InsertMany(records)
|
||||
}
|
||||
|
||||
// AddVideo 专题下添加一条视频
|
||||
func AddVideo(sectionVid modulevidmod.SectionVideo) error {
|
||||
videoID := sectionVid.VideoID
|
||||
video, err := vidmod.GetVideoInfo(videoID.Hex())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sectionVid.VideoReviewedAt = video.ReviewAt
|
||||
sectionVid.NewsType = video.NewsType
|
||||
return modulevidmod.InsertOne(§ionVid)
|
||||
}
|
||||
|
||||
// AllSections 获取所有专题
|
||||
func AllSections() (data []modulesectionmod.ModuleSection, err error) {
|
||||
modules, err := moduleconfmod.GetAllModule()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sections, err := modulesectionmod.AllSections()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sectionMap := make(map[primitive.ObjectID][]modulesectionmod.Section)
|
||||
for _, s := range sections {
|
||||
sectionMap[s.SubModuleID] = append(sectionMap[s.SubModuleID], s)
|
||||
}
|
||||
data = make([]modulesectionmod.ModuleSection, len(modules))
|
||||
for i, m := range modules {
|
||||
data[i] = modulesectionmod.ModuleSection{
|
||||
ModuleConf: m,
|
||||
Sections: sectionMap[m.ID],
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SectionSearch(q modulesectionmod.QuerySelector, p commod.Page) (resp modulesectionmod.ListResp, err error) {
|
||||
sections, hasNext, total, err := modulesectionmod.Search(q, p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp.HasNext = hasNext
|
||||
resp.Total = total
|
||||
sectionsLen := len(sections)
|
||||
originalUserIDs := make([]uint64, 0, sectionsLen)
|
||||
subModuleIDs := make([]primitive.ObjectID, 0, sectionsLen)
|
||||
for _, section := range sections {
|
||||
if section.OriginalUserID != nil && *section.OriginalUserID != 0 {
|
||||
originalUserIDs = append(originalUserIDs, *section.OriginalUserID)
|
||||
}
|
||||
if !section.SubModuleID.IsZero() {
|
||||
subModuleIDs = append(subModuleIDs, section.SubModuleID)
|
||||
}
|
||||
}
|
||||
users, err := usermod.FindUsersByUID(originalUserIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
subModules, err := moduleconfmod.FindByIDs(subModuleIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
originalBloggerInfo := make(map[uint64]usermod.User)
|
||||
subModuleInfo := make(map[primitive.ObjectID]moduleconfmod.ModuleConf)
|
||||
for _, user := range users {
|
||||
originalBloggerInfo[user.UID] = *user
|
||||
}
|
||||
for _, module := range subModules {
|
||||
subModuleInfo[module.ID] = module
|
||||
}
|
||||
resp.List = make([]modulesectionmod.SectionDetail, sectionsLen)
|
||||
for i, section := range sections {
|
||||
detail := modulesectionmod.SectionDetail{
|
||||
Section: section,
|
||||
}
|
||||
if section.OriginalUserID != nil && *section.OriginalUserID != 0 {
|
||||
detail.OriginalBloggerInfo = modulesectionmod.OriginalBloggerInfo{
|
||||
Name: originalBloggerInfo[*section.OriginalUserID].Name,
|
||||
Portrait: originalBloggerInfo[*section.OriginalUserID].Portrait,
|
||||
OfficialCert: originalBloggerInfo[*section.OriginalUserID].OfficialCert,
|
||||
}
|
||||
}
|
||||
if !section.SubModuleID.IsZero() {
|
||||
detail.ModuleInfo = modulesectionmod.ModuleInfo{
|
||||
ModuleName: subModuleInfo[section.SubModuleID].ModuleName,
|
||||
SubModuleName: subModuleInfo[section.SubModuleID].SubModuleName,
|
||||
SectionLimit: subModuleInfo[detail.SubModuleID].SectionLimit,
|
||||
}
|
||||
}
|
||||
resp.List[i] = detail
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// QueryAllCartoonList 查询动漫模块专题配置
|
||||
func QueryAllCartoonList() ([]*modulesectionmod.AllSectionConf, error) {
|
||||
var out []*modulesectionmod.AllSectionConf
|
||||
filter := bson.M{"type": bson.M{"$in": []int{moduleconfmod.Novel, moduleconfmod.Comics, moduleconfmod.Cartoon}}, "status": 1, "deletedAt": bson.M{"$exists": false}}
|
||||
op := options.Find().SetSort(bson.M{"sortNum": -1})
|
||||
list, err := moduleconfmod.QueryAllList(filter, op)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("moduleconfmod QueryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
var subIds []primitive.ObjectID
|
||||
if len(list) > 0 {
|
||||
for _, m := range list {
|
||||
subIds = append(subIds, m.ID)
|
||||
}
|
||||
}
|
||||
sf := bson.M{"subModuleID": bson.M{"$in": subIds}, "status": 1, "deletedAt": bson.M{"$exists": false}}
|
||||
sop := options.Find().SetSort(bson.M{"sort": -1})
|
||||
sections, err := modulesectionmod.QueryAllList(sf, sop)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("modulesectionmod QueryAllList error:%+v:", err))
|
||||
return nil, err
|
||||
}
|
||||
if len(sections) > 0 {
|
||||
for _, m := range list {
|
||||
mes := &modulesectionmod.AllSectionConf{
|
||||
ID: m.ID,
|
||||
ModuleName: m.ModuleName,
|
||||
}
|
||||
var allSection []modulesectionmod.SectionConf
|
||||
for _, s := range sections {
|
||||
if s.SubModuleID.Hex() == m.ID.Hex() {
|
||||
nes := modulesectionmod.SectionConf{
|
||||
ID: s.ID,
|
||||
SectionName: s.SectionName,
|
||||
}
|
||||
allSection = append(allSection, nes)
|
||||
}
|
||||
}
|
||||
mes.AllSection = allSection
|
||||
out = append(out, mes)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package moduleser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ModuleVideoList struct {
|
||||
HasNext bool `json:"hasNext"` // 是否还有下一页
|
||||
List []ModuleVideo `json:"list"` // 列表
|
||||
Total int64 `json:"total"` // 总数
|
||||
}
|
||||
|
||||
type ModuleVideo struct {
|
||||
ID primitive.ObjectID `json:"id"` // ID
|
||||
SectionID primitive.ObjectID `json:"sectionID"` // 专题id
|
||||
SectionName string `json:"sectionName"` // 专题名称
|
||||
ModuleName string `json:"moduleName"` // 模块名称
|
||||
SubModuleName string `json:"subModuleName"` // 亚模块名称
|
||||
SortCode int `json:"sortCode"` // 专题内排序
|
||||
CreatedAt time.Time `json:"createdAt"` // 创建时间
|
||||
VideoInfo // 视频信息
|
||||
}
|
||||
|
||||
type VideoInfo struct {
|
||||
VideoID primitive.ObjectID `json:"videoID"` // 视频ID
|
||||
Title string `json:"title"` // 标题
|
||||
PublisherID uint64 `json:"publisherID"` // 发布用户ID
|
||||
Cover string `json:"cover"` // 视频封面图
|
||||
}
|
||||
|
||||
type AddVideoBatchReq struct {
|
||||
SectionID primitive.ObjectID `json:"sectionID" bson:"sectionID" binding:"required"` // 专题id
|
||||
VideoIDs string `json:"videoIDs" binding:"required"` // 视频id列表,各个id以','分隔
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package nakedchatorderser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/nakedchatorderdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/nakedchatordermod"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
Uid *uint64 `json:"uid" form:"uid"` // 用户id
|
||||
OrderId *string `json:"orderId" form:"orderId"` // 订单id
|
||||
Nid *string `json:"nid" bson:"nid"` // 裸聊id
|
||||
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []nakedchatordermod.NakedChatOrder `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
filter := bson.M{}
|
||||
if q.OrderId != nil {
|
||||
orderId, _ := primitive.ObjectIDFromHex(*q.OrderId)
|
||||
filter["_id"] = orderId
|
||||
}
|
||||
if q.Uid != nil {
|
||||
filter["uid"] = *q.Uid
|
||||
}
|
||||
if q.Nid != nil {
|
||||
nid, _ := primitive.ObjectIDFromHex(*q.Nid)
|
||||
filter["nid"] = nid
|
||||
}
|
||||
res.List, res.Total, res.HasNext, err = nakedchatordermod.GetList(filter, int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
Status *int `json:"status"` // 0-已下单 1-已完成
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
_, err := nakedchatordermod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
|
||||
if _, err = nakedchatorderdata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Executable
+201
@@ -0,0 +1,201 @@
|
||||
package nakedchatser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/cache/nakedchatdata"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/nakedchatmod"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type WebListReq struct {
|
||||
Title *string `json:"title" form:"title"`
|
||||
Status *int `json:"status" form:"status"` // 0-下架 1-上架
|
||||
Mid *string `json:"mid" form:"mid"` // 模块id
|
||||
commod.Page
|
||||
}
|
||||
type WebListRes struct {
|
||||
Total int64 `json:"total"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
List []nakedchatmod.NakedChat `json:"list"`
|
||||
}
|
||||
|
||||
// GetList 获取列表
|
||||
func (q *WebListReq) GetList() (res WebListRes, err error) {
|
||||
filter := bson.M{}
|
||||
if q.Title != nil {
|
||||
filter["title"] = bson.M{
|
||||
"$regex": *q.Title,
|
||||
}
|
||||
}
|
||||
if q.Status != nil {
|
||||
filter["status"] = *q.Status
|
||||
}
|
||||
if q.Mid != nil {
|
||||
mid, _ := primitive.ObjectIDFromHex(*q.Mid)
|
||||
filter["mid"] = mid
|
||||
}
|
||||
res.List, res.Total, res.HasNext, err = nakedchatmod.GetList(filter, int64(q.Skip()), int64(q.Limit()), q.GetSort())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type WebCreateReq struct {
|
||||
Title string `json:"title" form:"title" binding:"required"` // 标题
|
||||
Cover string `json:"cover" form:"cover" binding:"required"` // 封面
|
||||
Price uint64 `json:"price" form:"price" binding:"required"` // 价格 n金币/分钟
|
||||
Options []uint64 `json:"options" form:"options" binding:"required"` // 可供购买选项 ,多少分钟
|
||||
Images []string `json:"images" form:"images"` // 图片列表
|
||||
Video string `json:"video" form:"video"` // 展示的视频
|
||||
Contact string `json:"contact" form:"contact" binding:"required"` // 联系方式
|
||||
Age int `json:"age" form:"age" binding:"required"` // 年龄 单位 岁
|
||||
Weight int `json:"weight" form:"weight" binding:"required"` // 体重 单位 kg
|
||||
Height int `json:"height" form:"height" binding:"required"` // 身高 单位 cm
|
||||
Cup string `json:"cup" form:"cup" binding:"required"` // 罩杯
|
||||
FakeSaleNum int64 `json:"fakeSaleNum" form:"fakeSaleNum" binding:"required"` // 销售数量(假)
|
||||
BusinessHours string `json:"businessHours" form:"businessHours" binding:"required"` // 连线时间
|
||||
Summary string `json:"summary" form:"summary"` // 简介
|
||||
Status int `json:"status" form:"status"` // 0-下架 1-上架
|
||||
SortCode int64 `json:"sortCode" bson:"sortCode"` // 排序号
|
||||
|
||||
}
|
||||
|
||||
// Create 发布数据
|
||||
func (p *WebCreateReq) Create() error {
|
||||
data := nakedchatmod.NakedChat{
|
||||
Title: p.Title,
|
||||
Cover: p.Cover,
|
||||
Price: p.Price,
|
||||
Options: p.Options,
|
||||
Images: p.Images,
|
||||
Video: p.Video,
|
||||
Contact: p.Contact,
|
||||
Age: p.Age,
|
||||
Weight: p.Weight,
|
||||
Height: p.Height,
|
||||
Cup: p.Cup,
|
||||
FakeSaleNum: p.FakeSaleNum,
|
||||
BusinessHours: p.BusinessHours,
|
||||
Summary: p.Summary,
|
||||
Status: p.Status,
|
||||
SortCode: p.SortCode,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
if _, err := nakedchatdata.InsertData(nil, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebUpdateReq struct {
|
||||
ID primitive.ObjectID `json:"id" binding:"required"`
|
||||
Title *string `json:"title"` // 标题
|
||||
Cover *string `json:"cover"` // 封面
|
||||
Price *int64 `json:"price"` // 价格 n金币/分钟
|
||||
Options *[]int64 `json:"options"` // 可供购买选项 ,多少分钟
|
||||
Images *[]string `json:"images"` // 图片列表
|
||||
Video *string `json:"video"` // 展示的视频
|
||||
Contact *string `json:"contact"` // 联系方式
|
||||
Age *int `json:"age"` // 年龄 单位 岁
|
||||
Weight *int `json:"weight"` // 体重 单位 kg
|
||||
Height *int `json:"height"` // 身高 单位 cm
|
||||
Cup *string `json:"cup"` // 罩杯
|
||||
FakeSaleNum *int64 `json:"fakeSaleNum"` // 销售数量(假)
|
||||
BusinessHours *string `json:"businessHours"` // 连线时间
|
||||
Summary *string `json:"summary"` // 简介
|
||||
Status *int `json:"status" bson:"status"` // 0-下架 1-上架
|
||||
SortCode *int64 `json:"sortCode" bson:"sortCode"` // 排序号
|
||||
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebUpdateReq) Update() error {
|
||||
_, err := nakedchatmod.GetInfo(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
if p.Title != nil {
|
||||
data["title"] = *p.Title
|
||||
}
|
||||
if p.Cover != nil {
|
||||
data["cover"] = *p.Cover
|
||||
}
|
||||
if p.Price != nil {
|
||||
data["price"] = *p.Price
|
||||
}
|
||||
if p.Options != nil {
|
||||
data["options"] = *p.Options
|
||||
}
|
||||
if p.Images != nil {
|
||||
data["images"] = *p.Images
|
||||
}
|
||||
if p.Video != nil {
|
||||
data["video"] = *p.Video
|
||||
}
|
||||
if p.Contact != nil {
|
||||
data["contact"] = *p.Contact
|
||||
}
|
||||
if p.Age != nil {
|
||||
data["age"] = *p.Age
|
||||
}
|
||||
if p.Weight != nil {
|
||||
data["weight"] = *p.Weight
|
||||
}
|
||||
if p.Height != nil {
|
||||
data["height"] = *p.Height
|
||||
}
|
||||
if p.Cup != nil {
|
||||
data["cup"] = *p.Cup
|
||||
}
|
||||
if p.FakeSaleNum != nil {
|
||||
data["fakeSaleNum"] = *p.FakeSaleNum
|
||||
}
|
||||
if p.BusinessHours != nil {
|
||||
data["businessHours"] = *p.BusinessHours
|
||||
}
|
||||
if p.Summary != nil {
|
||||
data["summary"] = *p.Summary
|
||||
}
|
||||
if p.Status != nil {
|
||||
data["status"] = *p.Status
|
||||
}
|
||||
if p.SortCode != nil {
|
||||
data["sortCode"] = *p.SortCode
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
|
||||
if _, err = nakedchatdata.UpdateData(nil, p.ID.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebBatchUpdateReq struct {
|
||||
Ids []primitive.ObjectID `json:"ids" binding:"required"`
|
||||
Mid *primitive.ObjectID `json:"mid" form:"mid"` // 模块id
|
||||
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
func (p *WebBatchUpdateReq) Update() error {
|
||||
data := make(map[string]interface{})
|
||||
if p.Mid != nil {
|
||||
data["mid"] = *p.Mid
|
||||
}
|
||||
data["updatedAt"] = time.Now()
|
||||
for _, id := range p.Ids {
|
||||
if _, err := nakedchatdata.UpdateData(nil, id.Hex(), data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListAlbum(req *AlbumListReq) (int64, []officialwebsitemod.Album, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.Album{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
// resolveAlbumSeoSlug 处理 seoSlug:手填则校验格式并查重(冲突报错);为空则由标题生成雏形并追加后缀去重。
|
||||
func resolveAlbumSeoSlug(provided, title string, excludeID primitive.ObjectID) (string, error) {
|
||||
return ResolveSeoSlug(provided, title, func(slug string) (bool, error) {
|
||||
filter := officialwebsitemod.M{"seoSlug": slug}
|
||||
if !excludeID.IsZero() {
|
||||
filter["_id"] = officialwebsitemod.M{"$ne": excludeID}
|
||||
}
|
||||
n, err := (&officialwebsitemod.Album{}).Count(filter)
|
||||
return n > 0, err
|
||||
})
|
||||
}
|
||||
|
||||
func CreateAlbum(req *ModifyAlbumReq) (officialwebsitemod.Album, error) {
|
||||
data, err := req.toMod()
|
||||
if err != nil {
|
||||
return officialwebsitemod.Album{}, err
|
||||
}
|
||||
if data.SeoSlug, err = resolveAlbumSeoSlug(data.SeoSlug, data.Title, primitive.NilObjectID); err != nil {
|
||||
return officialwebsitemod.Album{}, err
|
||||
}
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.Album{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdateAlbum(req *ModifyAlbumReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Album{}
|
||||
err = mod.FindOne(officialwebsitemod.M{"_id": id})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(mod.ID) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
// 若提交了 seoSlug,先校验/去重(排除自身),回写后由 toM 落库
|
||||
if req.SeoSlug != nil {
|
||||
var slug string
|
||||
if slug, err = resolveAlbumSeoSlug(*req.SeoSlug, mod.Title, id); err != nil {
|
||||
return
|
||||
}
|
||||
*req.SeoSlug = slug
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteAlbum(req *DeleteAlbumReq) (data DeleteAlbumResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Album{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyAlbumReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
HeroID *string `json:"heroID" form:"heroID"`
|
||||
Title *string `json:"title"`
|
||||
SeoSlug *string `json:"seoSlug"`
|
||||
Description *string `json:"description"`
|
||||
Cover *string `json:"cover"`
|
||||
IsHot *bool `json:"isHot"`
|
||||
Tags *[]officialwebsitemod.Tag `json:"tags"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteAlbumReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteAlbumResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyAlbumReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyAlbumReq) toMod() (officialwebsitemod.Album, error) {
|
||||
if req == nil {
|
||||
return officialwebsitemod.Album{}, nil
|
||||
}
|
||||
data := officialwebsitemod.Album{
|
||||
Title: stringOrZero(req.Title),
|
||||
Description: stringOrZero(req.Description),
|
||||
Cover: stringOrZero(req.Cover),
|
||||
IsHot: boolOrZero(req.IsHot),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
}
|
||||
if req.HeroID != nil {
|
||||
if *req.HeroID != "" {
|
||||
heroID, err := primitive.ObjectIDFromHex(*req.HeroID)
|
||||
if err != nil || isNilObjectID(heroID) {
|
||||
return officialwebsitemod.Album{}, stderr.ErrParamError
|
||||
}
|
||||
data.HeroID = heroID
|
||||
}
|
||||
}
|
||||
if req.Tags != nil {
|
||||
data.Tags = *req.Tags
|
||||
}
|
||||
if req.SeoSlug != nil {
|
||||
data.SeoSlug = *req.SeoSlug
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyAlbumReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyAlbumReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.HeroID != nil {
|
||||
if *req.HeroID != "" {
|
||||
heroID, err := primitive.ObjectIDFromHex(*req.HeroID)
|
||||
if err != nil || isNilObjectID(heroID) {
|
||||
return nil, stderr.ErrParamError
|
||||
}
|
||||
set["heroID"] = heroID
|
||||
}
|
||||
}
|
||||
if req.Title != nil {
|
||||
set["title"] = *req.Title
|
||||
}
|
||||
if req.SeoSlug != nil && *req.SeoSlug != "" {
|
||||
set["seoSlug"] = *req.SeoSlug
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.Cover != nil {
|
||||
set["cover"] = *req.Cover
|
||||
}
|
||||
if req.IsHot != nil {
|
||||
set["isHot"] = *req.IsHot
|
||||
}
|
||||
if req.Tags != nil {
|
||||
set["tags"] = *req.Tags
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type AlbumListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
HeroID *string `form:"heroID"`
|
||||
Title *string `form:"title"`
|
||||
Description *string `form:"description"`
|
||||
Cover *string `form:"cover"`
|
||||
IsHot *bool `form:"isHot"`
|
||||
Tags *string `form:"tags"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *AlbumListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseObjectIDFilter(filter, "heroID", r.HeroID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Title != nil {
|
||||
filter["title"] = *r.Title
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.Cover != nil {
|
||||
filter["cover"] = *r.Cover
|
||||
}
|
||||
if r.IsHot != nil {
|
||||
filter["isHot"] = *r.IsHot
|
||||
}
|
||||
if err := parseJSONFilter(filter, "tags", r.Tags, &[]officialwebsitemod.Tag{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Sort != nil {
|
||||
filter["sort"] = *r.Sort
|
||||
}
|
||||
if r.IsActive != nil {
|
||||
filter["isActive"] = *r.IsActive
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
var seoSlugBackfillCron *cron.Cron
|
||||
|
||||
// StartSeoSlugBackfillCron 启动定时任务:扫描官网各表(Video/Hero/Album/News/Photograph)
|
||||
// 中缺失 seoSlug 的记录,按标题自动生成并回填。
|
||||
// 启动后异步先跑一次(补存量),之后每小时扫一次(兜底导入/批量遗漏的记录)。
|
||||
func StartSeoSlugBackfillCron() {
|
||||
go backfillAllSeoSlug() // 首次异步执行,避免阻塞服务启动
|
||||
seoSlugBackfillCron = cron.New()
|
||||
if _, err := seoSlugBackfillCron.AddFunc("@every 1h", backfillAllSeoSlug); err != nil {
|
||||
log.Error("StartSeoSlugBackfillCron AddFunc fail", log.E(err))
|
||||
return
|
||||
}
|
||||
seoSlugBackfillCron.Start()
|
||||
}
|
||||
|
||||
func backfillAllSeoSlug() {
|
||||
backfillVideoSeoSlug()
|
||||
backfillHeroSeoSlug()
|
||||
backfillAlbumSeoSlug()
|
||||
backfillNewsSeoSlug()
|
||||
backfillPhotographSeoSlug()
|
||||
}
|
||||
|
||||
// seoSlugMissingFilter 匹配 seoSlug 字段缺失或为空的记录。
|
||||
func seoSlugMissingFilter() officialwebsitemod.M {
|
||||
return officialwebsitemod.M{"$or": []officialwebsitemod.M{
|
||||
{"seoSlug": officialwebsitemod.M{"$exists": false}},
|
||||
{"seoSlug": ""},
|
||||
}}
|
||||
}
|
||||
|
||||
// setSeoSlug 回填单条记录的 seoSlug。
|
||||
func setSeoSlug(update func(officialwebsitemod.M, officialwebsitemod.M) (int64, error), id interface{}, slug string) {
|
||||
if _, err := update(officialwebsitemod.M{"_id": id}, officialwebsitemod.M{"$set": officialwebsitemod.M{"seoSlug": slug}}); err != nil {
|
||||
log.Error("backfill seoSlug update fail", log.E(err), log.Any("id", id))
|
||||
}
|
||||
}
|
||||
|
||||
func backfillVideoSeoSlug() {
|
||||
mod := &officialwebsitemod.Video{}
|
||||
list, err := mod.FindMany(seoSlugMissingFilter())
|
||||
if err != nil {
|
||||
log.Error("backfill video seoSlug find fail", log.E(err))
|
||||
return
|
||||
}
|
||||
for i := range list {
|
||||
if list[i].Title == "" {
|
||||
continue
|
||||
}
|
||||
slug, e := resolveVideoSeoSlug("", list[i].Title, list[i].ID)
|
||||
if e != nil || slug == "" {
|
||||
continue
|
||||
}
|
||||
setSeoSlug(mod.Update, list[i].ID, slug)
|
||||
}
|
||||
}
|
||||
|
||||
func backfillHeroSeoSlug() {
|
||||
mod := &officialwebsitemod.Hero{}
|
||||
list, err := mod.FindMany(seoSlugMissingFilter())
|
||||
if err != nil {
|
||||
log.Error("backfill hero seoSlug find fail", log.E(err))
|
||||
return
|
||||
}
|
||||
for i := range list {
|
||||
if list[i].Name == "" {
|
||||
continue
|
||||
}
|
||||
slug, e := resolveHeroSeoSlug("", list[i].Name, list[i].ID)
|
||||
if e != nil || slug == "" {
|
||||
continue
|
||||
}
|
||||
setSeoSlug(mod.Update, list[i].ID, slug)
|
||||
}
|
||||
}
|
||||
|
||||
func backfillAlbumSeoSlug() {
|
||||
mod := &officialwebsitemod.Album{}
|
||||
list, err := mod.FindMany(seoSlugMissingFilter())
|
||||
if err != nil {
|
||||
log.Error("backfill album seoSlug find fail", log.E(err))
|
||||
return
|
||||
}
|
||||
for i := range list {
|
||||
if list[i].Title == "" {
|
||||
continue
|
||||
}
|
||||
slug, e := resolveAlbumSeoSlug("", list[i].Title, list[i].ID)
|
||||
if e != nil || slug == "" {
|
||||
continue
|
||||
}
|
||||
setSeoSlug(mod.Update, list[i].ID, slug)
|
||||
}
|
||||
}
|
||||
|
||||
func backfillNewsSeoSlug() {
|
||||
mod := &officialwebsitemod.News{}
|
||||
list, err := mod.FindMany(seoSlugMissingFilter())
|
||||
if err != nil {
|
||||
log.Error("backfill news seoSlug find fail", log.E(err))
|
||||
return
|
||||
}
|
||||
for i := range list {
|
||||
if list[i].Title == "" {
|
||||
continue
|
||||
}
|
||||
slug, e := resolveNewsSeoSlug("", list[i].Title, list[i].ID)
|
||||
if e != nil || slug == "" {
|
||||
continue
|
||||
}
|
||||
setSeoSlug(mod.Update, list[i].ID, slug)
|
||||
}
|
||||
}
|
||||
|
||||
func backfillPhotographSeoSlug() {
|
||||
mod := &officialwebsitemod.Photograph{}
|
||||
list, err := mod.FindMany(seoSlugMissingFilter())
|
||||
if err != nil {
|
||||
log.Error("backfill photograph seoSlug find fail", log.E(err))
|
||||
return
|
||||
}
|
||||
for i := range list {
|
||||
if list[i].Name == "" {
|
||||
continue
|
||||
}
|
||||
slug, e := resolvePhotographSeoSlug("", list[i].Name, list[i].ID)
|
||||
if e != nil || slug == "" {
|
||||
continue
|
||||
}
|
||||
setSeoSlug(mod.Update, list[i].ID, slug)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,779 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/cachev2"
|
||||
"91porn-server/common/constant/redisconst"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func GetBasicData() (data GetBasicDataResp, err error) {
|
||||
mod := &officialwebsitemod.BasicData{}
|
||||
if err = mod.FindOne(); err != nil {
|
||||
return
|
||||
}
|
||||
err = data.transfer(mod)
|
||||
return
|
||||
}
|
||||
|
||||
func CreateBasicData(req *ModifyBasicDataReq) (data officialwebsitemod.BasicData, err error) {
|
||||
data, err = req.toMod()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = data.Create(); err != nil {
|
||||
return officialwebsitemod.BasicData{}, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func UpdateBasicData(req *ModifyBasicDataReq) (resp UpdateBasicDataResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.BasicData{}
|
||||
if err = mod.FindOne(); err != nil {
|
||||
return
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
|
||||
common.Go(func() {
|
||||
// 删除缓存
|
||||
cachev2.Classes().
|
||||
CacheTime(redisconst.OfficialWebsiteBasicDataCacheExpire).
|
||||
AutoListKey(redisconst.OfficialWebsiteBasicDataCacheKey).
|
||||
DeleteCurrent()
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteBasicData(req *DeleteBasicDataReq) (data DeleteBasicDataResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.BasicData{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Query (BasicData) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type GetBasicDataReq struct{}
|
||||
|
||||
type GetBasicDataResp struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
Title string `json:"title" bson:"title"`
|
||||
Description string `json:"description" bson:"description"`
|
||||
Banner []GBDBanner `json:"banner" bson:"banner"`
|
||||
HeroBanner []GBDHeroBanner `json:"heroBanner" bson:"heroBanner"`
|
||||
Masterpiece []GBDMasterpiece `json:"masterpiece" bson:"masterpiece"`
|
||||
Business []GBDBusiness `json:"business" bson:"business"`
|
||||
BannersDescription string `json:"bannersDescription" bson:"bannersDescription"`
|
||||
BannersDuration int `json:"bannersDuration" bson:"bannersDuration"`
|
||||
AboutUs GBDAboutUs `json:"aboutUs" bson:"aboutUs"`
|
||||
FAQ []GBDFAQ `json:"faq" bson:"faq"`
|
||||
HomePageCMS []GBDCMSData `json:"homePageCMS" bson:"homePageCMS"`
|
||||
HomePageCMSDescription string `json:"homePageCMSDescription" bson:"homePageCMSDescription"`
|
||||
RecruitCMS []GBDCMSData `json:"recruitCMS" bson:"recruitCMS"`
|
||||
Tags []officialwebsitemod.Tag `json:"tags" bson:"tags"`
|
||||
}
|
||||
|
||||
type GBDBanner struct {
|
||||
Name string `json:"name" bson:"name"`
|
||||
Description string `json:"description" bson:"description"`
|
||||
Image string `json:"image" bson:"image"`
|
||||
PhoneImage string `json:"phoneImage" bson:"phoneImage"`
|
||||
Thumbnail string `json:"thumbnail" bson:"thumbnail"`
|
||||
URL string `json:"url" bson:"url"`
|
||||
}
|
||||
|
||||
type GBDHeroBanner struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
HeroID string `json:"heroId" bson:"heroId"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Description string `json:"description" bson:"description"`
|
||||
Image string `json:"image" bson:"image"`
|
||||
URL string `json:"url" bson:"url"`
|
||||
Duration int `json:"duration" bson:"duration"`
|
||||
}
|
||||
|
||||
type GBDMasterpiece struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Description string `json:"description" bson:"description"`
|
||||
Cover string `json:"cover" bson:"cover"`
|
||||
Type string `json:"type" bson:"type"`
|
||||
}
|
||||
|
||||
type GBDBusiness struct {
|
||||
Name string `json:"name" bson:"name"`
|
||||
EnglishName string `json:"englishName" bson:"englishName"`
|
||||
Description string `json:"description" bson:"description"`
|
||||
Cover string `json:"cover" bson:"cover"`
|
||||
Detail []GBDBusinessDetail `json:"detail" bson:"detail"`
|
||||
}
|
||||
|
||||
type GBDBusinessDetail struct {
|
||||
Cover string `json:"cover" bson:"cover"`
|
||||
Description string `json:"description" bson:"description"`
|
||||
}
|
||||
|
||||
type GBDAboutUs struct {
|
||||
Description string `json:"description" bson:"description"`
|
||||
Companies []GBDCompanyInfo `json:"companies" bson:"companies"`
|
||||
Values string `json:"values" bson:"values"`
|
||||
ValuesImg string `json:"valuesImg" bson:"valuesImg"`
|
||||
}
|
||||
|
||||
type GBDCompanyInfo struct {
|
||||
Name string `json:"name" bson:"name"`
|
||||
Address string `json:"address" bson:"address"`
|
||||
Description string `json:"description" bson:"description"`
|
||||
Contact []GBDContactInfo `json:"contact" bson:"contact"`
|
||||
}
|
||||
|
||||
type GBDContactInfo struct {
|
||||
Type string `json:"type" bson:"type"`
|
||||
Value string `json:"value" bson:"value"`
|
||||
}
|
||||
|
||||
type GBDFAQ struct {
|
||||
Question string `json:"question" bson:"question"`
|
||||
Answer string `json:"answer" bson:"answer"`
|
||||
}
|
||||
|
||||
type GBDCMSData struct {
|
||||
Description string `json:"description" bson:"description"`
|
||||
Value string `json:"value" bson:"value"`
|
||||
}
|
||||
|
||||
func (data *GetBasicDataResp) transfer(mod *officialwebsitemod.BasicData) error {
|
||||
if mod == nil {
|
||||
return stderr.CodeEmptyData
|
||||
}
|
||||
data.ID = mod.ID
|
||||
data.Title = mod.Title
|
||||
data.Description = mod.Description
|
||||
data.Tags = mod.Tags
|
||||
data.BannersDescription = mod.BannersDescription
|
||||
data.BannersDuration = mod.BannersDuration
|
||||
data.HomePageCMSDescription = mod.HomePageCMSDescription
|
||||
data.AboutUs.Description = mod.AboutUs.Description
|
||||
data.AboutUs.Values = mod.AboutUs.Values
|
||||
data.AboutUs.ValuesImg = mod.AboutUs.ValuesImg
|
||||
|
||||
data.Banner = make([]GBDBanner, 0, len(mod.Banner))
|
||||
for _, v := range mod.Banner {
|
||||
data.Banner = append(data.Banner, GBDBanner{
|
||||
Name: v.Name, Description: v.Description, Image: v.Image, PhoneImage: v.PhoneImage, Thumbnail: v.Thumbnail, URL: v.URL,
|
||||
})
|
||||
}
|
||||
data.HeroBanner = make([]GBDHeroBanner, 0, len(mod.HeroBanner))
|
||||
for _, v := range mod.HeroBanner {
|
||||
data.HeroBanner = append(data.HeroBanner, GBDHeroBanner{
|
||||
ID: v.ID, HeroID: v.HeroID.Hex(), Name: v.Name, Description: v.Description, Image: v.Image, URL: v.URL, Duration: v.Duration,
|
||||
})
|
||||
}
|
||||
data.Masterpiece = make([]GBDMasterpiece, 0, len(mod.Masterpiece))
|
||||
for _, v := range mod.Masterpiece {
|
||||
data.Masterpiece = append(data.Masterpiece, GBDMasterpiece{
|
||||
ID: v.ID, Name: v.Name, Description: v.Description, Cover: v.Cover, Type: v.Type,
|
||||
})
|
||||
}
|
||||
data.Business = make([]GBDBusiness, 0, len(mod.Business))
|
||||
for _, v := range mod.Business {
|
||||
business := GBDBusiness{
|
||||
Name: v.Name, EnglishName: v.EnglishName, Description: v.Description, Cover: v.Cover,
|
||||
Detail: make([]GBDBusinessDetail, 0, len(v.Detail)),
|
||||
}
|
||||
for _, d := range v.Detail {
|
||||
business.Detail = append(business.Detail, GBDBusinessDetail{
|
||||
Cover: d.Cover, Description: d.Description,
|
||||
})
|
||||
}
|
||||
data.Business = append(data.Business, business)
|
||||
}
|
||||
data.AboutUs.Companies = make([]GBDCompanyInfo, 0, len(mod.AboutUs.Companies))
|
||||
for _, v := range mod.AboutUs.Companies {
|
||||
company := GBDCompanyInfo{
|
||||
Name: v.Name, Address: v.Address, Description: v.Description,
|
||||
Contact: make([]GBDContactInfo, 0, len(v.Contact)),
|
||||
}
|
||||
for _, c := range v.Contact {
|
||||
company.Contact = append(company.Contact, GBDContactInfo{Type: c.Type, Value: c.Value})
|
||||
}
|
||||
data.AboutUs.Companies = append(data.AboutUs.Companies, company)
|
||||
}
|
||||
data.FAQ = make([]GBDFAQ, 0, len(mod.FAQ))
|
||||
for _, v := range mod.FAQ {
|
||||
data.FAQ = append(data.FAQ, GBDFAQ{Question: v.Question, Answer: v.Answer})
|
||||
}
|
||||
data.HomePageCMS = make([]GBDCMSData, 0, len(mod.HomePageCMS))
|
||||
for _, v := range mod.HomePageCMS {
|
||||
data.HomePageCMS = append(data.HomePageCMS, GBDCMSData{Description: v.Description, Value: v.Value})
|
||||
}
|
||||
data.RecruitCMS = make([]GBDCMSData, 0, len(mod.RecruitCMS))
|
||||
for _, v := range mod.RecruitCMS {
|
||||
data.RecruitCMS = append(data.RecruitCMS, GBDCMSData{Description: v.Description, Value: v.Value})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyBasicDataReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Title *string `json:"title" bson:"title"`
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Banner *[]ModifyBanner `json:"banner" bson:"banner"`
|
||||
HeroBanner *[]ModifyHeroBanner `json:"heroBanner" bson:"heroBanner"`
|
||||
Masterpiece *[]ModifyMasterpiece `json:"masterpiece" bson:"masterpiece"`
|
||||
Business *[]ModifyBusiness `json:"business" bson:"business"`
|
||||
BannersDescription *string `json:"bannersDescription" bson:"bannersDescription"`
|
||||
BannersDuration *int `json:"bannersDuration" bson:"bannersDuration"`
|
||||
AboutUs *ModifyAboutUs `json:"aboutUs" bson:"aboutUs"`
|
||||
FAQ *[]ModifyFAQ `json:"faq" bson:"faq"`
|
||||
HomePageCMS *[]ModifyCMSData `json:"homePageCMS" bson:"homePageCMS"`
|
||||
HomePageCMSDescription *string `json:"homePageCMSDescription" bson:"homePageCMSDescription"`
|
||||
RecruitCMS *[]ModifyCMSData `json:"recruitCMS" bson:"recruitCMS"`
|
||||
Tags *[]officialwebsitemod.Tag `json:"tags" bson:"tags"`
|
||||
}
|
||||
|
||||
type ModifyBanner struct {
|
||||
Name *string `json:"name" bson:"name"`
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Image *string `json:"image" bson:"image"`
|
||||
PhoneImage *string `json:"phoneImage" bson:"phoneImage"`
|
||||
Thumbnail *string `json:"thumbnail" bson:"thumbnail"`
|
||||
URL *string `json:"url" bson:"url"`
|
||||
}
|
||||
|
||||
type ModifyHeroBanner struct {
|
||||
ID *primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
HeroID *primitive.ObjectID `json:"heroId" bson:"heroId"`
|
||||
Name *string `json:"name" bson:"name"`
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Image *string `json:"image" bson:"image"`
|
||||
URL *string `json:"url" bson:"url"`
|
||||
Duration *int `json:"duration" bson:"duration"`
|
||||
}
|
||||
|
||||
type ModifyMasterpiece struct {
|
||||
ID *primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
Name *string `json:"name" bson:"name"`
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Cover *string `json:"cover" bson:"cover"`
|
||||
Type *string `json:"type" bson:"type"`
|
||||
}
|
||||
|
||||
type ModifyBusiness struct {
|
||||
Name *string `json:"name" bson:"name"`
|
||||
EnglishName *string `json:"englishName" bson:"englishName"`
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Cover *string `json:"cover" bson:"cover"`
|
||||
Detail *[]ModifyBusinessDetail `json:"detail" bson:"detail"`
|
||||
}
|
||||
|
||||
type ModifyBusinessDetail struct {
|
||||
Cover *string `json:"cover" bson:"cover"`
|
||||
Description *string `json:"description" bson:"description"`
|
||||
}
|
||||
|
||||
type ModifyAboutUs struct {
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Companies *[]ModifyCompanyInfo `json:"companies" bson:"companies"`
|
||||
Values *string `json:"values" bson:"values"`
|
||||
ValuesImg *string `json:"valuesImg" bson:"valuesImg"`
|
||||
}
|
||||
|
||||
type ModifyCompanyInfo struct {
|
||||
Name *string `json:"name" bson:"name"`
|
||||
Address *string `json:"address" bson:"address"`
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Contact *[]ModifyContactInfo `json:"contact" bson:"contact"`
|
||||
}
|
||||
|
||||
type ModifyContactInfo struct {
|
||||
Type *string `json:"type" bson:"type"`
|
||||
Value *string `json:"value" bson:"value"`
|
||||
}
|
||||
|
||||
type ModifyFAQ struct {
|
||||
Question *string `json:"question" bson:"question"`
|
||||
Answer *string `json:"answer" bson:"answer"`
|
||||
}
|
||||
|
||||
type ModifyCMSData struct {
|
||||
Description *string `json:"description" bson:"description"`
|
||||
Value *string `json:"value" bson:"value"`
|
||||
}
|
||||
|
||||
type UpdateBasicDataResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteBasicDataReq struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type DeleteBasicDataResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — Modify* → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (v ModifyBanner) toMod() officialwebsitemod.Banner {
|
||||
return officialwebsitemod.Banner{
|
||||
Name: stringOrZero(v.Name), Description: stringOrZero(v.Description),
|
||||
Image: stringOrZero(v.Image), Thumbnail: stringOrZero(v.Thumbnail),
|
||||
PhoneImage: stringOrZero(v.PhoneImage), URL: stringOrZero(v.URL),
|
||||
}
|
||||
}
|
||||
|
||||
func (v ModifyHeroBanner) toMod() officialwebsitemod.HeroBanner {
|
||||
return officialwebsitemod.HeroBanner{
|
||||
HeroID: objectIDOrZero(v.HeroID), Name: stringOrZero(v.Name), Description: stringOrZero(v.Description),
|
||||
Image: stringOrZero(v.Image), URL: stringOrZero(v.URL), Duration: intOrZero(v.Duration),
|
||||
}
|
||||
}
|
||||
|
||||
func (v ModifyMasterpiece) toMod() officialwebsitemod.Masterpiece {
|
||||
return officialwebsitemod.Masterpiece{
|
||||
ID: objectIDOrZero(v.ID), Name: stringOrZero(v.Name), Description: stringOrZero(v.Description),
|
||||
Cover: stringOrZero(v.Cover), Type: stringOrZero(v.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func (v ModifyBusiness) toMod() officialwebsitemod.Business {
|
||||
out := officialwebsitemod.Business{
|
||||
Name: stringOrZero(v.Name), EnglishName: stringOrZero(v.EnglishName),
|
||||
Description: stringOrZero(v.Description), Cover: stringOrZero(v.Cover),
|
||||
}
|
||||
if v.Detail != nil {
|
||||
out.Detail = make([]officialwebsitemod.BusinessDetail, 0, len(*v.Detail))
|
||||
for _, d := range *v.Detail {
|
||||
out.Detail = append(out.Detail, d.toMod())
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyBusinessDetail) toMod() officialwebsitemod.BusinessDetail {
|
||||
return officialwebsitemod.BusinessDetail{
|
||||
Cover: stringOrZero(v.Cover), Description: stringOrZero(v.Description),
|
||||
}
|
||||
}
|
||||
|
||||
func (v ModifyContactInfo) toMod() officialwebsitemod.ContactInfo {
|
||||
return officialwebsitemod.ContactInfo{Type: stringOrZero(v.Type), Value: stringOrZero(v.Value)}
|
||||
}
|
||||
|
||||
func (v ModifyCompanyInfo) toMod() officialwebsitemod.CompanyInfo {
|
||||
out := officialwebsitemod.CompanyInfo{
|
||||
Name: stringOrZero(v.Name), Address: stringOrZero(v.Address), Description: stringOrZero(v.Description),
|
||||
}
|
||||
if v.Contact != nil {
|
||||
out.Contact = make([]officialwebsitemod.ContactInfo, 0, len(*v.Contact))
|
||||
for _, c := range *v.Contact {
|
||||
out.Contact = append(out.Contact, c.toMod())
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v *ModifyAboutUs) toMod() officialwebsitemod.AboutUs {
|
||||
if v == nil {
|
||||
return officialwebsitemod.AboutUs{}
|
||||
}
|
||||
out := officialwebsitemod.AboutUs{
|
||||
Description: stringOrZero(v.Description), Values: stringOrZero(v.Values), ValuesImg: stringOrZero(v.ValuesImg),
|
||||
}
|
||||
if v.Companies != nil {
|
||||
out.Companies = make([]officialwebsitemod.CompanyInfo, 0, len(*v.Companies))
|
||||
for _, c := range *v.Companies {
|
||||
out.Companies = append(out.Companies, c.toMod())
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyFAQ) toMod() officialwebsitemod.FAQ {
|
||||
return officialwebsitemod.FAQ{Question: stringOrZero(v.Question), Answer: stringOrZero(v.Answer)}
|
||||
}
|
||||
|
||||
func (v ModifyCMSData) toMod() officialwebsitemod.CMSData {
|
||||
return officialwebsitemod.CMSData{Description: stringOrZero(v.Description), Value: stringOrZero(v.Value)}
|
||||
}
|
||||
|
||||
// toMod 将请求结构体转换为完整的 BasicData model
|
||||
func (req *ModifyBasicDataReq) toMod() (officialwebsitemod.BasicData, error) {
|
||||
if req == nil {
|
||||
return officialwebsitemod.BasicData{}, stderr.CodeEmptyData
|
||||
}
|
||||
data := officialwebsitemod.BasicData{
|
||||
Title: stringOrZero(req.Title),
|
||||
Description: stringOrZero(req.Description),
|
||||
BannersDescription: stringOrZero(req.BannersDescription),
|
||||
BannersDuration: intOrZero(req.BannersDuration),
|
||||
HomePageCMSDescription: stringOrZero(req.HomePageCMSDescription),
|
||||
}
|
||||
if req.AboutUs != nil {
|
||||
data.AboutUs = req.AboutUs.toMod()
|
||||
}
|
||||
if req.Banner != nil {
|
||||
data.Banner = make([]officialwebsitemod.Banner, 0, len(*req.Banner))
|
||||
for _, b := range *req.Banner {
|
||||
data.Banner = append(data.Banner, b.toMod())
|
||||
}
|
||||
}
|
||||
if req.HeroBanner != nil {
|
||||
data.HeroBanner = make([]officialwebsitemod.HeroBanner, 0, len(*req.HeroBanner))
|
||||
for _, h := range *req.HeroBanner {
|
||||
data.HeroBanner = append(data.HeroBanner, h.toMod())
|
||||
}
|
||||
}
|
||||
if req.Masterpiece != nil {
|
||||
data.Masterpiece = make([]officialwebsitemod.Masterpiece, 0, len(*req.Masterpiece))
|
||||
for _, m := range *req.Masterpiece {
|
||||
data.Masterpiece = append(data.Masterpiece, m.toMod())
|
||||
}
|
||||
}
|
||||
if req.Business != nil {
|
||||
data.Business = make([]officialwebsitemod.Business, 0, len(*req.Business))
|
||||
for _, b := range *req.Business {
|
||||
data.Business = append(data.Business, b.toMod())
|
||||
}
|
||||
}
|
||||
if req.FAQ != nil {
|
||||
data.FAQ = make([]officialwebsitemod.FAQ, 0, len(*req.FAQ))
|
||||
for _, f := range *req.FAQ {
|
||||
data.FAQ = append(data.FAQ, f.toMod())
|
||||
}
|
||||
}
|
||||
if req.HomePageCMS != nil {
|
||||
data.HomePageCMS = make([]officialwebsitemod.CMSData, 0, len(*req.HomePageCMS))
|
||||
for _, h := range *req.HomePageCMS {
|
||||
data.HomePageCMS = append(data.HomePageCMS, h.toMod())
|
||||
}
|
||||
}
|
||||
if req.RecruitCMS != nil {
|
||||
data.RecruitCMS = make([]officialwebsitemod.CMSData, 0, len(*req.RecruitCMS))
|
||||
for _, r := range *req.RecruitCMS {
|
||||
data.RecruitCMS = append(data.RecruitCMS, r.toMod())
|
||||
}
|
||||
}
|
||||
if req.Tags != nil {
|
||||
data.Tags = *req.Tags
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — Modify* → officialwebsitemod.M (Mongo update doc)
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (v ModifyBanner) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Name != nil {
|
||||
out["name"] = *v.Name
|
||||
}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
if v.Image != nil {
|
||||
out["image"] = *v.Image
|
||||
}
|
||||
if v.Thumbnail != nil {
|
||||
out["thumbnail"] = *v.Thumbnail
|
||||
}
|
||||
if v.PhoneImage != nil {
|
||||
out["phoneImage"] = *v.PhoneImage
|
||||
}
|
||||
if v.URL != nil {
|
||||
out["url"] = *v.URL
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyHeroBanner) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.ID != nil {
|
||||
out["_id"] = *v.ID
|
||||
}
|
||||
if v.HeroID != nil {
|
||||
out["heroId"] = *v.HeroID
|
||||
}
|
||||
if v.Name != nil {
|
||||
out["name"] = *v.Name
|
||||
}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
if v.Image != nil {
|
||||
out["image"] = *v.Image
|
||||
}
|
||||
if v.URL != nil {
|
||||
out["url"] = *v.URL
|
||||
}
|
||||
if v.Duration != nil {
|
||||
out["duration"] = *v.Duration
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyMasterpiece) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.ID != nil {
|
||||
out["_id"] = *v.ID
|
||||
}
|
||||
if v.Name != nil {
|
||||
out["name"] = *v.Name
|
||||
}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
if v.Cover != nil {
|
||||
out["cover"] = *v.Cover
|
||||
}
|
||||
if v.Type != nil {
|
||||
out["type"] = *v.Type
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyBusiness) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Name != nil {
|
||||
out["name"] = *v.Name
|
||||
}
|
||||
if v.EnglishName != nil {
|
||||
out["englishName"] = *v.EnglishName
|
||||
}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
if v.Cover != nil {
|
||||
out["cover"] = *v.Cover
|
||||
}
|
||||
if v.Detail != nil {
|
||||
detail := make([]officialwebsitemod.M, 0, len(*v.Detail))
|
||||
for _, d := range *v.Detail {
|
||||
detail = append(detail, d.toM())
|
||||
}
|
||||
out["detail"] = detail
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyBusinessDetail) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Cover != nil {
|
||||
out["cover"] = *v.Cover
|
||||
}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyContactInfo) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Type != nil {
|
||||
out["type"] = *v.Type
|
||||
}
|
||||
if v.Value != nil {
|
||||
out["value"] = *v.Value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyCompanyInfo) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Name != nil {
|
||||
out["name"] = *v.Name
|
||||
}
|
||||
if v.Address != nil {
|
||||
out["address"] = *v.Address
|
||||
}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
if v.Contact != nil {
|
||||
contacts := make([]officialwebsitemod.M, 0, len(*v.Contact))
|
||||
for _, c := range *v.Contact {
|
||||
contacts = append(contacts, c.toM())
|
||||
}
|
||||
out["contact"] = contacts
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v *ModifyAboutUs) toM() officialwebsitemod.M {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
if v.Values != nil {
|
||||
out["values"] = *v.Values
|
||||
}
|
||||
if v.ValuesImg != nil {
|
||||
out["valuesImg"] = *v.ValuesImg
|
||||
}
|
||||
if v.Companies != nil {
|
||||
companies := make([]officialwebsitemod.M, 0, len(*v.Companies))
|
||||
for _, c := range *v.Companies {
|
||||
companies = append(companies, c.toM())
|
||||
}
|
||||
out["companies"] = companies
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyFAQ) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Question != nil {
|
||||
out["question"] = *v.Question
|
||||
}
|
||||
if v.Answer != nil {
|
||||
out["answer"] = *v.Answer
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v ModifyCMSData) toM() officialwebsitemod.M {
|
||||
out := officialwebsitemod.M{}
|
||||
if v.Description != nil {
|
||||
out["description"] = *v.Description
|
||||
}
|
||||
if v.Value != nil {
|
||||
out["value"] = *v.Value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// toM 将请求中的非 nil 字段构造为 Mongo $set 更新文档
|
||||
func (req *ModifyBasicDataReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Title != nil {
|
||||
set["title"] = *req.Title
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.BannersDescription != nil {
|
||||
set["bannersDescription"] = *req.BannersDescription
|
||||
}
|
||||
if req.BannersDuration != nil {
|
||||
set["bannersDuration"] = *req.BannersDuration
|
||||
}
|
||||
if req.HomePageCMSDescription != nil {
|
||||
set["homePageCMSDescription"] = *req.HomePageCMSDescription
|
||||
}
|
||||
if req.Banner != nil {
|
||||
banners := make([]officialwebsitemod.M, 0, len(*req.Banner))
|
||||
for _, b := range *req.Banner {
|
||||
banners = append(banners, b.toM())
|
||||
}
|
||||
set["banner"] = banners
|
||||
}
|
||||
if req.HeroBanner != nil {
|
||||
heroBanners := make([]officialwebsitemod.M, 0, len(*req.HeroBanner))
|
||||
for _, h := range *req.HeroBanner {
|
||||
heroBanners = append(heroBanners, h.toM())
|
||||
}
|
||||
set["heroBanner"] = heroBanners
|
||||
}
|
||||
if req.Masterpiece != nil {
|
||||
masterpiece := make([]officialwebsitemod.M, 0, len(*req.Masterpiece))
|
||||
for _, m := range *req.Masterpiece {
|
||||
masterpiece = append(masterpiece, m.toM())
|
||||
}
|
||||
set["masterpiece"] = masterpiece
|
||||
}
|
||||
if req.Business != nil {
|
||||
businesses := make([]officialwebsitemod.M, 0, len(*req.Business))
|
||||
for _, b := range *req.Business {
|
||||
businesses = append(businesses, b.toM())
|
||||
}
|
||||
set["business"] = businesses
|
||||
}
|
||||
if req.AboutUs != nil {
|
||||
aboutUsM := req.AboutUs.toM()
|
||||
if len(aboutUsM) > 0 {
|
||||
set["aboutUs"] = aboutUsM
|
||||
}
|
||||
}
|
||||
if req.FAQ != nil {
|
||||
faqs := make([]officialwebsitemod.M, 0, len(*req.FAQ))
|
||||
for _, f := range *req.FAQ {
|
||||
faqs = append(faqs, f.toM())
|
||||
}
|
||||
set["faq"] = faqs
|
||||
}
|
||||
if req.HomePageCMS != nil {
|
||||
cms := make([]officialwebsitemod.M, 0, len(*req.HomePageCMS))
|
||||
for _, h := range *req.HomePageCMS {
|
||||
cms = append(cms, h.toM())
|
||||
}
|
||||
set["homePageCMS"] = cms
|
||||
}
|
||||
if req.RecruitCMS != nil {
|
||||
rcms := make([]officialwebsitemod.M, 0, len(*req.RecruitCMS))
|
||||
for _, r := range *req.RecruitCMS {
|
||||
rcms = append(rcms, r.toM())
|
||||
}
|
||||
set["recruitCMS"] = rcms
|
||||
}
|
||||
if req.Tags != nil {
|
||||
set["tags"] = *req.Tags
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListBusiness(req *BusinessListReq) (int64, []officialwebsitemod.BusinessData, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.BusinessData{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func CreateBusiness(req *ModifyBusinessReq) (officialwebsitemod.BusinessData, error) {
|
||||
data := req.toMod()
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.BusinessData{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdateBusiness(req *ModifyBusinessReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.BusinessData{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteBusiness(req *DeleteBusinessReq) (data DeleteBusinessResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.BusinessData{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyBusinessReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Title *string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
Type *string `json:"type"`
|
||||
ServiceOverview *string `json:"serviceOverview"`
|
||||
CollaborationProcess *string `json:"collaborationProcess"`
|
||||
CaseStudies *string `json:"caseStudies"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteBusinessReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteBusinessResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyBusinessReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyBusinessReq) toMod() officialwebsitemod.BusinessData {
|
||||
if req == nil {
|
||||
return officialwebsitemod.BusinessData{}
|
||||
}
|
||||
return officialwebsitemod.BusinessData{
|
||||
Title: stringOrZero(req.Title),
|
||||
Description: stringOrZero(req.Description),
|
||||
Type: stringOrZero(req.Type),
|
||||
ServiceOverview: stringOrZero(req.ServiceOverview),
|
||||
CollaborationProcess: stringOrZero(req.CollaborationProcess),
|
||||
CaseStudies: stringOrZero(req.CaseStudies),
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyBusinessReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyBusinessReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Title != nil {
|
||||
set["title"] = *req.Title
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.Type != nil {
|
||||
set["type"] = *req.Type
|
||||
}
|
||||
if req.ServiceOverview != nil {
|
||||
set["serviceOverview"] = *req.ServiceOverview
|
||||
}
|
||||
if req.CollaborationProcess != nil {
|
||||
set["collaborationProcess"] = *req.CollaborationProcess
|
||||
}
|
||||
if req.CaseStudies != nil {
|
||||
set["caseStudies"] = *req.CaseStudies
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type BusinessListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
Title *string `form:"title"`
|
||||
Description *string `form:"description"`
|
||||
Type *string `form:"type"`
|
||||
ServiceOverview *string `form:"serviceOverview"`
|
||||
CollaborationProcess *string `form:"collaborationProcess"`
|
||||
CaseStudies *string `form:"caseStudies"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *BusinessListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Title != nil {
|
||||
filter["title"] = *r.Title
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.Type != nil {
|
||||
filter["type"] = *r.Type
|
||||
}
|
||||
if r.ServiceOverview != nil {
|
||||
filter["serviceOverview"] = *r.ServiceOverview
|
||||
}
|
||||
if r.CollaborationProcess != nil {
|
||||
filter["collaborationProcess"] = *r.CollaborationProcess
|
||||
}
|
||||
if r.CaseStudies != nil {
|
||||
filter["caseStudies"] = *r.CaseStudies
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List Helper Types and Functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
// CountResp 通用操作返回
|
||||
type CountResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// PageReq 分页请求
|
||||
type PageReq struct {
|
||||
Page int64 `form:"page"`
|
||||
PageSize int64 `form:"pageSize"`
|
||||
}
|
||||
|
||||
func (r *PageReq) FindOptions() *options.FindOptions {
|
||||
page := r.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := r.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
skip := (page - 1) * pageSize
|
||||
return options.Find().SetSkip(skip).SetLimit(pageSize).SetSort(officialwebsitemod.M{"createdAt": -1})
|
||||
}
|
||||
|
||||
func parseObjectIDFilter(filter officialwebsitemod.M, key string, raw *string) error {
|
||||
if raw == nil || *raw == "" {
|
||||
return nil
|
||||
}
|
||||
id, err := primitive.ObjectIDFromHex(*raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid %s: %w", key, err)
|
||||
}
|
||||
filter[key] = id
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseJSONFilter(filter officialwebsitemod.M, key string, raw *string, out interface{}) error {
|
||||
if raw == nil || *raw == "" {
|
||||
return nil
|
||||
}
|
||||
if err := json.Unmarshal([]byte(*raw), out); err != nil {
|
||||
return fmt.Errorf("invalid %s json: %w", key, err)
|
||||
}
|
||||
v := reflect.ValueOf(out)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
filter[key] = v.Elem().Interface()
|
||||
return nil
|
||||
}
|
||||
filter[key] = out
|
||||
return nil
|
||||
}
|
||||
|
||||
// ─────���───────────────────────────────────
|
||||
// Update Helper Functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func modelToMap(doc interface{}) (officialwebsitemod.M, error) {
|
||||
raw, err := bson.Marshal(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := officialwebsitemod.M{}
|
||||
if err := bson.Unmarshal(raw, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delete(data, "_id")
|
||||
delete(data, "createdAt")
|
||||
delete(data, "updatedAt")
|
||||
delete(data, "deletedAt")
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func modelChanged(oldDoc interface{}, newDoc interface{}) (bool, error) {
|
||||
oldMap, err := modelToMap(oldDoc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
newMap, err := modelToMap(newDoc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !reflect.DeepEqual(oldMap, newMap), nil
|
||||
}
|
||||
|
||||
func buildModelUpdateDoc(doc interface{}) (officialwebsitemod.M, error) {
|
||||
data, err := modelToMap(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": data}, nil
|
||||
}
|
||||
|
||||
func buildPointerUpdateDoc(oldDoc interface{}, updateDoc interface{}) (officialwebsitemod.M, error) {
|
||||
oldVal, err := structValueOf(oldDoc, "old doc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newVal, err := structValueOf(updateDoc, "update doc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
collectPointerChanges(set, oldVal, newVal)
|
||||
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
func structValueOf(doc interface{}, label string) (reflect.Value, error) {
|
||||
val := reflect.ValueOf(doc)
|
||||
if val.Kind() == reflect.Ptr {
|
||||
if val.IsNil() {
|
||||
return reflect.Value{}, errors.New(label + " is nil")
|
||||
}
|
||||
val = val.Elem()
|
||||
}
|
||||
if val.Kind() != reflect.Struct {
|
||||
return reflect.Value{}, errors.New(label + " must be struct")
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func collectPointerChanges(set officialwebsitemod.M, oldVal reflect.Value, newVal reflect.Value) {
|
||||
newType := newVal.Type()
|
||||
for i := 0; i < newVal.NumField(); i++ {
|
||||
addPointerFieldChange(set, oldVal, newVal.Field(i), newType.Field(i))
|
||||
}
|
||||
}
|
||||
|
||||
func addPointerFieldChange(set officialwebsitemod.M, oldVal reflect.Value, newField reflect.Value, fieldInfo reflect.StructField) {
|
||||
if fieldInfo.Name == "ID" {
|
||||
return
|
||||
}
|
||||
if !newField.IsValid() || newField.Kind() != reflect.Ptr || newField.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
oldField := findStructFieldByName(oldVal, fieldInfo.Name)
|
||||
if !oldField.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
newValue := derefValue(newField)
|
||||
oldValue := derefValue(oldField)
|
||||
if !newValue.IsValid() || !oldValue.IsValid() {
|
||||
return
|
||||
}
|
||||
if reflect.DeepEqual(oldValue.Interface(), newValue.Interface()) {
|
||||
return
|
||||
}
|
||||
|
||||
key := bsonFieldName(fieldInfo)
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
set[key] = newValue.Interface()
|
||||
}
|
||||
|
||||
func findStructFieldByName(val reflect.Value, name string) reflect.Value {
|
||||
if !val.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
if val.Kind() == reflect.Ptr {
|
||||
if val.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
val = val.Elem()
|
||||
}
|
||||
if val.Kind() != reflect.Struct {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
if field := val.FieldByName(name); field.IsValid() {
|
||||
return field
|
||||
}
|
||||
|
||||
valType := val.Type()
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
fieldType := valType.Field(i)
|
||||
if !fieldType.Anonymous {
|
||||
continue
|
||||
}
|
||||
field := findStructFieldByName(val.Field(i), name)
|
||||
if field.IsValid() {
|
||||
return field
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
func derefValue(v reflect.Value) reflect.Value {
|
||||
for v.IsValid() && v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func bsonFieldName(field reflect.StructField) string {
|
||||
if tag := field.Tag.Get("bson"); tag != "" {
|
||||
name := strings.Split(tag, ",")[0]
|
||||
if name != "" && name != "-" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
if tag := field.Tag.Get("json"); tag != "" {
|
||||
name := strings.Split(tag, ",")[0]
|
||||
if name != "" && name != "-" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return field.Name
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Pointer helpers
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func stringOrZero(v *string) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func intOrZero(v *int) int {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func objectIDOrZero(v *primitive.ObjectID) primitive.ObjectID {
|
||||
if v == nil {
|
||||
return primitive.NilObjectID
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func int64OrZero(v *int64) int64 {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func boolOrZero(v *bool) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func isNilObjectID(id primitive.ObjectID) bool {
|
||||
return id == primitive.NilObjectID
|
||||
}
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListHero(req *HeroListReq) (int64, []officialwebsitemod.Hero, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.Hero{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func CreateHero(req *ModifyHeroReq) (officialwebsitemod.Hero, error) {
|
||||
data := req.toMod()
|
||||
// SEO slug:手填优先(校验唯一),为空则由名称生成雏形并去重
|
||||
var err error
|
||||
if data.SeoSlug, err = resolveHeroSeoSlug(data.SeoSlug, data.Name, primitive.NilObjectID); err != nil {
|
||||
return officialwebsitemod.Hero{}, err
|
||||
}
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.Hero{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// resolveHeroSeoSlug 处理 seoSlug:手填则校验格式并查重(冲突报错);为空则由名称生成雏形并追加后缀去重。
|
||||
func resolveHeroSeoSlug(provided, name string, excludeID primitive.ObjectID) (string, error) {
|
||||
return ResolveSeoSlug(provided, name, func(slug string) (bool, error) {
|
||||
filter := officialwebsitemod.M{"seoSlug": slug}
|
||||
if !excludeID.IsZero() {
|
||||
filter["_id"] = officialwebsitemod.M{"$ne": excludeID}
|
||||
}
|
||||
n, err := (&officialwebsitemod.Hero{}).Count(filter)
|
||||
return n > 0, err
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateHero(req *ModifyHeroReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Hero{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
// 若提交了 seoSlug,先校验/去重(排除自身),回写后由 toM 落库
|
||||
if req.SeoSlug != nil {
|
||||
var slug string
|
||||
if slug, err = resolveHeroSeoSlug(*req.SeoSlug, mod.Name, id); err != nil {
|
||||
return
|
||||
}
|
||||
*req.SeoSlug = slug
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteHero(req *DeleteHeroReq) (data DeleteHeroResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Hero{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyHeroReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Name *string `json:"name"`
|
||||
SeoSlug *string `json:"seoSlug"`
|
||||
Avatar *string `json:"avatar"`
|
||||
Description *string `json:"description"`
|
||||
FansCount *int64 `json:"fansCount"`
|
||||
Height *int64 `json:"height"`
|
||||
Bust *int64 `json:"bust"`
|
||||
Waist *int64 `json:"waist"`
|
||||
Hip *int64 `json:"hip"`
|
||||
Cover *string `json:"cover"`
|
||||
Partners *[]primitive.ObjectID `json:"partners"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteHeroReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteHeroResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyHeroReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyHeroReq) toMod() officialwebsitemod.Hero {
|
||||
if req == nil {
|
||||
return officialwebsitemod.Hero{}
|
||||
}
|
||||
data := officialwebsitemod.Hero{
|
||||
Name: stringOrZero(req.Name),
|
||||
Avatar: stringOrZero(req.Avatar),
|
||||
Description: stringOrZero(req.Description),
|
||||
FansCount: int64OrZero(req.FansCount),
|
||||
Height: int64OrZero(req.Height),
|
||||
Bust: int64OrZero(req.Bust),
|
||||
Waist: int64OrZero(req.Waist),
|
||||
Hip: int64OrZero(req.Hip),
|
||||
Cover: stringOrZero(req.Cover),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
}
|
||||
if req.Partners != nil {
|
||||
data.Partners = *req.Partners
|
||||
}
|
||||
if req.SeoSlug != nil {
|
||||
data.SeoSlug = *req.SeoSlug
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyHeroReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyHeroReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Name != nil {
|
||||
set["name"] = *req.Name
|
||||
}
|
||||
if req.SeoSlug != nil && *req.SeoSlug != "" {
|
||||
set["seoSlug"] = *req.SeoSlug
|
||||
}
|
||||
if req.Avatar != nil {
|
||||
set["avatar"] = *req.Avatar
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.FansCount != nil {
|
||||
set["fansCount"] = *req.FansCount
|
||||
}
|
||||
if req.Height != nil {
|
||||
set["height"] = *req.Height
|
||||
}
|
||||
if req.Bust != nil {
|
||||
set["bust"] = *req.Bust
|
||||
}
|
||||
if req.Waist != nil {
|
||||
set["waist"] = *req.Waist
|
||||
}
|
||||
if req.Hip != nil {
|
||||
set["hip"] = *req.Hip
|
||||
}
|
||||
if req.Cover != nil {
|
||||
set["cover"] = *req.Cover
|
||||
}
|
||||
if req.Partners != nil {
|
||||
set["partners"] = *req.Partners
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type HeroListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
Name *string `form:"name"`
|
||||
Avatar *string `form:"avatar"`
|
||||
Height *int64 `form:"height"`
|
||||
Bust *int64 `form:"bust"`
|
||||
Waist *int64 `form:"waist"`
|
||||
Hip *int64 `form:"hip"`
|
||||
Description *string `form:"description"`
|
||||
FansCount *int64 `form:"fansCount"`
|
||||
Cover *string `form:"cover"`
|
||||
Partners *string `form:"partners"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *HeroListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Name != nil {
|
||||
filter["name"] = *r.Name
|
||||
}
|
||||
if r.Avatar != nil {
|
||||
filter["avatar"] = *r.Avatar
|
||||
}
|
||||
if r.Height != nil {
|
||||
filter["height"] = *r.Height
|
||||
}
|
||||
if r.Bust != nil {
|
||||
filter["bust"] = *r.Bust
|
||||
}
|
||||
if r.Waist != nil {
|
||||
filter["waist"] = *r.Waist
|
||||
}
|
||||
if r.Hip != nil {
|
||||
filter["hip"] = *r.Hip
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.FansCount != nil {
|
||||
filter["fansCount"] = *r.FansCount
|
||||
}
|
||||
if r.Cover != nil {
|
||||
filter["cover"] = *r.Cover
|
||||
}
|
||||
if err := parseJSONFilter(filter, "partners", r.Partners, &[]primitive.ObjectID{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Sort != nil {
|
||||
filter["sort"] = *r.Sort
|
||||
}
|
||||
if r.IsActive != nil {
|
||||
filter["isActive"] = *r.IsActive
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListJobList(req *JobListReq) (int64, []officialwebsitemod.Job, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.Job{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func CreateJobList(req *ModifyJobReq) (officialwebsitemod.Job, error) {
|
||||
data := req.toMod()
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.Job{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdateJobList(req *ModifyJobReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Job{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteJobList(req *DeleteJobReq) (data DeleteJobResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Job{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyJobReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Title *string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
Requirements *string `json:"requirements"`
|
||||
Benefits *string `json:"benefits"`
|
||||
JDUrl *string `json:"jdUrl"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteJobReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteJobResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyJobReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyJobReq) toMod() officialwebsitemod.Job {
|
||||
if req == nil {
|
||||
return officialwebsitemod.Job{}
|
||||
}
|
||||
return officialwebsitemod.Job{
|
||||
Title: stringOrZero(req.Title),
|
||||
Description: stringOrZero(req.Description),
|
||||
Requirements: stringOrZero(req.Requirements),
|
||||
Benefits: stringOrZero(req.Benefits),
|
||||
JDUrl: stringOrZero(req.JDUrl),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyJobReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyJobReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Title != nil {
|
||||
set["title"] = *req.Title
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.Requirements != nil {
|
||||
set["requirements"] = *req.Requirements
|
||||
}
|
||||
if req.Benefits != nil {
|
||||
set["benefits"] = *req.Benefits
|
||||
}
|
||||
if req.JDUrl != nil {
|
||||
set["jdUrl"] = *req.JDUrl
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type JobListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
Title *string `form:"title"`
|
||||
Description *string `form:"description"`
|
||||
Requirements *string `form:"requirements"`
|
||||
Benefits *string `form:"benefits"`
|
||||
JDUrl *string `form:"jdUrl"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *JobListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Title != nil {
|
||||
filter["title"] = *r.Title
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.Requirements != nil {
|
||||
filter["requirements"] = *r.Requirements
|
||||
}
|
||||
if r.Benefits != nil {
|
||||
filter["benefits"] = *r.Benefits
|
||||
}
|
||||
if r.JDUrl != nil {
|
||||
filter["jdUrl"] = *r.JDUrl
|
||||
}
|
||||
if r.Sort != nil {
|
||||
filter["sort"] = *r.Sort
|
||||
}
|
||||
if r.IsActive != nil {
|
||||
filter["isActive"] = *r.IsActive
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListNews(req *NewsListReq) (int64, []officialwebsitemod.News, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.News{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
// resolveNewsSeoSlug 处理 seoSlug:手填则校验格式并查重(冲突报错);为空则由标题生成雏形并追加后缀去重。
|
||||
func resolveNewsSeoSlug(provided, title string, excludeID primitive.ObjectID) (string, error) {
|
||||
return ResolveSeoSlug(provided, title, func(slug string) (bool, error) {
|
||||
filter := officialwebsitemod.M{"seoSlug": slug}
|
||||
if !excludeID.IsZero() {
|
||||
filter["_id"] = officialwebsitemod.M{"$ne": excludeID}
|
||||
}
|
||||
n, err := (&officialwebsitemod.News{}).Count(filter)
|
||||
return n > 0, err
|
||||
})
|
||||
}
|
||||
|
||||
func CreateNews(req *ModifyNewsReq) (officialwebsitemod.News, error) {
|
||||
data := req.toMod()
|
||||
// SEO slug:手填优先(校验唯一),为空则由标题生成雏形并去重
|
||||
slug, err := resolveNewsSeoSlug(data.SeoSlug, data.Title, primitive.NilObjectID)
|
||||
if err != nil {
|
||||
return officialwebsitemod.News{}, err
|
||||
}
|
||||
data.SeoSlug = slug
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.News{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdateNews(req *ModifyNewsReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.News{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
// 若提交了 seoSlug,先校验/去重(排除自身),回写后由 toM 落库
|
||||
if req.SeoSlug != nil {
|
||||
var slug string
|
||||
if slug, err = resolveNewsSeoSlug(*req.SeoSlug, mod.Title, id); err != nil {
|
||||
return
|
||||
}
|
||||
*req.SeoSlug = slug
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteNews(req *DeleteNewsReq) (data DeleteNewsResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.News{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyNewsReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Title *string `json:"title"`
|
||||
SeoSlug *string `json:"seoSlug"`
|
||||
Description *string `json:"description"`
|
||||
Cover *string `json:"cover"`
|
||||
Url *string `json:"url"`
|
||||
Tags *[]officialwebsitemod.Tag `json:"tags"`
|
||||
Detail *string `json:"detail"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteNewsReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteNewsResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyNewsReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyNewsReq) toMod() officialwebsitemod.News {
|
||||
if req == nil {
|
||||
return officialwebsitemod.News{}
|
||||
}
|
||||
data := officialwebsitemod.News{
|
||||
Title: stringOrZero(req.Title),
|
||||
Description: stringOrZero(req.Description),
|
||||
Cover: stringOrZero(req.Cover),
|
||||
Url: stringOrZero(req.Url),
|
||||
Detail: stringOrZero(req.Detail),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
}
|
||||
if req.Tags != nil {
|
||||
data.Tags = *req.Tags
|
||||
}
|
||||
if req.SeoSlug != nil {
|
||||
data.SeoSlug = *req.SeoSlug
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyNewsReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyNewsReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Title != nil {
|
||||
set["title"] = *req.Title
|
||||
}
|
||||
if req.SeoSlug != nil && *req.SeoSlug != "" {
|
||||
set["seoSlug"] = *req.SeoSlug
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.Cover != nil {
|
||||
set["cover"] = *req.Cover
|
||||
}
|
||||
if req.Url != nil {
|
||||
set["url"] = *req.Url
|
||||
}
|
||||
if req.Tags != nil {
|
||||
set["tags"] = *req.Tags
|
||||
}
|
||||
if req.Detail != nil {
|
||||
set["detail"] = *req.Detail
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type NewsListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
Title *string `form:"title"`
|
||||
Description *string `form:"description"`
|
||||
Cover *string `form:"cover"`
|
||||
Url *string `form:"url"`
|
||||
Tags *string `form:"tags"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *NewsListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Title != nil {
|
||||
filter["title"] = *r.Title
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.Cover != nil {
|
||||
filter["cover"] = *r.Cover
|
||||
}
|
||||
if r.Url != nil {
|
||||
filter["url"] = *r.Url
|
||||
}
|
||||
if err := parseJSONFilter(filter, "tags", r.Tags, &[]officialwebsitemod.Tag{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Sort != nil {
|
||||
filter["sort"] = *r.Sort
|
||||
}
|
||||
if r.IsActive != nil {
|
||||
filter["isActive"] = *r.IsActive
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListPartner(req *PartnerListReq) (int64, []officialwebsitemod.Partner, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.Partner{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func CreatePartner(req *ModifyPartnerReq) (officialwebsitemod.Partner, error) {
|
||||
data := req.toMod()
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.Partner{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdatePartner(req *ModifyPartnerReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Partner{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeletePartner(req *DeletePartnerReq) (data DeletePartnerResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Partner{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyPartnerReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Name *string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
Logo *string `json:"logo"`
|
||||
Url *string `json:"url"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeletePartnerReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeletePartnerResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyPartnerReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyPartnerReq) toMod() officialwebsitemod.Partner {
|
||||
if req == nil {
|
||||
return officialwebsitemod.Partner{}
|
||||
}
|
||||
return officialwebsitemod.Partner{
|
||||
Name: stringOrZero(req.Name),
|
||||
Description: stringOrZero(req.Description),
|
||||
Logo: stringOrZero(req.Logo),
|
||||
Url: stringOrZero(req.Url),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyPartnerReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyPartnerReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Name != nil {
|
||||
set["name"] = *req.Name
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.Logo != nil {
|
||||
set["logo"] = *req.Logo
|
||||
}
|
||||
if req.Url != nil {
|
||||
set["url"] = *req.Url
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type PartnerListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
Name *string `form:"name"`
|
||||
Description *string `form:"description"`
|
||||
Logo *string `form:"logo"`
|
||||
Url *string `form:"url"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *PartnerListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Name != nil {
|
||||
filter["name"] = *r.Name
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.Logo != nil {
|
||||
filter["logo"] = *r.Logo
|
||||
}
|
||||
if r.Url != nil {
|
||||
filter["url"] = *r.Url
|
||||
}
|
||||
if r.Sort != nil {
|
||||
filter["sort"] = *r.Sort
|
||||
}
|
||||
if r.IsActive != nil {
|
||||
filter["isActive"] = *r.IsActive
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListPhotograph(req *ListPhotographReq) (int64, []officialwebsitemod.Photograph, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.Photograph{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
// resolvePhotographSeoSlug 处理 seoSlug:手填则校验格式并查重(冲突报错);为空则由标题生成雏形并追加后缀去重。
|
||||
func resolvePhotographSeoSlug(provided, name string, excludeID primitive.ObjectID) (string, error) {
|
||||
return ResolveSeoSlug(provided, name, func(slug string) (bool, error) {
|
||||
filter := officialwebsitemod.M{"seoSlug": slug}
|
||||
if !excludeID.IsZero() {
|
||||
filter["_id"] = officialwebsitemod.M{"$ne": excludeID}
|
||||
}
|
||||
n, err := (&officialwebsitemod.Photograph{}).Count(filter)
|
||||
return n > 0, err
|
||||
})
|
||||
}
|
||||
|
||||
func CreatePhotograph(req *ModifyPhotographReq) (officialwebsitemod.Photograph, error) {
|
||||
data, err := req.toMod()
|
||||
if err != nil {
|
||||
return officialwebsitemod.Photograph{}, err
|
||||
}
|
||||
// SEO slug:手填优先(校验唯一),为空则由标题生成雏形并去重
|
||||
if data.SeoSlug, err = resolvePhotographSeoSlug(data.SeoSlug, data.Name, primitive.NilObjectID); err != nil {
|
||||
return officialwebsitemod.Photograph{}, err
|
||||
}
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.Photograph{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdatePhotograph(req *ModifyPhotographReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Photograph{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(mod.ID) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
// 若提交了 seoSlug,先校验/去重(排除自身),回写后由 toM 落库
|
||||
if req.SeoSlug != nil {
|
||||
var slug string
|
||||
if slug, err = resolvePhotographSeoSlug(*req.SeoSlug, mod.Name, id); err != nil {
|
||||
return
|
||||
}
|
||||
*req.SeoSlug = slug
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeletePhotograph(req *DeletePhotographReq) (data DeletePhotographResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Photograph{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
func BatchUpdatePhotographOwner(req *BatchUpdatePhotographOwnerReq) (data BatchUpdatePhotographOwnerResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
|
||||
// Check owner is exist
|
||||
var ownerID primitive.ObjectID
|
||||
ownerID, err = primitive.ObjectIDFromHex(req.OwnerID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var heroMod = &officialwebsitemod.Hero{}
|
||||
err = heroMod.FindOne(officialwebsitemod.M{"_id": ownerID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(heroMod.ID) {
|
||||
err = stderr.OfficialWebsiteHeroExist
|
||||
return
|
||||
}
|
||||
|
||||
var photographIDs []primitive.ObjectID
|
||||
for i := range req.PhotographIDs {
|
||||
photographID, parseErr := primitive.ObjectIDFromHex(req.PhotographIDs[i])
|
||||
err = parseErr
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
photographIDs = append(photographIDs, photographID)
|
||||
}
|
||||
var photographMod = &officialwebsitemod.Photograph{}
|
||||
filter := bson.M{"_id": bson.M{"$in": photographIDs}}
|
||||
update := bson.M{"$set": bson.M{"heroId": ownerID}}
|
||||
data.Count, err = photographMod.Update(filter, update)
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Batch (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type BatchUpdatePhotographOwnerReq struct {
|
||||
PhotographIDs []string `json:"photograph_ids"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
}
|
||||
|
||||
type BatchUpdatePhotographOwnerResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyPhotographReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
HeroID *string `json:"heroID"`
|
||||
Name *string `json:"name"`
|
||||
SeoSlug *string `json:"seoSlug"`
|
||||
Description *string `json:"description"`
|
||||
Cover *string `json:"cover"`
|
||||
Photos *[]string `json:"photos"`
|
||||
Tags *[]officialwebsitemod.Tag `json:"tags"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeletePhotographReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeletePhotographResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyPhotographReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyPhotographReq) toMod() (data officialwebsitemod.Photograph, err error) {
|
||||
if req == nil {
|
||||
return
|
||||
}
|
||||
data = officialwebsitemod.Photograph{
|
||||
Name: stringOrZero(req.Name),
|
||||
Description: stringOrZero(req.Description),
|
||||
Cover: stringOrZero(req.Cover),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
}
|
||||
if req.HeroID != nil {
|
||||
data.HeroID, err = primitive.ObjectIDFromHex(stringOrZero(req.HeroID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if req.Photos != nil {
|
||||
data.Photos = *req.Photos
|
||||
}
|
||||
if req.Tags != nil {
|
||||
data.Tags = *req.Tags
|
||||
}
|
||||
if req.SeoSlug != nil {
|
||||
data.SeoSlug = *req.SeoSlug
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyPhotographReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyPhotographReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.HeroID != nil {
|
||||
if *req.HeroID != "" {
|
||||
heroID, err := primitive.ObjectIDFromHex(*req.HeroID)
|
||||
if err != nil || isNilObjectID(heroID) {
|
||||
return nil, stderr.ErrParamError
|
||||
}
|
||||
set["heroId"] = heroID
|
||||
}
|
||||
}
|
||||
if req.Name != nil {
|
||||
set["name"] = *req.Name
|
||||
}
|
||||
if req.SeoSlug != nil && *req.SeoSlug != "" {
|
||||
set["seoSlug"] = *req.SeoSlug
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.Cover != nil {
|
||||
set["cover"] = *req.Cover
|
||||
}
|
||||
if req.Photos != nil {
|
||||
set["photos"] = *req.Photos
|
||||
}
|
||||
if req.Tags != nil {
|
||||
set["tags"] = *req.Tags
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ListPhotographReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
HeroID *string `form:"heroID"`
|
||||
Name *string `form:"name"`
|
||||
Description *string `form:"description"`
|
||||
Cover *string `form:"cover"`
|
||||
Photos *string `form:"photos"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (l *ListPhotographReq) Query() (filter officialwebsitemod.M, err error) {
|
||||
filter = officialwebsitemod.M{}
|
||||
if err = parseObjectIDFilter(filter, "_id", l.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = parseObjectIDFilter(filter, "heroId", l.HeroID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l.Name != nil {
|
||||
filter["name"] = *l.Name
|
||||
}
|
||||
if l.Description != nil {
|
||||
filter["description"] = *l.Description
|
||||
}
|
||||
if l.Cover != nil {
|
||||
filter["cover"] = *l.Cover
|
||||
}
|
||||
if err = parseJSONFilter(filter, "photos", l.Photos, &[]string{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l.Sort != nil {
|
||||
filter["sort"] = *l.Sort
|
||||
}
|
||||
if l.IsActive != nil {
|
||||
filter["isActive"] = *l.IsActive
|
||||
}
|
||||
if l.CreatedAt != nil {
|
||||
filter["createdAt"] = *l.CreatedAt
|
||||
}
|
||||
if l.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *l.UpdatedAt
|
||||
}
|
||||
if l.DeletedAt != nil {
|
||||
filter["deletedAt"] = *l.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListRecruitForm(req *RecruitFormListReq) (int64, []officialwebsitemod.RecruitForm, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.RecruitForm{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func CreateRecruitForm(req *ModifyRecruitFormReq) (officialwebsitemod.RecruitForm, error) {
|
||||
data := req.toMod()
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.RecruitForm{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdateRecruitForm(req *ModifyRecruitFormReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.RecruitForm{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteRecruitForm(req *DeleteRecruitFormReq) (data DeleteRecruitFormResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.RecruitForm{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyRecruitFormReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Name *string `json:"name"`
|
||||
Sex *int `json:"sex"`
|
||||
Age *int `json:"age"`
|
||||
Country *string `json:"country"`
|
||||
Address *string `json:"address"`
|
||||
Contact *[]officialwebsitemod.ContactInfo `json:"contact"`
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteRecruitFormReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteRecruitFormResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyRecruitFormReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyRecruitFormReq) toMod() officialwebsitemod.RecruitForm {
|
||||
if req == nil {
|
||||
return officialwebsitemod.RecruitForm{}
|
||||
}
|
||||
data := officialwebsitemod.RecruitForm{
|
||||
Name: stringOrZero(req.Name),
|
||||
Sex: intOrZero(req.Sex),
|
||||
Age: intOrZero(req.Age),
|
||||
Country: stringOrZero(req.Country),
|
||||
Address: stringOrZero(req.Address),
|
||||
Description: stringOrZero(req.Description),
|
||||
}
|
||||
if req.Contact != nil {
|
||||
data.Contact = *req.Contact
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyRecruitFormReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyRecruitFormReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Name != nil {
|
||||
set["name"] = *req.Name
|
||||
}
|
||||
if req.Sex != nil {
|
||||
set["sex"] = *req.Sex
|
||||
}
|
||||
if req.Age != nil {
|
||||
set["age"] = *req.Age
|
||||
}
|
||||
if req.Country != nil {
|
||||
set["country"] = *req.Country
|
||||
}
|
||||
if req.Address != nil {
|
||||
set["address"] = *req.Address
|
||||
}
|
||||
if req.Contact != nil {
|
||||
set["contact"] = *req.Contact
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type RecruitFormListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
Name *string `form:"name"`
|
||||
Sex *int `form:"sex"`
|
||||
Age *int `form:"age"`
|
||||
Country *string `form:"country"`
|
||||
Address *string `form:"address"`
|
||||
Contact *string `form:"contact"`
|
||||
Description *string `form:"description"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *RecruitFormListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Name != nil {
|
||||
filter["name"] = *r.Name
|
||||
}
|
||||
if r.Sex != nil {
|
||||
filter["sex"] = *r.Sex
|
||||
}
|
||||
if r.Age != nil {
|
||||
filter["age"] = *r.Age
|
||||
}
|
||||
if r.Country != nil {
|
||||
filter["country"] = *r.Country
|
||||
}
|
||||
if r.Address != nil {
|
||||
filter["address"] = *r.Address
|
||||
}
|
||||
if err := parseJSONFilter(filter, "contact", r.Contact, &[]officialwebsitemod.ContactInfo{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
|
||||
"github.com/mozillazg/go-pinyin"
|
||||
)
|
||||
|
||||
var (
|
||||
seoSlugValidRe = regexp.MustCompile(`^[a-z]+$`) // 纯小写英文字母
|
||||
seoSlugStripRe = regexp.MustCompile(`[^a-z]`) // 非 a-z 一律剔除
|
||||
)
|
||||
|
||||
// seoSlugMaxLen 自动生成 slug 的最大长度(按拼音音节边界截断,避免长标题生成超长 slug)
|
||||
const seoSlugMaxLen = 20
|
||||
|
||||
// GenerateSeoSlug 从标题生成纯小写字母 slug:中文转拼音、英文保留、其余字符剔除,
|
||||
// 并按音节边界累加到 seoSlugMaxLen 为止(不从音节中间截断)。
|
||||
// 例: "优雅Summer 2023" -> "youyasummer"
|
||||
func GenerateSeoSlug(title string) string {
|
||||
if title == "" {
|
||||
return ""
|
||||
}
|
||||
args := pinyin.NewArgs()
|
||||
// 非汉字(英文/数字/符号)原样返回,后续再统一剔除非 a-z
|
||||
args.Fallback = func(r rune, a pinyin.Args) []string {
|
||||
return []string{string(r)}
|
||||
}
|
||||
rows := pinyin.Pinyin(title, args)
|
||||
var b strings.Builder
|
||||
for _, row := range rows {
|
||||
if len(row) == 0 {
|
||||
continue
|
||||
}
|
||||
// 单个音节先剔除非 a-z、转小写
|
||||
syl := seoSlugStripRe.ReplaceAllString(strings.ToLower(row[0]), "")
|
||||
if syl == "" {
|
||||
continue
|
||||
}
|
||||
// 到长度上限则在音节边界停止,避免 slug 过长
|
||||
if b.Len()+len(syl) > seoSlugMaxLen {
|
||||
break
|
||||
}
|
||||
b.WriteString(syl)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// ValidateSeoSlug 校验 slug 格式:非空且纯小写英文字母。
|
||||
func ValidateSeoSlug(s string) bool {
|
||||
return seoSlugValidRe.MatchString(s)
|
||||
}
|
||||
|
||||
// ResolveSeoSlug 统一处理 seoSlug:
|
||||
// - 手填(provided 非空):校验纯小写字母格式,并查重(冲突报错)
|
||||
// - 留空:由 title 生成拼音雏形,冲突时追加后缀去重
|
||||
//
|
||||
// exists 由各表提供(查各自表是否已占用该 slug)。
|
||||
func ResolveSeoSlug(provided, title string, exists func(slug string) (bool, error)) (string, error) {
|
||||
if provided != "" {
|
||||
if !ValidateSeoSlug(provided) {
|
||||
return "", stderr.ErrParamError
|
||||
}
|
||||
used, err := exists(provided)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if used {
|
||||
return "", stderr.ErrParamError
|
||||
}
|
||||
return provided, nil
|
||||
}
|
||||
return UniqueSeoSlug(GenerateSeoSlug(title), exists)
|
||||
}
|
||||
|
||||
// UniqueSeoSlug 基于 base 生成同表唯一的 slug;exists 判断某 slug 是否已被占用。
|
||||
// 冲突时依次追加纯小写字母后缀 a、b…z、aa、ab…zz、aaa… 直到命中未占用值;
|
||||
// base 为空则返回空(走稀疏索引,不参与唯一约束)。
|
||||
func UniqueSeoSlug(base string, exists func(slug string) (bool, error)) (string, error) {
|
||||
if base == "" {
|
||||
return "", nil
|
||||
}
|
||||
// n=0 先试 base 原值,之后依次追加 a、b…z、aa… 后缀(双射 26 进制递增)。
|
||||
// 表内记录有限且各候选互不相同,必能在有限次内命中未占用值。
|
||||
for n := 0; ; n++ {
|
||||
candidate := base
|
||||
if n > 0 {
|
||||
candidate += seoSlugSuffix(n)
|
||||
}
|
||||
used, err := exists(candidate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !used {
|
||||
return candidate, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// seoSlugSuffix 返回第 n 个纯小写字母后缀(n>=1),按双射 26 进制递增:
|
||||
// 1->a 2->b … 26->z 27->aa 28->ab … 52->az 53->ba … 702->zz 703->aaa
|
||||
func seoSlugSuffix(n int) string {
|
||||
var buf []byte
|
||||
for n > 0 {
|
||||
n--
|
||||
buf = append([]byte{byte('a' + n%26)}, buf...)
|
||||
n /= 26
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func ListTag(req *TagListReq) (int64, []officialwebsitemod.Tag, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.Tag{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func CreateTag(req *ModifyTagReq) (officialwebsitemod.Tag, error) {
|
||||
data := req.toMod()
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.Tag{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdateTag(req *ModifyTagReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Tag{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteTag(req *DeleteTagReq) (data DeleteTagResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Tag{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyTagReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
Name *string `json:"name"`
|
||||
Type *string `json:"type"`
|
||||
IsHot *bool `json:"isHot"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteTagReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteTagResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyTagReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyTagReq) toMod() officialwebsitemod.Tag {
|
||||
if req == nil {
|
||||
return officialwebsitemod.Tag{}
|
||||
}
|
||||
return officialwebsitemod.Tag{
|
||||
Name: stringOrZero(req.Name),
|
||||
Type: stringOrZero(req.Type),
|
||||
IsHot: boolOrZero(req.IsHot),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyTagReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyTagReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.Name != nil {
|
||||
set["name"] = *req.Name
|
||||
}
|
||||
if req.Type != nil {
|
||||
set["type"] = *req.Type
|
||||
}
|
||||
if req.IsHot != nil {
|
||||
set["isHot"] = *req.IsHot
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type TagListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
Name *string `form:"name"`
|
||||
Type *string `form:"type"`
|
||||
IsHot *bool `form:"isHot"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *TagListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Name != nil {
|
||||
filter["name"] = *r.Name
|
||||
}
|
||||
if r.Type != nil {
|
||||
filter["type"] = *r.Type
|
||||
}
|
||||
if r.IsHot != nil {
|
||||
filter["isHot"] = *r.IsHot
|
||||
}
|
||||
if r.Sort != nil {
|
||||
filter["sort"] = *r.Sort
|
||||
}
|
||||
if r.IsActive != nil {
|
||||
filter["isActive"] = *r.IsActive
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,652 @@
|
||||
package officialwebsiteser
|
||||
|
||||
import (
|
||||
"91porn-server/models/v/tagmod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
"math/rand/v2"
|
||||
"time"
|
||||
|
||||
"91porn-server/common/stderr"
|
||||
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Service functions
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func CheckVideoExistByVid(vid []primitive.ObjectID) ([]officialwebsitemod.Video, error) {
|
||||
filter := bson.M{"_id": bson.M{"$in": vid}}
|
||||
mod := &officialwebsitemod.Video{}
|
||||
videos, err := mod.FindMany(filter)
|
||||
return videos, err
|
||||
}
|
||||
|
||||
func ListVideo(req *VideoListReq) (int64, []officialwebsitemod.Video, error) {
|
||||
filter, err := req.Query()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
mod := &officialwebsitemod.Video{}
|
||||
total, err := mod.Count(filter)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err := mod.FindMany(filter, req.FindOptions())
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
// resolveVideoSeoSlug 处理 seoSlug:手填则校验格式并查重(冲突报错);为空则由标题生成雏形并追加后缀去重。
|
||||
func resolveVideoSeoSlug(provided, title string, excludeID primitive.ObjectID) (string, error) {
|
||||
exists := func(slug string) (bool, error) {
|
||||
filter := officialwebsitemod.M{"seoSlug": slug}
|
||||
if !excludeID.IsZero() {
|
||||
filter["_id"] = officialwebsitemod.M{"$ne": excludeID}
|
||||
}
|
||||
n, err := (&officialwebsitemod.Video{}).Count(filter)
|
||||
return n > 0, err
|
||||
}
|
||||
if provided != "" {
|
||||
if !ValidateSeoSlug(provided) {
|
||||
return "", stderr.ErrParamError
|
||||
}
|
||||
used, err := exists(provided)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if used {
|
||||
return "", stderr.ErrParamError
|
||||
}
|
||||
return provided, nil
|
||||
}
|
||||
return UniqueSeoSlug(GenerateSeoSlug(title), exists)
|
||||
}
|
||||
|
||||
func CreateVideo(req *ModifyVideoReq) (officialwebsitemod.Video, error) {
|
||||
data, err := req.toMod()
|
||||
if err != nil {
|
||||
return officialwebsitemod.Video{}, err
|
||||
}
|
||||
// SEO slug:手填优先(校验唯一),为空则由标题生成雏形并去重
|
||||
if data.SeoSlug, err = resolveVideoSeoSlug(data.SeoSlug, data.Title, primitive.NilObjectID); err != nil {
|
||||
return officialwebsitemod.Video{}, err
|
||||
}
|
||||
if err := data.Create(); err != nil {
|
||||
return officialwebsitemod.Video{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func UpdateVideo(req *ModifyVideoReq) (resp CountResp, err error) {
|
||||
if req == nil || req.ID == "" {
|
||||
return
|
||||
}
|
||||
id, parseErr := primitive.ObjectIDFromHex(req.ID)
|
||||
if parseErr != nil || isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Video{}
|
||||
if err = mod.FindOne(officialwebsitemod.M{"_id": id}); err != nil {
|
||||
return
|
||||
}
|
||||
// 若提交了 seoSlug,先校验/去重(排除自身),回写后由 toM 落库
|
||||
if req.SeoSlug != nil {
|
||||
var slug string
|
||||
if slug, err = resolveVideoSeoSlug(*req.SeoSlug, mod.Title, id); err != nil {
|
||||
return
|
||||
}
|
||||
*req.SeoSlug = slug
|
||||
}
|
||||
var update officialwebsitemod.M
|
||||
if update, err = req.toM(); err != nil || update == nil {
|
||||
return
|
||||
}
|
||||
resp.Count, err = mod.Update(officialwebsitemod.M{"_id": id}, update)
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteVideo(req *DeleteVideoReq) (data DeleteVideoResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.CodeEmptyData
|
||||
return
|
||||
}
|
||||
var id primitive.ObjectID
|
||||
if id, err = primitive.ObjectIDFromHex(req.ID); err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(id) {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
mod := &officialwebsitemod.Video{}
|
||||
data.Count, err = mod.Delete(officialwebsitemod.M{"_id": id})
|
||||
return
|
||||
}
|
||||
|
||||
func BatchUpdateVideoOwner(req *BatchUpdateVideoOwnerReq) (data BatchUpdateVideoOwnerResp, err error) {
|
||||
if req == nil {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
|
||||
// Check owner is exist
|
||||
var ownerID primitive.ObjectID
|
||||
ownerID, err = primitive.ObjectIDFromHex(req.OwnerID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var heroMod = &officialwebsitemod.Hero{}
|
||||
err = heroMod.FindOne(officialwebsitemod.M{"_id": ownerID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isNilObjectID(heroMod.ID) {
|
||||
err = stderr.OfficialWebsiteHeroExist
|
||||
return
|
||||
}
|
||||
|
||||
var update = bson.M{"$set": bson.M{"heroId": ownerID}}
|
||||
// Check album is exist, if exist then add into update
|
||||
if req.AlbumID != "" {
|
||||
var albumID primitive.ObjectID
|
||||
albumID, err = primitive.ObjectIDFromHex(req.AlbumID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var album = &officialwebsitemod.Album{}
|
||||
err = album.FindOne(officialwebsitemod.M{"_id": albumID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
update = bson.M{"$set": bson.M{"heroId": ownerID, "albumId": albumID}}
|
||||
}
|
||||
|
||||
var videoIDs []primitive.ObjectID
|
||||
for i := range req.VideoIDs {
|
||||
videoID, parseErr := primitive.ObjectIDFromHex(req.VideoIDs[i])
|
||||
err = parseErr
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
videoIDs = append(videoIDs, videoID)
|
||||
}
|
||||
var videoMod = &officialwebsitemod.Video{}
|
||||
filter := bson.M{"_id": bson.M{"$in": videoIDs}}
|
||||
data.Count, err = videoMod.UpdateMany(filter, update)
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Batch (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type BatchUpdateVideoOwnerReq struct {
|
||||
VideoIDs []string `json:"video_ids"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
AlbumID string `json:"album_id"`
|
||||
}
|
||||
|
||||
type BatchUpdateVideoOwnerResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Mutation (Modify) types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type ModifyVideoReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
AlbumID *string `json:"albumId" form:"albumId"`
|
||||
HeroID *string `json:"heroId" form:"heroId"`
|
||||
Title *string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
Cover *string `json:"cover"`
|
||||
Url *string `json:"url"`
|
||||
SeoSlug *string `json:"seoSlug"`
|
||||
Tags *[]officialwebsitemod.Tag `json:"tags"`
|
||||
IsHot *bool `json:"isHot"`
|
||||
Sort *int64 `json:"sort"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
WatchCount *int64 `json:"watchCount"`
|
||||
LikeCount *int64 `json:"likeCount"`
|
||||
CommentCount *int64 `json:"commentCount"`
|
||||
CollectCount *int64 `json:"collectCount"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Delete types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type DeleteVideoReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type DeleteVideoResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toMod — ModifyVideoReq → model struct
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyVideoReq) toMod() (officialwebsitemod.Video, error) {
|
||||
if req == nil {
|
||||
return officialwebsitemod.Video{}, nil
|
||||
}
|
||||
data := officialwebsitemod.Video{
|
||||
Title: stringOrZero(req.Title),
|
||||
Description: stringOrZero(req.Description),
|
||||
Cover: stringOrZero(req.Cover),
|
||||
Url: stringOrZero(req.Url),
|
||||
IsHot: boolOrZero(req.IsHot),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: int64OrZero(req.Sort),
|
||||
IsActive: boolOrZero(req.IsActive),
|
||||
},
|
||||
WatchCount: int64OrZero(req.WatchCount),
|
||||
LikeCount: int64OrZero(req.LikeCount),
|
||||
CommentCount: int64OrZero(req.CommentCount),
|
||||
CollectCount: int64OrZero(req.CollectCount),
|
||||
}
|
||||
if req.AlbumID != nil {
|
||||
if *req.AlbumID != "" {
|
||||
albumID, err := primitive.ObjectIDFromHex(*req.AlbumID)
|
||||
if err != nil || isNilObjectID(albumID) {
|
||||
return officialwebsitemod.Video{}, stderr.ErrParamError
|
||||
}
|
||||
data.AlbumID = albumID
|
||||
}
|
||||
}
|
||||
if req.HeroID != nil {
|
||||
if *req.HeroID != "" {
|
||||
heroID, err := primitive.ObjectIDFromHex(*req.HeroID)
|
||||
if err != nil || isNilObjectID(heroID) {
|
||||
return officialwebsitemod.Video{}, stderr.ErrParamError
|
||||
}
|
||||
data.HeroID = heroID
|
||||
}
|
||||
}
|
||||
if req.Tags != nil {
|
||||
data.Tags = *req.Tags
|
||||
}
|
||||
if req.SeoSlug != nil {
|
||||
data.SeoSlug = *req.SeoSlug
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// toM — ModifyVideoReq → officialwebsitemod.M
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
func (req *ModifyVideoReq) toM() (officialwebsitemod.M, error) {
|
||||
if req == nil {
|
||||
return nil, stderr.CodeEmptyData
|
||||
}
|
||||
set := officialwebsitemod.M{}
|
||||
if req.AlbumID != nil {
|
||||
if *req.AlbumID != "" {
|
||||
albumID, err := primitive.ObjectIDFromHex(*req.AlbumID)
|
||||
if err != nil || isNilObjectID(albumID) {
|
||||
return nil, stderr.ErrParamError
|
||||
}
|
||||
set["albumId"] = albumID
|
||||
}
|
||||
}
|
||||
if req.HeroID != nil {
|
||||
if *req.HeroID != "" {
|
||||
heroID, err := primitive.ObjectIDFromHex(*req.HeroID)
|
||||
if err != nil || isNilObjectID(heroID) {
|
||||
return nil, stderr.ErrParamError
|
||||
}
|
||||
set["heroId"] = heroID
|
||||
}
|
||||
}
|
||||
if req.Title != nil {
|
||||
set["title"] = *req.Title
|
||||
}
|
||||
if req.Description != nil {
|
||||
set["description"] = *req.Description
|
||||
}
|
||||
if req.Cover != nil {
|
||||
set["cover"] = *req.Cover
|
||||
}
|
||||
if req.Url != nil {
|
||||
set["url"] = *req.Url
|
||||
}
|
||||
if req.Tags != nil {
|
||||
set["tags"] = *req.Tags
|
||||
}
|
||||
if req.SeoSlug != nil && *req.SeoSlug != "" {
|
||||
set["seoSlug"] = *req.SeoSlug
|
||||
}
|
||||
if req.IsHot != nil {
|
||||
set["isHot"] = *req.IsHot
|
||||
}
|
||||
if req.Sort != nil {
|
||||
set["sort"] = *req.Sort
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
set["isActive"] = *req.IsActive
|
||||
}
|
||||
if req.WatchCount != nil {
|
||||
set["watchCount"] = *req.WatchCount
|
||||
}
|
||||
if req.LikeCount != nil {
|
||||
set["likeCount"] = *req.LikeCount
|
||||
}
|
||||
if req.CommentCount != nil {
|
||||
set["commentCount"] = *req.CommentCount
|
||||
}
|
||||
if req.CollectCount != nil {
|
||||
set["collectCount"] = *req.CollectCount
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
set["updatedAt"] = time.Now().UTC()
|
||||
return officialwebsitemod.M{"$set": set}, nil
|
||||
}
|
||||
|
||||
func BatchImportVideo(req *BatchImportVideoReq) (resp BatchImportVideoResp, err error) {
|
||||
if req == nil || len(req.VideoIds) == 0 {
|
||||
err = stderr.ErrParamError
|
||||
return
|
||||
}
|
||||
objIds := make([]primitive.ObjectID, 0, len(req.VideoIds))
|
||||
videoIDSet := make(map[primitive.ObjectID]struct{}, len(req.VideoIds))
|
||||
for _, videoId := range req.VideoIds {
|
||||
var id primitive.ObjectID
|
||||
id, err = primitive.ObjectIDFromHex(videoId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, exists := videoIDSet[id]; exists {
|
||||
continue
|
||||
}
|
||||
videoIDSet[id] = struct{}{}
|
||||
objIds = append(objIds, id)
|
||||
}
|
||||
if len(objIds) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 1) Filter out videos that already exist in official website video table by `_id`.
|
||||
var videoMod = &officialwebsitemod.Video{}
|
||||
officialVideos, findVideoErr := videoMod.FindMany(officialwebsitemod.M{"_id": bson.M{"$in": objIds}})
|
||||
if findVideoErr != nil {
|
||||
err = findVideoErr
|
||||
return
|
||||
}
|
||||
existedVideoMap := make(map[primitive.ObjectID]struct{}, len(officialVideos))
|
||||
for _, v := range officialVideos {
|
||||
existedVideoMap[v.ID] = struct{}{}
|
||||
}
|
||||
|
||||
needImportVideoIDs := make([]primitive.ObjectID, 0, len(objIds))
|
||||
for _, id := range objIds {
|
||||
if _, exists := existedVideoMap[id]; exists {
|
||||
continue
|
||||
}
|
||||
needImportVideoIDs = append(needImportVideoIDs, id)
|
||||
}
|
||||
if len(needImportVideoIDs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
mats := []vidmod.Matcher{
|
||||
(&vidmod.IDInMatch{IDs: needImportVideoIDs}).New(),
|
||||
}
|
||||
list, err := vidmod.FindMany(mats...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var insertVideos []officialwebsitemod.Video
|
||||
allTagIDs := make([]primitive.ObjectID, 0)
|
||||
allTagIDSet := make(map[primitive.ObjectID]struct{})
|
||||
for _, video := range list {
|
||||
insertVideos = append(insertVideos, officialwebsitemod.Video{
|
||||
ID: video.ID,
|
||||
Title: video.Title,
|
||||
Description: video.Content,
|
||||
Cover: video.Cover,
|
||||
Url: video.SourceURL,
|
||||
IsHot: false,
|
||||
WatchCount: int64(video.FakePlayCount+video.PlayCount) * rand.Int64N(10),
|
||||
LikeCount: int64(video.FakeLikeCount+video.LikeCount) * rand.Int64N(10),
|
||||
CommentCount: int64(video.FakeCommentCount+video.CommentCount) * rand.Int64N(10),
|
||||
CollectCount: int64(video.CollectCount) * rand.Int64N(10),
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
IsActive: true,
|
||||
},
|
||||
BaseModel: officialwebsitemod.BaseModel{
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
for _, tag := range video.Tags {
|
||||
if _, exists := allTagIDSet[tag]; exists {
|
||||
continue
|
||||
}
|
||||
allTagIDSet[tag] = struct{}{}
|
||||
allTagIDs = append(allTagIDs, tag)
|
||||
}
|
||||
}
|
||||
|
||||
if len(insertVideos) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 2) Filter out tags that already exist in official website tag table by `_id`.
|
||||
var tagMod = &officialwebsitemod.Tag{}
|
||||
existedTagMap := map[primitive.ObjectID]struct{}{}
|
||||
existedOfficialTagInfoMap := map[primitive.ObjectID]officialwebsitemod.Tag{}
|
||||
if len(allTagIDs) > 0 {
|
||||
officialWebsiteTags, findTagErr := tagMod.FindMany(officialwebsitemod.M{
|
||||
"_id": bson.M{"$in": allTagIDs},
|
||||
"type": officialwebsitemod.OfficialWebsiteTagTypeVideo,
|
||||
})
|
||||
if findTagErr != nil {
|
||||
err = findTagErr
|
||||
return
|
||||
}
|
||||
existedTagMap = make(map[primitive.ObjectID]struct{}, len(officialWebsiteTags))
|
||||
for _, t := range officialWebsiteTags {
|
||||
existedTagMap[t.ID] = struct{}{}
|
||||
existedOfficialTagInfoMap[t.ID] = t
|
||||
}
|
||||
}
|
||||
|
||||
needImportTagIDs := make([]primitive.ObjectID, 0, len(allTagIDs))
|
||||
for _, tagID := range allTagIDs {
|
||||
if _, exists := existedTagMap[tagID]; exists {
|
||||
continue
|
||||
}
|
||||
needImportTagIDs = append(needImportTagIDs, tagID)
|
||||
}
|
||||
|
||||
tagsMap, err := tagmod.FindTagsMapByIDS(needImportTagIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var insertTags []officialwebsitemod.Tag
|
||||
for _, tagID := range needImportTagIDs {
|
||||
tag, ok := tagsMap[tagID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
insertTags = append(insertTags, officialwebsitemod.Tag{
|
||||
ID: tag.ID,
|
||||
Name: tag.TagName,
|
||||
Type: officialwebsitemod.OfficialWebsiteTagTypeVideo,
|
||||
IsHot: len(tag.HotMark) > 0,
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
IsActive: true,
|
||||
},
|
||||
BaseModel: officialwebsitemod.BaseModel{
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Fill video tags from tag info map to keep video docs self-contained.
|
||||
for i := range insertVideos {
|
||||
srcVideo := list[i]
|
||||
videoTags := make([]officialwebsitemod.Tag, 0, len(srcVideo.Tags))
|
||||
for _, tagID := range srcVideo.Tags {
|
||||
if oldTag, exists := existedOfficialTagInfoMap[tagID]; exists {
|
||||
videoTags = append(videoTags, officialwebsitemod.Tag{
|
||||
ID: oldTag.ID,
|
||||
Name: oldTag.Name,
|
||||
Type: officialwebsitemod.OfficialWebsiteTagTypeVideo,
|
||||
IsHot: oldTag.IsHot,
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
Sort: oldTag.Sort,
|
||||
IsActive: true,
|
||||
},
|
||||
})
|
||||
continue
|
||||
}
|
||||
tag, ok := tagsMap[tagID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
videoTags = append(videoTags, officialwebsitemod.Tag{
|
||||
ID: tag.ID,
|
||||
Name: tag.TagName,
|
||||
Type: officialwebsitemod.OfficialWebsiteTagTypeVideo,
|
||||
IsHot: len(tag.HotMark) > 0,
|
||||
SortModel: officialwebsitemod.SortModel{
|
||||
IsActive: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
insertVideos[i].Tags = videoTags
|
||||
}
|
||||
|
||||
// 3) Insert filtered videos and tags into official website tables separately.
|
||||
var ids []primitive.ObjectID
|
||||
ids, err = videoMod.InsertMany(insertVideos)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range insertTags {
|
||||
if createErr := insertTags[i].Create(); createErr != nil {
|
||||
err = createErr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resp.Count = int64(len(ids))
|
||||
return
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// Batch Import types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type BatchImportVideoReq struct {
|
||||
VideoIds []string `json:"videoIds"`
|
||||
}
|
||||
|
||||
type BatchImportVideoResp struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// List types
|
||||
// ─────────────────────────────────────────
|
||||
|
||||
type VideoListReq struct {
|
||||
PageReq
|
||||
ID *string `form:"id"`
|
||||
IDs []string `form:"ids"`
|
||||
PrimiteveIDs []primitive.ObjectID `form:"primitiveIds"`
|
||||
AlbumID *string `form:"albumId"`
|
||||
HeroID *string `form:"heroID"`
|
||||
Title *string `form:"title"`
|
||||
Description *string `form:"description"`
|
||||
Cover *string `form:"cover"`
|
||||
Url *string `form:"url"`
|
||||
Tags *string `form:"tags"`
|
||||
IsHot *bool `form:"isHot"`
|
||||
Sort *int64 `form:"sort"`
|
||||
IsActive *bool `form:"isActive"`
|
||||
CreatedAt *time.Time `form:"createdAt"`
|
||||
UpdatedAt *time.Time `form:"updatedAt"`
|
||||
DeletedAt *time.Time `form:"deletedAt"`
|
||||
}
|
||||
|
||||
func (r *VideoListReq) Query() (officialwebsitemod.M, error) {
|
||||
filter := officialwebsitemod.M{}
|
||||
if err := parseObjectIDFilter(filter, "_id", r.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(r.IDs) > 0 {
|
||||
var objIDs []primitive.ObjectID
|
||||
for _, id := range r.IDs {
|
||||
objID, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objIDs = append(objIDs, objID)
|
||||
}
|
||||
filter["_id"] = bson.M{"$in": objIDs}
|
||||
}
|
||||
if len(r.PrimiteveIDs) > 0 {
|
||||
filter["_id"] = bson.M{"$in": r.PrimiteveIDs}
|
||||
}
|
||||
if err := parseObjectIDFilter(filter, "albumId", r.AlbumID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseObjectIDFilter(filter, "heroId", r.HeroID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Title != nil {
|
||||
filter["title"] = *r.Title
|
||||
}
|
||||
if r.Description != nil {
|
||||
filter["description"] = *r.Description
|
||||
}
|
||||
if r.Cover != nil {
|
||||
filter["cover"] = *r.Cover
|
||||
}
|
||||
if r.Url != nil {
|
||||
filter["url"] = *r.Url
|
||||
}
|
||||
if err := parseJSONFilter(filter, "tags", r.Tags, &[]officialwebsitemod.Tag{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsHot != nil {
|
||||
filter["isHot"] = *r.IsHot
|
||||
}
|
||||
if r.Sort != nil {
|
||||
filter["sort"] = *r.Sort
|
||||
}
|
||||
if r.IsActive != nil {
|
||||
filter["isActive"] = *r.IsActive
|
||||
}
|
||||
if r.CreatedAt != nil {
|
||||
filter["createdAt"] = *r.CreatedAt
|
||||
}
|
||||
if r.UpdatedAt != nil {
|
||||
filter["updatedAt"] = *r.UpdatedAt
|
||||
}
|
||||
if r.DeletedAt != nil {
|
||||
filter["deletedAt"] = *r.DeletedAt
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package officialser
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/officialmod"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// QueryAll 查询列表
|
||||
func QueryAll(in *officialmod.QueryAllCond) ([]*officialmod.OfficialConfig, stderr.Code) {
|
||||
list, err := officialmod.QueryAll(in, in.Options())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
return list, stderr.Success
|
||||
}
|
||||
|
||||
// UpdateOfficial 编辑官方配置
|
||||
func UpdateOfficial(officialID primitive.ObjectID, doc officialmod.OfficiaUpdateDoc) (err error) {
|
||||
tMod, err := officialmod.FindOneTagByID(officialID)
|
||||
if err != nil {
|
||||
log.Error("web service Official UpdateOfficial FindOneTagByID fail error", log.E(err), log.Any("officialID", officialID))
|
||||
return
|
||||
}
|
||||
if tMod.ID.IsZero() {
|
||||
log.Error("web service Official UpdateOfficial Official is null", log.E(err), log.Any("officialID", officialID))
|
||||
return
|
||||
}
|
||||
return officialmod.UpdateOneOfficiaByID(officialID, doc)
|
||||
}
|
||||
|
||||
// AddOfficial 添加标签
|
||||
func AddOfficial(official officialmod.OfficialConfig) (data interface{}, err error) {
|
||||
return officialmod.InsertOne(&official)
|
||||
}
|
||||
|
||||
// DelOfficial 删除标签
|
||||
func DelOfficial(tag officialmod.WebOfficialDeleteReqInfo) (err error) {
|
||||
_, err = officialmod.DeleteOfficias(tag.IDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package operationlogser
|
||||
|
||||
import (
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/operationlogmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func GetOperationLogList(req GetOperationLogListReq) (*GetOperationLogListResp, stderr.Code) {
|
||||
data := new(GetOperationLogListResp)
|
||||
filter := bson.M{}
|
||||
if req.UserID != nil && *req.UserID > 0 {
|
||||
filter["userID"] = *req.UserID
|
||||
}
|
||||
if req.CreatedUser != nil && len(*req.CreatedUser) > 0 {
|
||||
filter["createdUser"] = *req.CreatedUser
|
||||
}
|
||||
if req.Content != nil && len(*req.Content) > 0 {
|
||||
filter["afterContent"] = *req.Content
|
||||
}
|
||||
if req.Start != nil && !req.Start.IsZero() && req.End != nil && !req.End.IsZero() {
|
||||
filter["createdAt"] = bson.M{"$gte": req.Start, "$lt": req.End}
|
||||
}
|
||||
if req.OperationType != nil && *req.OperationType > 0 {
|
||||
filter["operationType"] = *req.OperationType
|
||||
}
|
||||
count := operationlogmod.Count(filter)
|
||||
data.Total = count
|
||||
opts := (&options.FindOptions{}).
|
||||
SetSort(bson.D{{Key: "createdAt", Value: -1}}).SetLimit(int64(req.PageSize)).SetSkip(int64((req.PageNumber - 1) * req.PageSize))
|
||||
operationlogList, serr := operationlogmod.FindManyOperationLog(filter, opts)
|
||||
if serr != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
data.List = operationlogList
|
||||
return data, stderr.Success
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package operationlogser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/operationlogmod"
|
||||
)
|
||||
|
||||
// GetOperationLogListReq 获取帖子、用户操作日志列表 请求数据
|
||||
type GetOperationLogListReq struct {
|
||||
UserID *int64 `form:"userID,omitempty" json:"userID,omitempty" bson:"userID,omitempty"` //用户ID
|
||||
OperationType *int `form:"operationType,omitempty" json:"operationType,omitempty" bson:"operationType,omitempty"` //操作类型
|
||||
Content *string `form:"content,omitempty" json:"content,omitempty" bson:"content,omitempty"` //操作内容
|
||||
CreatedUser *string `form:"createdUser,omitempty" json:"createdUser,omitempty" bson:"createdUser,omitempty"` //操作人
|
||||
Start *time.Time `form:"start,omitempty" json:"start,omitempty" bson:"start,omitempty"` //开始时间
|
||||
End *time.Time `form:"end,omitempty" json:"end,omitempty" bson:"end,omitempty"` //结束时间
|
||||
commod.Page
|
||||
}
|
||||
|
||||
// GetOperationLogListResp 获取帖子、用户操作日志列表 回复数据
|
||||
type GetOperationLogListResp struct {
|
||||
Total int64 `json:"total" bson:"total"`
|
||||
List []operationlogmod.OperationLog `json:"list" bson:"list"`
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user