mirror of
https://github.com/ProudMuBai/GoFilm.git
synced 2026-02-17 08:04:42 +08:00
init
This commit is contained in:
43
server/model/Categories.go
Normal file
43
server/model/Categories.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
)
|
||||
|
||||
// Category 分类信息
|
||||
type Category struct {
|
||||
Id int64 `json:"id"` // 分类ID
|
||||
Pid int64 `json:"pid"` // 父级分类ID
|
||||
Name string `json:"name"` // 分类名称
|
||||
}
|
||||
|
||||
// CategoryTree 分类信息树形结构
|
||||
type CategoryTree struct {
|
||||
*Category
|
||||
Children []*CategoryTree `json:"children"` // 子分类信息
|
||||
}
|
||||
|
||||
// SaveCategoryTree 保存影片分类信息
|
||||
func SaveCategoryTree(tree string) error {
|
||||
return db.Rdb.Set(db.Cxt, config.CategoryTreeKey, tree, config.CategoryTreeExpired).Err()
|
||||
}
|
||||
|
||||
// GetCategoryTree 获取影片分类信息
|
||||
func GetCategoryTree() CategoryTree {
|
||||
data := db.Rdb.Get(db.Cxt, config.CategoryTreeKey).Val()
|
||||
tree := CategoryTree{}
|
||||
_ = json.Unmarshal([]byte(data), &tree)
|
||||
return tree
|
||||
}
|
||||
|
||||
// ExistsCategoryTree 查询分类信息是否存在
|
||||
func ExistsCategoryTree() bool {
|
||||
exists, err := db.Rdb.Exists(db.Cxt, config.CategoryTreeKey).Result()
|
||||
if err != nil {
|
||||
log.Println("ExistsCategoryTree Error", err)
|
||||
}
|
||||
return exists == 1
|
||||
}
|
||||
81
server/model/LZJson.go
Normal file
81
server/model/LZJson.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package model
|
||||
|
||||
/*
|
||||
量子资源JSON解析
|
||||
*/
|
||||
|
||||
// ClassInfo class 分类数据
|
||||
type ClassInfo struct {
|
||||
Id int64 `json:"type_id"` //分类ID
|
||||
Pid int64 `json:"type_pid"` //上级分类ID
|
||||
Name string `json:"type_name"` //分类名称
|
||||
}
|
||||
|
||||
// MovieInfo 影片数据
|
||||
type MovieInfo struct {
|
||||
Id int64 `json:"vod_id"` // 影片ID
|
||||
Name string `json:"vod_name"` // 影片名
|
||||
Cid int64 `json:"type_id"` // 所属分类ID
|
||||
CName string `json:"type_name"` // 所属分类名称
|
||||
EnName string `json:"vod_en"` // 英文片名
|
||||
Time string `json:"vod_time"` // 更新时间
|
||||
Remarks string `json:"vod_remarks"` // 备注 | 清晰度
|
||||
PlayFrom string `json:"vod_play_from"` // 播放来源
|
||||
}
|
||||
|
||||
// MovieListInfo 影视列表响应数据
|
||||
type MovieListInfo struct {
|
||||
Code int64 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Page string `json:"page"`
|
||||
PageCount int64 `json:"pagecount"`
|
||||
Limit string `json:"limit"`
|
||||
Total int64 `json:"total"`
|
||||
List []MovieInfo `json:"list"`
|
||||
Class []ClassInfo `json:"class"`
|
||||
}
|
||||
|
||||
// MovieDetailInfo 影片详情数据 (只保留需要的部分)
|
||||
type MovieDetailInfo struct {
|
||||
Id int64 `json:"vod_id"` //影片Id
|
||||
Cid int64 `json:"type_id"` //分类ID
|
||||
Pid int64 `json:"type_id_1"` //一级分类ID
|
||||
Name string `json:"vod_name"` //片名
|
||||
SubTitle string `json:"vod_sub"` //子标题
|
||||
CName string `json:"type_name"` //分类名称
|
||||
EnName string `json:"vod_en"` //英文名
|
||||
Initial string `json:"vod_letter"` //首字母
|
||||
ClassTag string `json:"vod_class"` //分类标签
|
||||
Pic string `json:"vod_pic"` //简介图片
|
||||
Actor string `json:"vod_actor"` //主演
|
||||
Director string `json:"vod_director"` //导演
|
||||
Writer string `json:"vod_writer"` //作者
|
||||
Blurb string `json:"vod_blurb"` //简介, 残缺,不建议使用
|
||||
Remarks string `json:"vod_remarks"` // 更新情况
|
||||
PubDate string `json:"vod_pubdate"` //上映时间
|
||||
Area string `json:"vod_area"` // 地区
|
||||
Language string `json:"vod_lang"` //语言
|
||||
Year string `json:"vod_year"` //年份
|
||||
State string `json:"vod_state"` //影片状态 正片|预告...
|
||||
UpdateTime string `json:"vod_time"` //更新时间
|
||||
AddTime int64 `json:"vod_time_add"` //资源添加时间戳
|
||||
DbId int64 `json:"vod_douban_id"` //豆瓣id
|
||||
DbScore string `json:"vod_douban_score"` // 豆瓣评分
|
||||
Content string `json:"vod_content"` //内容简介
|
||||
PlayFrom string `json:"vod_play_from"` // 播放来源
|
||||
PlaySeparator string `json:"vod_play_note"` // 播放信息分隔符
|
||||
PlayUrl string `json:"vod_play_url"` //播放地址url
|
||||
DownFrom string `json:"vod_down_from"` //下载来源 例: http
|
||||
DownUrl string `json:"vod_down_url"` // 下载url地址
|
||||
}
|
||||
|
||||
// DetailListInfo 影视详情信息
|
||||
type DetailListInfo struct {
|
||||
Code int64 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Page int64 `json:"page"`
|
||||
PageCount int64 `json:"pagecount"`
|
||||
Limit string `json:"limit"`
|
||||
Total int64 `json:"total"`
|
||||
List []MovieDetailInfo `json:"list"`
|
||||
}
|
||||
332
server/model/Movies.go
Normal file
332
server/model/Movies.go
Normal file
@@ -0,0 +1,332 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"log"
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Movie 影片基本信息
|
||||
type Movie struct {
|
||||
Id int64 `json:"id"` // 影片ID
|
||||
Name string `json:"name"` // 影片名
|
||||
Cid int64 `json:"cid"` // 所属分类ID
|
||||
CName string `json:"CName"` // 所属分类名称
|
||||
EnName string `json:"enName"` // 英文片名
|
||||
Time string `json:"time"` // 更新时间
|
||||
Remarks string `json:"remarks"` // 备注 | 清晰度
|
||||
PlayFrom string `json:"playFrom"` // 播放来源
|
||||
}
|
||||
|
||||
// MovieDescriptor 影片详情介绍信息
|
||||
type MovieDescriptor struct {
|
||||
SubTitle string `json:"subTitle"` //子标题
|
||||
CName string `json:"cName"` //分类名称
|
||||
EnName string `json:"enName"` //英文名
|
||||
Initial string `json:"initial"` //首字母
|
||||
ClassTag string `json:"classTag"` //分类标签
|
||||
Actor string `json:"actor"` //主演
|
||||
Director string `json:"director"` //导演
|
||||
Writer string `json:"writer"` //作者
|
||||
Blurb string `json:"blurb"` //简介, 残缺,不建议使用
|
||||
Remarks string `json:"remarks"` // 更新情况
|
||||
ReleaseDate string `json:"releaseDate"` //上映时间
|
||||
Area string `json:"area"` // 地区
|
||||
Language string `json:"language"` //语言
|
||||
Year string `json:"year"` //年份
|
||||
State string `json:"state"` //影片状态 正片|预告...
|
||||
UpdateTime string `json:"updateTime"` //更新时间
|
||||
AddTime int64 `json:"addTime"` //资源添加时间戳
|
||||
DbId int64 `json:"dbId"` //豆瓣id
|
||||
DbScore string `json:"dbScore"` // 豆瓣评分
|
||||
Content string `json:"content"` //内容简介
|
||||
}
|
||||
|
||||
// MovieBasicInfo 影片基本信息
|
||||
type MovieBasicInfo struct {
|
||||
Id int64 `json:"id"` //影片Id
|
||||
Cid int64 `json:"cid"` //分类ID
|
||||
Pid int64 `json:"pid"` //一级分类ID
|
||||
Name string `json:"name"` //片名
|
||||
SubTitle string `json:"subTitle"` //子标题
|
||||
CName string `json:"cName"` //分类名称
|
||||
State string `json:"state"` //影片状态 正片|预告...
|
||||
Picture string `json:"picture"` //简介图片
|
||||
Actor string `json:"actor"` //主演
|
||||
Director string `json:"director"` //导演
|
||||
Blurb string `json:"blurb"` //简介, 不完整
|
||||
Remarks string `json:"remarks"` // 更新情况
|
||||
Area string `json:"area"` // 地区
|
||||
Year string `json:"year"` //年份
|
||||
}
|
||||
|
||||
// MovieUrlInfo 影视资源url信息
|
||||
type MovieUrlInfo struct {
|
||||
Episode string `json:"episode"` // 集数
|
||||
Link string `json:"link"` // 播放地址
|
||||
}
|
||||
|
||||
// MovieDetail 影片详情信息
|
||||
type MovieDetail struct {
|
||||
Id int64 `json:"id"` //影片Id
|
||||
Cid int64 `json:"cid"` //分类ID
|
||||
Pid int64 `json:"pid"` //一级分类ID
|
||||
Name string `json:"name"` //片名
|
||||
Picture string `json:"picture"` //简介图片
|
||||
PlayFrom []string `json:"playFrom"` // 播放来源
|
||||
DownFrom string `json:"DownFrom"` //下载来源 例: http
|
||||
//PlaySeparator string `json:"playSeparator"` // 播放信息分隔符
|
||||
PlayList [][]MovieUrlInfo `json:"playList"` //播放地址url
|
||||
DownloadList [][]MovieUrlInfo `json:"downloadList"` // 下载url地址
|
||||
MovieDescriptor `json:"descriptor"` //影片描述信息
|
||||
}
|
||||
|
||||
// SaveMoves 保存影片分页请求list
|
||||
func SaveMoves(list []Movie) (err error) {
|
||||
// 整合数据
|
||||
for _, m := range list {
|
||||
//score, _ := time.ParseInLocation(time.DateTime, m.Time, time.Local)
|
||||
movie, _ := json.Marshal(m)
|
||||
// 以Cid为目录为集合进行存储, 便于后续搜索, 以影片id为分值进行存储 例 MovieList:Cid%d
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.MovieListInfoKey, m.Cid), redis.Z{Score: float64(m.Id), Member: movie}).Err()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AllMovieInfoKey 获取redis中所有的影视列表信息key MovieList:Cid
|
||||
func AllMovieInfoKey() []string {
|
||||
return db.Rdb.Keys(db.Cxt, fmt.Sprint("MovieList:Cid*")).Val()
|
||||
}
|
||||
|
||||
// GetMovieListByKey 获取指定分类的影片列表数据
|
||||
func GetMovieListByKey(key string) []string {
|
||||
return db.Rdb.ZRange(db.Cxt, key, 0, -1).Val()
|
||||
}
|
||||
|
||||
// SaveDetails 保存影片详情信息到redis中 格式: MovieDetail:Cid?:Id?
|
||||
func SaveDetails(list []MovieDetail) (err error) {
|
||||
// 遍历list中的信息
|
||||
for _, detail := range list {
|
||||
// 序列化影片详情信息
|
||||
data, _ := json.Marshal(detail)
|
||||
// 1. 原使用Zset存储, 但是不便于单个检索 db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Cid%d", config.MovieDetailKey, detail.Cid), redis.Z{Score: float64(detail.Id), Member: member}).Err()
|
||||
// 改为普通 k v 存储, k-> id关键字, v json序列化的结果, //只保留十天, 没周更新一次
|
||||
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
|
||||
// 2. 同步保存简略信息到redis中
|
||||
SaveMovieBasicInfo(detail)
|
||||
// 3. 保存Search检索信息到redis
|
||||
if err == nil {
|
||||
// 转换 detail信息
|
||||
searchInfo := ConvertSearchInfo(detail)
|
||||
// 放弃redis进行检索, 多条件处理不方便
|
||||
//err = AddSearchInfo(searchInfo)
|
||||
// 只存储用于检索对应影片的关键字信息
|
||||
SearchKeyword(searchInfo)
|
||||
}
|
||||
|
||||
}
|
||||
// 保存一份search信息到mysql, 批量存储
|
||||
BatchSaveSearchInfo(list)
|
||||
return err
|
||||
}
|
||||
|
||||
// SaveMovieBasicInfo 摘取影片的详情部分信息转存为影视基本信息
|
||||
func SaveMovieBasicInfo(detail MovieDetail) {
|
||||
basicInfo := MovieBasicInfo{
|
||||
Id: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
SubTitle: detail.SubTitle,
|
||||
CName: detail.CName,
|
||||
State: detail.State,
|
||||
Picture: detail.Picture,
|
||||
Actor: detail.Actor,
|
||||
Director: detail.Director,
|
||||
Blurb: detail.Blurb,
|
||||
Remarks: detail.Remarks,
|
||||
Area: detail.Area,
|
||||
Year: detail.Year,
|
||||
}
|
||||
data, _ := json.Marshal(basicInfo)
|
||||
_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
|
||||
}
|
||||
|
||||
// AddSearchInfo 将影片关键字信息整合后存入search 集合中
|
||||
func AddSearchInfo(searchInfo SearchInfo) (err error) {
|
||||
// 片名 Name 分类 CName 类别标签 classTag 地区 Area 语言 Language 年份 Year 首字母 Initial, 排序
|
||||
data, _ := json.Marshal(searchInfo)
|
||||
// 时间排序 score -->时间戳 DbId 排序 --> 热度, 评分排序 DbScore
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchTimeListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Time), Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchScoreListKey, searchInfo.Pid), redis.Z{Score: searchInfo.Score, Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchHeatListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Rank), Member: data}).Err()
|
||||
// 添加搜索关键字信息
|
||||
SearchKeyword(searchInfo)
|
||||
return
|
||||
}
|
||||
|
||||
// SearchKeyword 设置search关键字集合
|
||||
func SearchKeyword(search SearchInfo) {
|
||||
// 首先获取redis中的search 关键字信息
|
||||
key := fmt.Sprintf("%s:Pid%d", config.SearchKeys, search.Pid)
|
||||
keyword := db.Rdb.HGetAll(db.Cxt, key).Val()
|
||||
if keyword["Year"] == "" {
|
||||
currentYear := time.Now().Year()
|
||||
year := ""
|
||||
for i := 0; i < 12; i++ {
|
||||
// 提供当前年份前推十二年的搜索
|
||||
year = fmt.Sprintf("%s,%d", year, currentYear-i)
|
||||
}
|
||||
initial := ""
|
||||
for i := 65; i <= 90; i++ {
|
||||
initial = fmt.Sprintf("%s,%c", initial, i)
|
||||
}
|
||||
keyword = map[string]string{
|
||||
//"Name": "",
|
||||
"Category": "",
|
||||
"Tag": "",
|
||||
"Area": "",
|
||||
"Language": "",
|
||||
"Year": strings.Trim(year, ","),
|
||||
"Initial": strings.Trim(initial, ","),
|
||||
"Sort": "Time,Db,Score", // 默认,一般不修改
|
||||
}
|
||||
}
|
||||
// 分类标签处理
|
||||
if !strings.Contains(keyword["Category"], search.CName) {
|
||||
keyword["Category"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Category"], search.CName), ",")
|
||||
}
|
||||
// 影视内容分类处理
|
||||
if strings.Contains(search.ClassTag, "/") {
|
||||
for _, t := range strings.Split(search.ClassTag, "/") {
|
||||
if !strings.Contains(keyword["Tag"], t) {
|
||||
keyword["Tag"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Tag"], t), ",")
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(search.ClassTag, ",") {
|
||||
for _, t := range strings.Split(search.ClassTag, ",") {
|
||||
if !strings.Contains(keyword["Tag"], t) {
|
||||
keyword["Tag"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Tag"], t), ",")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !strings.Contains(keyword["Tag"], search.ClassTag) {
|
||||
keyword["Tag"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Tag"], search.ClassTag), ",")
|
||||
}
|
||||
}
|
||||
// 如果地区中包含 / 分隔符 则先进行切分处理
|
||||
if strings.Contains(search.Area, "/") {
|
||||
for _, s := range strings.Split(search.Area, "/") {
|
||||
if !strings.Contains(keyword["Area"], strings.TrimSpace(s)) {
|
||||
keyword["Area"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Area"], s), ",")
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(search.Area, ",") {
|
||||
for _, s := range strings.Split(search.Area, ",") {
|
||||
if !strings.Contains(keyword["Area"], strings.TrimSpace(s)) {
|
||||
keyword["Area"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Area"], s), ",")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !strings.Contains(keyword["Area"], search.Area) {
|
||||
keyword["Area"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Area"], search.Area), ",")
|
||||
}
|
||||
}
|
||||
// 语言处理
|
||||
if strings.Contains(search.Language, "/") {
|
||||
for _, l := range strings.Split(search.Language, "/") {
|
||||
if !strings.Contains(keyword["Language"], l) {
|
||||
keyword["Language"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Language"], l), ",")
|
||||
}
|
||||
}
|
||||
|
||||
} else if strings.Contains(search.Language, ",") {
|
||||
for _, l := range strings.Split(search.Language, ",") {
|
||||
if !strings.Contains(keyword["Language"], l) {
|
||||
keyword["Language"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Language"], l), ",")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !strings.Contains(keyword["Language"], search.Language) {
|
||||
keyword["Language"] = strings.Trim(fmt.Sprintf("%s,%s", keyword["Language"], search.Language), ",")
|
||||
}
|
||||
}
|
||||
_ = db.Rdb.HMSet(db.Cxt, key, keyword).Err()
|
||||
}
|
||||
|
||||
// BatchSaveSearchInfo 批量保存Search信息
|
||||
func BatchSaveSearchInfo(list []MovieDetail) {
|
||||
var infoList []SearchInfo
|
||||
for _, v := range list {
|
||||
infoList = append(infoList, ConvertSearchInfo(v))
|
||||
}
|
||||
// 将检索信息存入redis中做一次转存
|
||||
RdbSaveSearchInfo(infoList)
|
||||
|
||||
// 废弃方案, 频繁大量入库容易引起主键冲突, 事务影响速率
|
||||
// 批量插入时应对已存在数据进行检测, 使用mysql事务进行锁表
|
||||
//BatchSave(infoList)
|
||||
// 使用批量添加or更新
|
||||
//BatchSaveOrUpdate(infoList)
|
||||
}
|
||||
|
||||
// ConvertSearchInfo 将detail信息处理成 searchInfo
|
||||
func ConvertSearchInfo(detail MovieDetail) SearchInfo {
|
||||
score, _ := strconv.ParseFloat(detail.DbScore, 64)
|
||||
stamp, _ := time.ParseInLocation(time.DateTime, detail.UpdateTime, time.Local)
|
||||
year, err := strconv.ParseInt(detail.Year, 10, 64)
|
||||
if err != nil {
|
||||
year = 0
|
||||
}
|
||||
|
||||
return SearchInfo{
|
||||
Mid: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
CName: detail.CName,
|
||||
ClassTag: detail.ClassTag,
|
||||
Area: detail.Area,
|
||||
Language: detail.Language,
|
||||
Year: year,
|
||||
Initial: detail.Initial,
|
||||
Score: score,
|
||||
Rank: detail.DbId,
|
||||
Time: stamp.Unix(),
|
||||
State: detail.State,
|
||||
Remarks: detail.Remarks,
|
||||
// releaseDate 部分影片缺失该参数, 所以使用添加时间作为上映时间排序
|
||||
ReleaseDate: detail.AddTime,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBasicInfoByKey 获取Id对应的影片基本信息
|
||||
func GetBasicInfoByKey(key string) MovieBasicInfo {
|
||||
// 反序列化得到的结果
|
||||
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
|
||||
basic := MovieBasicInfo{}
|
||||
_ = json.Unmarshal(data, &basic)
|
||||
return basic
|
||||
}
|
||||
|
||||
// GetDetailByKey 获取影片对应的详情信息
|
||||
func GetDetailByKey(key string) MovieDetail {
|
||||
// 反序列化得到的结果
|
||||
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
|
||||
detail := MovieDetail{}
|
||||
_ = json.Unmarshal(data, &detail)
|
||||
return detail
|
||||
}
|
||||
|
||||
// SearchMovie 搜索关键字影片
|
||||
func SearchMovie() {
|
||||
data, err := db.Rdb.ZScan(db.Cxt, "MovieList:cid30", 0, `*天使*`, config.SearchCount).Val()
|
||||
log.Println(err)
|
||||
fmt.Println(data)
|
||||
}
|
||||
291
server/model/Search.go
Normal file
291
server/model/Search.go
Normal file
@@ -0,0 +1,291 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SearchInfo 存储用于检索的信息
|
||||
type SearchInfo struct {
|
||||
gorm.Model
|
||||
Mid int64 `json:"mid" gorm:"uniqueIndex:idx_mid"` //影片ID
|
||||
Cid int64 `json:"cid"` //分类ID
|
||||
Pid int64 `json:"pid"` //上级分类ID
|
||||
Name string `json:"name"` // 片名
|
||||
CName string `json:"CName"` // 分类名称
|
||||
ClassTag string `json:"classTag"` //类型标签
|
||||
Area string `json:"area"` // 地区
|
||||
Language string `json:"language"` // 语言
|
||||
Year int64 `json:"year"` // 年份
|
||||
Initial string `json:"initial"` // 首字母
|
||||
Score float64 `json:"score"` //评分
|
||||
Time int64 `json:"time"` // 更新时间
|
||||
Rank int64 `json:"rank"` // 热度排行id
|
||||
State string `json:"state"` //状态 正片|预告
|
||||
Remarks string `json:"remarks"` // 完结 | 更新至x集
|
||||
ReleaseDate int64 `json:"releaseDate"` //上映时间 时间戳
|
||||
}
|
||||
|
||||
// Page 分页信息结构体
|
||||
type Page struct {
|
||||
PageSize int `json:"pageSize"` // 每页大小
|
||||
Current int `json:"current"` // 当前页
|
||||
PageCount int `json:"pageCount"` // 总页数
|
||||
Total int `json:"total"` // 总记录数
|
||||
//List []interface{} `json:"list"` // 数据
|
||||
}
|
||||
|
||||
func (s *SearchInfo) TableName() string {
|
||||
return "search_lz"
|
||||
//return "search_fs"
|
||||
}
|
||||
|
||||
// ================================= Spider 数据处理(redis) =================================
|
||||
|
||||
// RdbSaveSearchInfo 批量保存检索信息到redis
|
||||
func RdbSaveSearchInfo(list []SearchInfo) {
|
||||
// 1.整合一下zset数据集
|
||||
var members []redis.Z
|
||||
for _, s := range list {
|
||||
member, _ := json.Marshal(s)
|
||||
members = append(members, redis.Z{Score: float64(s.Mid), Member: member})
|
||||
}
|
||||
// 2.批量保存到zset集合中
|
||||
db.Rdb.ZAdd(db.Cxt, config.SearchInfoTemp, members...)
|
||||
}
|
||||
|
||||
// ScanSearchInfo 批量扫描处理详情检索信息, 返回检索信息列表和下次开始的游标
|
||||
func ScanSearchInfo(cursor uint64, count int64) ([]SearchInfo, uint64) {
|
||||
// 1.从redis中批量扫描详情信息
|
||||
list, nextCursor := db.Rdb.ZScan(db.Cxt, config.SearchInfoTemp, cursor, "*", count).Val()
|
||||
// 2. 处理数据
|
||||
var resList []SearchInfo
|
||||
for i, s := range list {
|
||||
// 3. 判断当前是否是元素
|
||||
if i%2 == 0 {
|
||||
info := SearchInfo{}
|
||||
_ = json.Unmarshal([]byte(s), &info)
|
||||
info.Model = gorm.Model{}
|
||||
resList = append(resList, info)
|
||||
}
|
||||
}
|
||||
return resList, nextCursor
|
||||
}
|
||||
|
||||
// RemoveAll 删除所有库存数据
|
||||
func RemoveAll() {
|
||||
// 删除redis中当前库存储的所有数据
|
||||
db.Rdb.FlushDB(db.Cxt)
|
||||
// 删除mysql中留存的检索表
|
||||
var s *SearchInfo
|
||||
db.Mdb.Exec(fmt.Sprintf(`drop table if exists %s`, s.TableName()))
|
||||
}
|
||||
|
||||
// ================================= Spider 数据处理(mysql) =================================
|
||||
|
||||
// CreateSearchTable 创建存储检索信息的数据表
|
||||
func CreateSearchTable() {
|
||||
// 1. 判断表中是否存在当前表
|
||||
isExist := db.Mdb.Migrator().HasTable(&SearchInfo{})
|
||||
// 如果不存在则创建表
|
||||
if !isExist {
|
||||
err := db.Mdb.AutoMigrate(&SearchInfo{})
|
||||
if err != nil {
|
||||
log.Println("Create Table SearchInfo Failed: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BatchSave 批量保存影片search信息
|
||||
func BatchSave(list []SearchInfo) {
|
||||
tx := db.Mdb.Begin()
|
||||
// 防止程序异常终止
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
if err := tx.CreateInBatches(list, len(list)).Error; err != nil {
|
||||
// 插入失败则回滚事务, 重新进行插入
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
// 插入成功后输出一下成功信息
|
||||
//log.Println("BatchSave SearchInfo Successful, Count: ", len(list))
|
||||
tx.Commit()
|
||||
}
|
||||
|
||||
// BatchSaveOrUpdate 判断数据库中是否存在对应mid的数据, 如果存在则更新, 否则插入
|
||||
func BatchSaveOrUpdate(list []SearchInfo) {
|
||||
tx := db.Mdb.Begin()
|
||||
// 失败则回滚事务
|
||||
//defer func() {
|
||||
// if r := recover(); r != nil {
|
||||
// tx.Rollback()
|
||||
// }
|
||||
//}()
|
||||
for _, info := range list {
|
||||
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{Time: info.Time, Rank: info.Rank, State: info.State,
|
||||
Remarks: info.Remarks, Score: info.Score, ReleaseDate: info.ReleaseDate}).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
} else {
|
||||
// 执行插入操作
|
||||
if err := tx.Create(&info).Error; err != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 提交事务
|
||||
tx.Commit()
|
||||
}
|
||||
|
||||
// SaveSearchData 添加影片检索信息
|
||||
func SaveSearchData(s SearchInfo) {
|
||||
// 先查询数据库中是否存在对应记录
|
||||
isExist := SearchMovieInfo(s.Mid)
|
||||
// 如果不存在对应记录则 保存当前记录
|
||||
if !isExist {
|
||||
db.Mdb.Create(&s)
|
||||
}
|
||||
}
|
||||
|
||||
// SearchMovieInfo 通过Mid查询符合条件的数据
|
||||
func SearchMovieInfo(mid int64) bool {
|
||||
search := SearchInfo{}
|
||||
db.Mdb.Where("mid", mid).First(&search)
|
||||
// reflect.DeepEqual(a, A{})
|
||||
return !reflect.DeepEqual(search, SearchInfo{})
|
||||
}
|
||||
|
||||
// TunCateSearchTable 截断SearchInfo数据表
|
||||
func TunCateSearchTable() {
|
||||
var searchInfo *SearchInfo
|
||||
err := db.Mdb.Exec(fmt.Sprint("TRUNCATE TABLE ", searchInfo.TableName())).Error
|
||||
if err != nil {
|
||||
log.Println("TRUNCATE TABLE Error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ================================= API 数据接口信息处理 =================================
|
||||
|
||||
// GetMovieListByPid 通过Pid 分类ID 获取对应影片的数据信息
|
||||
func GetMovieListByPid(pid int64, page *Page) []MovieBasicInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("pid", pid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid", pid).Order("year DESC, time DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
// 通过影片ID去redis中获取id对应数据信息
|
||||
var list []MovieBasicInfo
|
||||
for _, v := range s {
|
||||
// 通过key搜索指定的影片信息 , MovieDetail:Cid6:Id15441
|
||||
list = append(list, GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, v.Cid, v.Mid)))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// SearchFilmKeyword 通过关键字搜索库存中满足条件的影片名
|
||||
func SearchFilmKeyword(keyword string, page *Page) []SearchInfo {
|
||||
var searchList []SearchInfo
|
||||
// 1. 先统计搜索满足条件的数据量
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("name LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 2. 获取满足条件的数据
|
||||
db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).
|
||||
Where("name LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Order("year DESC, time DESC").Find(&searchList)
|
||||
return searchList
|
||||
}
|
||||
|
||||
// GetMovieListByCid 通过Cid查找对应的影片分页数据, 不适合GetMovieListByPid 糅合
|
||||
func GetMovieListByCid(cid int64, page *Page) []MovieBasicInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("cid", cid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("cid", cid).Order("year DESC, time DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
// 通过影片ID去redis中获取id对应数据信息
|
||||
var list []MovieBasicInfo
|
||||
for _, v := range s {
|
||||
// 通过key搜索指定的影片信息 , MovieDetail:Cid6:Id15441
|
||||
list = append(list, GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, v.Cid, v.Mid)))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// GetRelateMovieBasicInfo GetRelateMovie 根据SearchInfo获取相关影片
|
||||
func GetRelateMovieBasicInfo(search SearchInfo, page *Page) []MovieBasicInfo {
|
||||
/*
|
||||
根据当前影片信息匹配相关的影片
|
||||
1. 分类Cid,
|
||||
2. 如果影片名称含有第x季 则根据影片名进行模糊匹配
|
||||
3. class_tag 剧情内容匹配, 切分后使用 or 进行匹配
|
||||
4. area 地区
|
||||
5. 语言 Language
|
||||
*/
|
||||
|
||||
// sql 拼接查询条件
|
||||
sql := ""
|
||||
// 优先进行名称相似匹配
|
||||
re := regexp.MustCompile("第.{1,3}季")
|
||||
if re.MatchString(search.Name) {
|
||||
search.Name = re.ReplaceAllString(search.Name, "")
|
||||
sql = fmt.Sprintf(`select * from %s where name LIKE "%%%s%%" union`, search.TableName(), search.Name)
|
||||
}
|
||||
// 执行后续匹配内容
|
||||
//sql = fmt.Sprintf(`%s select * from %s where cid=%d AND area="%s" AND language="%s" AND`, sql, search.TableName(), search.Cid, search.Area, search.Language)
|
||||
|
||||
// 地区限制取消, 过滤掉的影片太多
|
||||
sql = fmt.Sprintf(`%s select * from %s where cid=%d AND language="%s" AND`, sql, search.TableName(), search.Cid, search.Language)
|
||||
if strings.Contains(search.ClassTag, ",") {
|
||||
s := "("
|
||||
for _, t := range strings.Split(search.ClassTag, ",") {
|
||||
s = fmt.Sprintf(`%s class_tag = "%s" OR`, s, t)
|
||||
}
|
||||
sql = fmt.Sprintf("%s %s)", sql, strings.TrimSuffix(s, "OR"))
|
||||
} else {
|
||||
sql = fmt.Sprintf(`%s class_tag = "%s"`, sql, search.ClassTag)
|
||||
}
|
||||
// 条件拼接完成后加上limit参数
|
||||
sql = fmt.Sprintf("(%s) limit %d,%d", sql, page.Current, page.PageSize)
|
||||
// 执行sql
|
||||
list := []SearchInfo{}
|
||||
db.Mdb.Raw(sql).Scan(&list)
|
||||
// 根据list 获取对应的BasicInfo
|
||||
var basicList []MovieBasicInfo
|
||||
for _, s := range list {
|
||||
// 通过key获取对应的影片基本数据
|
||||
basicList = append(basicList, GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)))
|
||||
}
|
||||
|
||||
return basicList
|
||||
}
|
||||
Reference in New Issue
Block a user