add tag search api

This commit is contained in:
mubai
2023-07-05 00:01:59 +08:00
parent 0b32d81785
commit 92a853a196
13 changed files with 593 additions and 292 deletions

View File

@@ -140,9 +140,9 @@ func FilmCategory(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": gin.H{
"list": logic.IL.GetFilmCategory(pid, "pid", &page),
"category": category,
"searchTags": logic.IL.SearchTags(pid),
"list": logic.IL.GetFilmCategory(pid, "pid", &page),
"category": category,
"search": logic.IL.SearchTags(pid),
},
"page": page,
})
@@ -153,9 +153,57 @@ func FilmCategory(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": gin.H{
"list": logic.IL.GetFilmCategory(cid, "cid", &page),
"category": category,
"searchTags": logic.IL.SearchTags(pid),
"list": logic.IL.GetFilmCategory(cid, "cid", &page),
"category": category,
"search": logic.IL.SearchTags(pid),
},
"page": page,
})
// 获取请求参数
}
func FilmTagSearch(c *gin.Context) {
params := model.SearchTagsVO{}
pidStr := c.DefaultQuery("Pid", "")
cidStr := c.DefaultQuery("Category", "")
yStr := c.DefaultQuery("Year", "")
if pidStr == "" {
c.JSON(http.StatusOK, gin.H{
"status": StatusFailed,
"message": "缺少分类信息",
})
return
}
params.Pid, _ = strconv.ParseInt(pidStr, 10, 64)
params.Cid, _ = strconv.ParseInt(cidStr, 10, 64)
params.Plot = c.DefaultQuery("Plot", "")
params.Area = c.DefaultQuery("Area", "")
params.Language = c.DefaultQuery("Language", "")
params.Year, _ = strconv.ParseInt(yStr, 10, 64)
params.Sort = c.DefaultQuery("Sort", "update_stamp")
// 设置分页信息
currentStr := c.DefaultQuery("current", "1")
current, _ := strconv.Atoi(currentStr)
page := model.Page{PageSize: 49, Current: current}
logic.IL.GetFilmsByTags(params, &page)
// 返回对应信息
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": gin.H{
"title": logic.IL.GetPidCategory(params.Pid).Category,
"list": logic.IL.GetFilmsByTags(params, &page),
"search": logic.IL.SearchTags(params.Pid),
"params": map[string]string{
"Pid": pidStr,
"Category": cidStr,
"Plot": params.Plot,
"Area": params.Area,
"Language": params.Language,
"Year": yStr,
"Sort": params.Sort,
},
},
"page": page,
})

View File

@@ -198,3 +198,15 @@ func multipleSource(detail *model.MovieDetail) {
}
}
// GetFilmsByTags 通过searchTag 返回满足条件的分页影片信息
func (i *IndexLogic) GetFilmsByTags(st model.SearchTagsVO, page *model.Page) []model.MovieBasicInfo {
// 获取满足条件的影片id 列表
sl := model.GetSearchInfosByTags(st, page)
// 通过key 获取对应影片的基本信息
var list []model.MovieBasicInfo
for _, s := range sl {
list = append(list, model.GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)))
}
return list
}

View File

@@ -0,0 +1,11 @@
package model
type SearchTagsVO struct {
Pid int64
Cid int64
Plot string
Area string
Language string
Year int64
Sort string
}

View File

@@ -10,6 +10,7 @@ import (
"reflect"
"regexp"
"server/config"
"server/plugin/common/param"
"server/plugin/db"
"strings"
"time"
@@ -46,6 +47,12 @@ type Page struct {
//List []interface{} `json:"list"` // 数据
}
// Tag 影片分类标签结构体
type Tag struct {
Name string `json:"name"`
Value interface{} `json:"value"`
}
func (s *SearchInfo) TableName() string {
return config.SearchTableName
}
@@ -115,6 +122,7 @@ func SaveSearchTag(search SearchInfo) {
searchMap := db.Rdb.HGetAll(db.Cxt, key).Val()
// 是否存储对应分类的map, 如果不存在则缓存一份
if len(searchMap) == 0 {
searchMap = make(map[string]string)
searchMap["Category"] = "类型"
searchMap["Plot"] = "剧情"
searchMap["Area"] = "地区"
@@ -132,37 +140,34 @@ func SaveSearchTag(search SearchInfo) {
case "Category":
// 获取 Category 数据, 如果不存在则缓存一份
if tagCount == 0 {
var tags []redis.Z
for _, t := range GetChildrenTree(search.Pid) {
tags = append(tags, redis.Z{Score: float64(-t.Id), Member: t.Name})
db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.SearchTag, search.Pid, k),
redis.Z{Score: float64(-t.Id), Member: fmt.Sprintf("%v:%v", t.Name, t.Id)})
}
db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.SearchTag, search.Pid, k), tags...)
}
case "Year":
// 获取 Year 数据, 如果不存在则缓存一份
if tagCount == 0 {
var tags []redis.Z
currentYear := time.Now().Year()
for i := 0; i < 12; i++ {
tags = append(tags, redis.Z{Score: float64(currentYear - i), Member: currentYear - i})
db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.SearchTag, search.Pid, k),
redis.Z{Score: float64(currentYear - i), Member: fmt.Sprintf("%v:%v", currentYear-i, currentYear-i)})
}
db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.SearchTag, search.Pid, k), tags...)
}
case "Initial":
// 如果不存在 首字母 Tag 数据, 则缓存一份
if tagCount == 0 {
var tags []redis.Z
for i := 65; i <= 90; i++ {
tags = append(tags, redis.Z{Score: float64(90 - i), Member: fmt.Sprintf("%c", i)})
db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.SearchTag, search.Pid, k),
redis.Z{Score: float64(90 - i), Member: fmt.Sprintf("%c:%c", i, i)})
}
db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.SearchTag, search.Pid, k), tags...)
}
case "Sort":
if tagCount == 0 {
tags := []redis.Z{
{2, "time"},
{1, "hits"},
{0, "score"},
{2, "时间排序:update_stamp"},
{1, "人气排序:hits"},
{0, "评分排序:score"},
}
db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.SearchTag, search.Pid, k), tags...)
}
@@ -185,10 +190,9 @@ func HandleSearchTags(preTags string, k string) {
f := func(sep string) {
for _, t := range strings.Split(preTags, sep) {
// 获取 tag对应的score
score := db.Rdb.ZScore(db.Cxt, k, t).Val()
score := db.Rdb.ZScore(db.Cxt, k, fmt.Sprintf("%v:%v", t, t)).Val()
// 在原score的基础上+1 重新存入redis中
db.Rdb.ZAdd(db.Cxt, k, redis.Z{Score: score + 1, Member: t})
db.Rdb.ZAdd(db.Cxt, k, redis.Z{Score: score + 1, Member: fmt.Sprintf("%v:%v", t, t)})
}
}
switch {
@@ -203,13 +207,18 @@ func HandleSearchTags(preTags string, k string) {
default:
// 获取 tag对应的score
if len(preTags) == 0 || preTags == "其它" {
db.Rdb.ZAdd(db.Cxt, k, redis.Z{Score: 0, Member: preTags})
db.Rdb.ZAdd(db.Cxt, k, redis.Z{Score: 0, Member: fmt.Sprintf("%v:%v", preTags, preTags)})
} else {
score := db.Rdb.ZScore(db.Cxt, k, preTags).Val()
db.Rdb.ZAdd(db.Cxt, k, redis.Z{Score: score + 1, Member: preTags})
score := db.Rdb.ZScore(db.Cxt, k, fmt.Sprintf("%v:%v", preTags, preTags)).Val()
db.Rdb.ZAdd(db.Cxt, k, redis.Z{Score: score + 1, Member: fmt.Sprintf("%v:%v", preTags, preTags)})
}
}
}
func BatchHandleSearchTag(infos ...SearchInfo) {
for _, info := range infos {
SaveSearchTag(info)
}
}
// ================================= Spider 数据处理(mysql) =================================
@@ -241,8 +250,8 @@ func BatchSave(list []SearchInfo) {
tx.Rollback()
return
}
// 插入成功后输出一下成功信息
//log.Println("BatchSave SearchInfo Successful, Count: ", len(list))
// 保存成功后将相应tag数据缓存到redis中
BatchHandleSearchTag(list...)
tx.Commit()
}
@@ -253,7 +262,7 @@ func BatchSaveOrUpdate(list []SearchInfo) {
var count int64
// 通过当前影片id 对应的记录数
tx.Model(&SearchInfo{}).Where("mid", info.Mid).Count(&count)
// 如果存在对应数据则进行更新, 否则进行删除
// 如果存在对应数据则进行更新, 否则保存相应数据
if count > 0 {
// 记录已经存在则执行更新部分内容
err := tx.Model(&SearchInfo{}).Where("mid", info.Mid).Updates(SearchInfo{UpdateStamp: info.UpdateStamp, Hits: info.Hits, State: info.State,
@@ -266,6 +275,8 @@ func BatchSaveOrUpdate(list []SearchInfo) {
if err := tx.Create(&info).Error; err != nil {
tx.Rollback()
}
// 插入成功后保存一份tag信息到redis中
BatchHandleSearchTag(info)
}
}
// 提交事务
@@ -450,35 +461,115 @@ func GetMultiplePlay(siteName, key string) []MovieUrlInfo {
// GetSearchTag 通过影片分类 Pid 返回对应分类的tag信息
func GetSearchTag(pid int64) map[string]interface{} {
// 整合searchTag相关内容
res := make(map[string]interface{})
titles := db.Rdb.HGetAll(db.Cxt, fmt.Sprintf(config.SearchTitle, pid)).Val()
for k, v := range titles {
res["titles"] = titles
// 处理单一分类的数据格式
tagMap := make(map[string]interface{})
for k, _ := range titles {
// 通过 k 获取对应的 tag , 并以score进行排序
tags := db.Rdb.ZRevRange(db.Cxt, fmt.Sprintf(config.SearchTag, pid, k), 0, 10).Val()
res[v] = tags
// 过滤分类tag
switch k {
case "Category", "Year", "Initial", "Sort":
case "Category":
tags := db.Rdb.ZRevRange(db.Cxt, fmt.Sprintf(config.SearchTag, pid, k), 0, -1).Val()
res[v] = tags
tagMap[k] = HandleTagStr(k, tags...)
case "Plot":
tags := db.Rdb.ZRevRange(db.Cxt, fmt.Sprintf(config.SearchTag, pid, k), 0, 10).Val()
res[v] = tags
tagMap[k] = HandleTagStr(k, tags...)
case "Area":
tags := db.Rdb.ZRevRange(db.Cxt, fmt.Sprintf(config.SearchTag, pid, k), 0, 11).Val()
res[v] = tags
tagMap[k] = HandleTagStr(k, tags...)
case "Language":
tags := db.Rdb.ZRevRange(db.Cxt, fmt.Sprintf(config.SearchTag, pid, k), 0, 6).Val()
res[v] = tags
tagMap[k] = HandleTagStr(k, tags...)
case "Year", "Initial", "Sort":
tags := db.Rdb.ZRevRange(db.Cxt, fmt.Sprintf(config.SearchTag, pid, k), 0, -1).Val()
tagMap[k] = HandleTagStr(k, tags...)
default:
break
}
}
res["tags"] = tagMap
// 分类列表展示的顺序
res["sortList"] = []string{"Category", "Plot", "Area", "Language", "Year", "Sort"}
return res
}
// HandleTagStr 处理tag数据格式
func HandleTagStr(title string, tags ...string) []map[string]string {
var r []map[string]string
if !strings.EqualFold(title, "Sort") {
r = append(r, map[string]string{
"Name": "全部",
"Value": "",
})
}
for _, t := range tags {
if sl := strings.Split(t, ":"); len(sl) > 0 {
r = append(r, map[string]string{
"Name": sl[0],
"Value": sl[1],
})
}
}
if !strings.EqualFold(title, "Sort") && !strings.EqualFold(title, "Year") {
r = append(r, map[string]string{
"Name": "其它",
"Value": "其它",
})
}
return r
}
// GetSearchInfosByTags 查询满足searchTag条件的影片分页数据
func GetSearchInfosByTags(st SearchTagsVO, page *Page) []SearchInfo {
// 准备查询语句的条件
qw := db.Mdb.Model(&SearchInfo{})
// 通过searchTags的非空属性值, 拼接对应的查询条件
t := reflect.TypeOf(st)
v := reflect.ValueOf(st)
for i := 0; i < t.NumField(); i++ {
// 如果字段值不为空
value := v.Field(i).Interface()
if !param.IsEmpty(value) {
cName := strings.ToLower(t.Field(i).Name)
switch cName {
case "pid", "cid", "area", "language", "year":
qw = qw.Where(fmt.Sprintf("%s = ?", cName), value)
case "plot":
qw = qw.Where("class_tag LIKE ?", fmt.Sprintf("%%%v%%", value))
case "sort":
qw.Order(fmt.Sprintf("%v Desc", value))
default:
break
}
//// 处理特殊条件
//if strings.EqualFold(cName, "sort") {
// qw.Order(fmt.Sprintf("year Desc, %v Desc", cName))
//}
}
}
// 返回分页参数
GetPage(qw, page)
//
var sl []SearchInfo
if err := qw.Limit(page.PageSize).Offset((page.Current - 1) * page.PageSize).Find(&sl).Error; err != nil {
log.Println(err)
return nil
}
return sl
}
func GetPage(db *gorm.DB, page *Page) {
var count int64
db.Count(&count)
page.Total = int(count)
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
}
// ================================= 接口数据缓存 =================================
// DataCache API请求 数据缓存

View File

@@ -1,4 +1,4 @@
package common
package dp
import (
"server/model"

View File

@@ -1,4 +1,4 @@
package common
package dp
import (
"server/model"

View File

@@ -0,0 +1,40 @@
package param
import (
"log"
"reflect"
)
/*
简单参数处理
*/
// IsEmpty 判断各种基本类型是否为空
func IsEmpty(target any) bool {
switch target.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
log.Println(`target == 0`, target == 0)
return target == reflect.Zero(reflect.TypeOf(target)).Interface()
case string:
return target == ""
case bool:
return target.(bool)
default:
return false
}
}
func IsEmptyRe[T ~int | ~uint | float32 | float64 | string | bool](target T) bool {
v := reflect.ValueOf(target)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32,
reflect.Float64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Interface() == 0
case reflect.String:
return v.String() == ""
case reflect.Bool:
return v.Bool()
default:
return false
}
}

View File

@@ -20,7 +20,7 @@ func InitRedisConn() error {
Addr: config.RedisAddr,
Password: config.RedisPassword,
DB: config.RedisDBNo,
PoolSize: 10, // 默认连接数
PoolSize: 10, // 最大连接数
DialTimeout: time.Second * 10, // 超时时间
})
// 测试连接是否正常
@@ -31,7 +31,7 @@ func InitRedisConn() error {
return nil
}
// 关闭redis连接
// CloseRedis 关闭redis连接
func CloseRedis() error {
return Rdb.Close()
}

View File

@@ -8,7 +8,7 @@ import (
"net/url"
"server/config"
"server/model"
"server/plugin/common"
"server/plugin/common/dp"
"time"
)
@@ -65,9 +65,9 @@ func StartSpider() {
model.CreateSearchTable()
SearchInfoToMdb()
log.Println("SearchInfoToMdb 影片检索信息保存完毕")
// 获取其他站点数据
go MtSiteSpider()
log.Println("Spider End , 数据保存执行完成")
// 获取其他站点数据13
//go MtSiteSpider()
//log.Println("Spider End , 数据保存执行完成")
//time.Sleep(time.Second * 10)
}
@@ -89,7 +89,7 @@ func CategoryList() {
// 获取分类列表信息
classList := movieListInfo.Class
// 组装分类数据信息树形结构
categoryTree := common.CategoryTree(classList)
categoryTree := dp.CategoryTree(classList)
// 序列化tree
data, _ := json.Marshal(categoryTree)
// 保存 tree 到redis
@@ -331,6 +331,6 @@ func GetMovieDetail(pageNumber int, r RequestInfo) (list []model.MovieDetail, er
return
}
// 处理details信息
list = common.ProcessMovieDetailList(details.List)
list = dp.ProcessMovieDetailList(details.List)
return
}

View File

@@ -19,7 +19,7 @@ var (
// RequestInfo 请求参数结构体
type RequestInfo struct {
Uri string `json:"uri"` // 请求url地址
Params url.Values `json:"params"` // 请求参数
Params url.Values `json:"param"` // 请求参数
Header http.Header `json:"header"` // 请求头数据
Resp []byte `json:"resp"` // 响应结果数据
}

View File

@@ -19,6 +19,7 @@ func SetupRouter() *gin.Engine {
r.GET(`/filmPlayInfo`, controller.FilmPlayInfo)
r.GET(`/searchFilm`, controller.SearchFilm)
r.GET(`/filmCategory`, controller.FilmCategory)
r.GET(`/filmClassified`, controller.FilmTagSearch)
// 触发spider
spiderRoute := r.Group(`/spider`)