This commit is contained in:
mubai
2023-12-23 22:32:52 +08:00
parent d85dbe915c
commit b48e53a637
151 changed files with 12451 additions and 1382 deletions

View File

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

View File

@@ -0,0 +1,201 @@
package collect
import (
"encoding/json"
"encoding/xml"
"fmt"
"log"
"server/config"
"server/plugin/db"
)
/*
视频详情接口序列化 struct
*/
//-------------------------------------------------Json 格式-------------------------------------------------
// FilmDetailLPage 视频详情分页数据
type FilmDetailLPage struct {
Code int `json:"code"` // 响应状态码
Msg string `json:"msg"` // 数据类型
Page any `json:"page"` // 页码
PageCount int `json:"pagecount"` // 总页数
Limit any `json:"limit"` // 每页数据量
Total int `json:"total"` // 总数据量
List []FilmDetail `json:"list"` // 影片详情数据List集合
}
// FilmDetail 视频详情列表
type FilmDetail struct {
VodID int64 `json:"vod_id"` // 影片ID
TypeID int64 `json:"type_id"` // 分类ID
TypeID1 int64 `json:"type_id_1"` // 一级分类ID
GroupID int `json:"group_id"` // 用户组ID
VodName string `json:"vod_name"` // 影片名称
VodSub string `json:"vod_sub"` // 影片别名
VodEn string `json:"vod_en"` // 影片名中文拼音
VodStatus int64 `json:"vod_status"` // 影片状态
VodLetter string `json:"vod_letter"` // 影片名首字母(大写)
VodColor string `json:"vod_color"` // UI展示颜色
VodTag string `json:"vod_tag"` // 索引标签
VodClass string `json:"vod_class"` // 剧情分类标签
VodPic string `json:"vod_pic"` // 影片封面图
VodPicThumb string `json:"vod_pic_thumb"` // 缩略图
VodPicSlide string `json:"vod_pic_slide"` // 幻灯图片
VodPicScreenshot string `json:"vod_pic_screenshot"` // ?截图
VodActor string `json:"vod_actor"` // 演员名
VodDirector string `json:"vod_director"` // 导演
VodWriter string `json:"vod_writer"` // 作者
VodBehind string `json:"vod_behind"` // 幕后
VodBlurb string `json:"vod_blurb"` // 内容简介
VodRemarks string `json:"vod_remarks"` // 更新状态 ( 完结 || 更新值 xx集)
VodPubDate string `json:"vod_pubdate"` // 上映日期
VodTotal int64 `json:"vod_total"` // 总集数
VodSerial string `json:"vod_serial"` // 连载数
VodTv string `json:"vod_tv"` // 上映电视台
VodWeekday string `json:"vod_weekday"` // 节目周期
VodArea string `json:"vod_area"` // 地区
VodLang string `json:"vod_lang"` // 语言
VodYear string `json:"vod_year"` // 年代
VodVersion string `json:"vod_version"` // 画质版本 DVD || HD || 720P
VodState string `json:"vod_state"` // 影片类别 正片 || 花絮 || 预告
VodAuthor string `json:"vod_author"` // 编辑人员
VodJumpUrl string `json:"vod_jumpurl"` // 跳转url
VodTpl string `json:"vod_tpl"` // 独立模板
VodTplPlay string `json:"vod_tpl_play"` // 独立播放页模板
VodTplDown string `json:"vod_tpl_down"` // 独立下载页模板
VodIsEnd int64 `json:"vod_isend"` // 是否完结
VodLock int64 `json:"vod_lock"` // 锁定
VodLevel int64 `json:"vod_level"` // 推荐级别
VodCopyright int64 `json:"vod_copyright"` // 版权
VodPoints int64 `json:"vod_points"` // 积分
VodPointsPlay int64 `json:"vod_points_play"` // 点播付费
VodPointsDown int64 `json:"vod_points_down"` // 下载付费
VodHits int64 `json:"vod_hits"` // 总点击量
VodHitsDay int64 `json:"vod_hits_day"` // 日点击量
VodHitsWeek int64 `json:"vod_hits_week"` // 周点击量
VodHitsMonth int64 `json:"vod_hits_month"` // 月点击量
VodDuration string `json:"vod_duration"` // 时长
VodUp int64 `json:"vod_up"` // 顶数
VodDown int64 `json:"vod_down"` // 踩数
VodScore string `json:"vod_score"` // 平均分
VodScoreAll int64 `json:"vod_score_all"` // 总评分
VodScoreNum int64 `json:"vod_score_num"` // 评分次数
VodTime string `json:"vod_time"` // 更新时间
VodTimeAdd int64 `json:"vod_time_add"` // 添加时间
VodTimeHits int64 `json:"vod_time_hits"` // 点击时间
VodTimeMake int64 `json:"vod_time_make"` // 生成时间
VodTrySee int64 `json:"vod_trysee"` // 试看时长
VodDouBanID int64 `json:"vod_douban_id"` // 豆瓣ID
VodDouBanScore string `json:"vod_douban_score"` // 豆瓣评分
VodReRrl string `json:"vod_reurl"` // 来源地址
VodRelVod string `json:"vod_rel_vod"` // 关联视频ids
VodRelArt string `json:"vod_rel_art"` // 关联文章 ids
VodPwd string `json:"vod_pwd"` // 访问内容密码
VodPwdURL string `json:"vod_pwd_url"` // 访问密码连接
VodPwdPlay string `json:"vod_pwd_play"` // 访问播放页密码
VodPwdPlayURL string `json:"vod_pwd_play_url"` // 获取访问密码连接
VodPwdDown string `json:"vod_pwd_down"` // 访问下载页密码
VodPwdDownURL string `json:"vod_pwd_down_url"` // 获取下载密码连接
VodContent string `json:"vod_content"` // 详细介绍
VodPlayFrom string `json:"vod_play_from"` // 播放组
VodPlayServer string `json:"vod_play_server"` // 播放组服务器
VodPlayNote string `json:"vod_play_note"` // 播放组备注 (分隔符)
VodPlayURL string `json:"vod_play_url"` // 播放地址
VodDownFrom string `json:"vod_down_from"` // 下载组
VodDownServer string `json:"vod_down_server"` // 瞎子服务器组
VodDownNote string `json:"vod_down_note"` // 下载备注 (分隔符)
VodDownURL string `json:"vod_down_url"` // 下载地址
VodPlot int64 `json:"vod_plot"` // 是否包含分级剧情
VodPlotName string `json:"vod_plot_name"` // 分类剧情名称
VodPlotDetail string `json:"vod_plot_detail"` // 分集剧情详情
TypeName string `json:"type_name"` // 分类名称
}
//-------------------------------------------------Xml 格式-------------------------------------------------
type RssD struct {
XMLName xml.Name `xml:"rss"`
Version string `xml:"version,attr"`
List FilmDetailPageX `xml:"list"`
}
type CDATA struct {
Text string `xml:",cdata"`
}
type FilmDetailPageX struct {
XMLName xml.Name `xml:"list"`
Page string `xml:"page,attr"`
PageCount int `xml:"pagecount,attr"`
PageSize string `xml:"pagesize,attr"`
RecordCount int `xml:"recordcount,attr"`
Videos []VideoDetail `xml:"video"`
}
type VideoDetail struct {
XMLName xml.Name `xml:"video"`
Last string `xml:"last"`
ID int64 `xml:"id"`
Tid int64 `xml:"tid"`
Name CDATA `xml:"name"`
Type string `xml:"type"`
Pic string `xml:"pic"`
Lang string `xml:"lang"`
Area string `xml:"area"`
Year string `xml:"year"`
State string `xml:"state"`
Note CDATA `xml:"note"`
Actor CDATA `xml:"actor"`
Director CDATA `xml:"director"`
DL DL `xml:"dl"`
Des CDATA `xml:"des"`
}
type DL struct {
XMLName xml.Name `xml:"dl"`
DD []DD `xml:"dd"`
}
type DD struct {
XMLName xml.Name `xml:"dd"`
Flag string `xml:"flag,attr"`
Value string `xml:",cdata"`
}
//-------------------------------------------------Json 格式-------------------------------------------------
// BatchSaveOriginalDetail 批量保存原始影片详情数据
func BatchSaveOriginalDetail(dl []FilmDetail) {
for _, d := range dl {
SaveOriginalDetail(d)
}
}
// SaveOriginalDetail 保存未处理的完整影片详情信息到redis
func SaveOriginalDetail(fd FilmDetail) {
data, err := json.Marshal(fd)
if err != nil {
log.Println("Json Marshal FilmDetail Error: ", err)
}
if err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.OriginalFilmDetailKey, fd.VodID), data, config.ResourceExpired).Err(); err != nil {
log.Println("Save Original FilmDetail Error: ", err)
}
}
// GetOriginalDetailById 获取原始的影片详情数据
func GetOriginalDetailById(id int64) (FilmDetail, error) {
data, err := db.Rdb.Get(db.Cxt, fmt.Sprintf(config.OriginalFilmDetailKey, id)).Result()
if err != nil {
log.Println("Get OriginalDetail Fail: ", err)
}
var fd = FilmDetail{}
err = json.Unmarshal([]byte(data), &fd)
if err != nil {
log.Println("json.Unmarshal OriginalDetail Fail: ", err)
return fd, err
}
return fd, nil
}

View File

@@ -0,0 +1,109 @@
package collect
import (
"encoding/json"
"encoding/xml"
"server/config"
"server/plugin/db"
)
/*
视频列表接口序列化 struct
*/
//-------------------------------------------------Json 格式-------------------------------------------------
// CommonPage 影视列表接口分页数据结构体
type CommonPage struct {
Code int `json:"code"` // 响应状态码
Msg string `json:"msg"` // 数据类型
Page any `json:"page"` // 页码
PageCount int `json:"pagecount"` // 总页数
Limit any `json:"limit"` // 每页数据量
Total int `json:"total"` // 总数据量
}
// FilmListPage 影视列表接口分页数据结构体
type FilmListPage struct {
Code int `json:"code"` // 响应状态码
Msg string `json:"msg"` // 数据类型
Page any `json:"page"` // 页码
PageCount int `json:"pagecount"` // 总页数
Limit any `json:"limit"` // 每页数据量
Total int `json:"total"` // 总数据量
List []FilmList `json:"list"` // 影片列表数据List集合
Class []FilmClass `json:"class"` // 影片分类信息
}
// FilmList 影视列表单部影片信息结构体
type FilmList struct {
VodID int64 `json:"vod_id"` // 影片ID
VodName string `json:"vod_name"` // 影片名称
TypeID int64 `json:"type_id"` // 分类ID
TypeName string `json:"type_name"` // 分类名称
VodEn string `json:"vod_en"` // 影片名中文拼音
VodTime string `json:"vod_time"` // 更新时间
VodRemarks string `json:"vod_remarks"` // 更新状态
VodPlayFrom string `json:"vod_play_from"` // 播放来源
}
// FilmClass 影视分类信息结构体
type FilmClass struct {
TypeID int64 `json:"type_id"` // 分类ID
TypePid int64 `json:"type_pid"` // 父级ID
TypeName string `json:"type_name"` // 类型名称
}
//-------------------------------------------------Xml 格式-------------------------------------------------
type RssL struct {
XMLName xml.Name `xml:"rss"`
Version string `xml:"version,attr"`
List FilmListPageX `xml:"list"`
ClassXL ClassXL `xml:"class"`
}
type FilmListPageX struct {
XMLName xml.Name `xml:"list"`
Page any `xml:"page,attr"`
PageCount int `xml:"pagecount,attr"`
PageSize any `xml:"pagesize,attr"`
RecordCount int `xml:"recordcount,attr"`
Videos []VideoList `xml:"video"`
}
type VideoList struct {
Last string `xml:"last"`
ID int64 `xml:"id"`
Tid int64 `xml:"tid"`
Name CDATA `xml:"name"`
Type string `xml:"type"`
Dt string `xml:"dt"`
Note CDATA `xml:"note"`
}
type ClassXL struct {
XMLName xml.Name `xml:"class"`
ClassX []ClassX `xml:"ty"`
}
type ClassX struct {
XMLName xml.Name `xml:"ty"`
ID int64 `xml:"id,attr"`
Value string `xml:",chardata"`
}
//-------------------------------------------------redis Func-------------------------------------------------
// SaveFilmClass 保存影片分类列表信息
func SaveFilmClass(list []FilmClass) error {
data, _ := json.Marshal(list)
return db.Rdb.Set(db.Cxt, config.FilmClassKey, data, config.ResourceExpired).Err()
}
// GetFilmClass 获取分类列表信息
func GetFilmClass() []FilmClass {
var l []FilmClass
data := db.Rdb.Get(db.Cxt, config.FilmClassKey).Val()
_ = json.Unmarshal([]byte(data), &l)
return l
}

View File

@@ -1,4 +1,4 @@
package model
package system
import (
"encoding/json"
@@ -12,6 +12,7 @@ type Category struct {
Id int64 `json:"id"` // 分类ID
Pid int64 `json:"pid"` // 父级分类ID
Name string `json:"name"` // 分类名称
Show bool `json:"show"` // 是否展示
}
// CategoryTree 分类信息树形结构
@@ -20,9 +21,12 @@ type CategoryTree struct {
Children []*CategoryTree `json:"children"` // 子分类信息
}
// 影视分类展示树形结构
// SaveCategoryTree 保存影片分类信息
func SaveCategoryTree(tree string) error {
return db.Rdb.Set(db.Cxt, config.CategoryTreeKey, tree, config.CategoryTreeExpired).Err()
func SaveCategoryTree(tree *CategoryTree) error {
data, _ := json.Marshal(tree)
return db.Rdb.Set(db.Cxt, config.CategoryTreeKey, data, config.CategoryTreeExpired).Err()
}
// GetCategoryTree 获取影片分类信息

View File

@@ -0,0 +1,176 @@
package system
import (
"encoding/json"
"errors"
"fmt"
"github.com/redis/go-redis/v9"
"log"
"server/config"
"server/plugin/common/util"
"server/plugin/db"
)
/*
影视采集站点信息
*/
type SourceGrade int
const (
MasterCollect SourceGrade = iota
SlaveCollect
)
type CollectResultModel int
const (
JsonResult CollectResultModel = iota
XmlResult
)
type ResourceType int
func (rt ResourceType) GetActionType() string {
var ac string = ""
switch rt {
case CollectVideo:
ac = "detail"
case CollectArticle:
ac = "article"
case CollectActor:
ac = "actor"
case CollectRole:
ac = "role"
case CollectWebSite:
ac = "web"
default:
ac = "detail"
}
return ac
}
const (
CollectVideo = iota
CollectArticle
CollectActor
CollectRole
CollectWebSite
)
// FilmSource 影视站点信息保存结构体
type FilmSource struct {
Id string `json:"id"` // 唯一ID
Name string `json:"name"` // 采集站点备注名
Uri string `json:"uri"` // 采集链接
ResultModel CollectResultModel `json:"resultModel"` // 接口返回类型, json || xml
Grade SourceGrade `json:"grade"` // 采集站等级 主站点 || 附属站
SyncPictures bool `json:"syncPictures"` // 是否同步图片到服务器
CollectType ResourceType `json:"collectType"` // 采集资源类型
State bool `json:"state"` // 是否启用
}
// SaveCollectSourceList 保存采集站Api列表
func SaveCollectSourceList(list []FilmSource) error {
var zl []redis.Z
for _, v := range list {
m, _ := json.Marshal(v)
zl = append(zl, redis.Z{Score: float64(v.Grade), Member: m})
}
return db.Rdb.ZAdd(db.Cxt, config.FilmSourceListKey, zl...).Err()
}
// GetCollectSourceList 获取采集站API列表
func GetCollectSourceList() []FilmSource {
l, err := db.Rdb.ZRange(db.Cxt, config.FilmSourceListKey, 0, -1).Result()
if err != nil {
log.Println(err)
return nil
}
return getCollectSource(l)
}
// GetCollectSourceListByGrade 返回指定类型的采集Api信息 Master | Slave
func GetCollectSourceListByGrade(grade SourceGrade) []FilmSource {
s := fmt.Sprintf("%d", grade)
zl, err := db.Rdb.ZRangeByScore(db.Cxt, config.FilmSourceListKey, &redis.ZRangeBy{Max: s, Min: s}).Result()
if err != nil {
log.Println(err)
return nil
}
return getCollectSource(zl)
}
// FindCollectSourceById 通过Id标识获取对应的资源站信息
func FindCollectSourceById(id string) *FilmSource {
for _, v := range GetCollectSourceList() {
if v.Id == id {
return &v
}
}
return nil
}
// 将 []string 转化为 []FilmSourceApi
func getCollectSource(sl []string) []FilmSource {
var l []FilmSource
for _, s := range sl {
f := FilmSource{}
_ = json.Unmarshal([]byte(s), &f)
l = append(l, f)
}
return l
}
// DelCollectResource 通过Id删除对应的采集站点信息
func DelCollectResource(id string) {
for _, v := range GetCollectSourceList() {
if v.Id == id {
data, _ := json.Marshal(v)
db.Rdb.ZRem(db.Cxt, config.FilmSourceListKey, data)
}
}
}
// AddCollectSource 添加采集站信息
func AddCollectSource(s FilmSource) error {
for _, v := range GetCollectSourceList() {
if v.Uri == s.Uri {
return errors.New("当前采集站点信息已存在, 请勿重复添加")
}
}
// 生成一个短uuid
s.Id = util.GenerateSalt()
data, _ := json.Marshal(s)
return db.Rdb.ZAddNX(db.Cxt, config.FilmSourceListKey, redis.Z{Score: float64(s.Grade), Member: data}).Err()
}
// UpdateCollectSource 更新采集站信息
func UpdateCollectSource(s FilmSource) error {
for _, v := range GetCollectSourceList() {
if v.Id != s.Id && v.Uri == s.Uri {
return errors.New("当前采集站链接已存在其他站点中, 请勿重复添加")
} else if v.Id == s.Id {
// 删除当前旧的采集信息
DelCollectResource(s.Id)
// 将新的采集信息存入list中
data, _ := json.Marshal(s)
db.Rdb.ZAdd(db.Cxt, config.FilmSourceListKey, redis.Z{Score: float64(s.Grade), Member: data})
}
}
return nil
}
// ClearAllCollectSource 删除所有采集站信息
func ClearAllCollectSource() {
db.Rdb.Del(db.Cxt, config.FilmSourceListKey)
}
// ExistCollectSourceList 查询是否已经存在站点list相关数据
func ExistCollectSourceList() bool {
if db.Rdb.Exists(db.Cxt, config.FilmSourceListKey).Val() == 0 {
return false
}
return true
}

View File

@@ -0,0 +1,70 @@
package system
import (
"encoding/json"
"errors"
"github.com/robfig/cron/v3"
"server/config"
"server/plugin/db"
)
/*
定时任务持久化
*/
// FilmCollectTask 影视采集任务
type FilmCollectTask struct {
Id string `json:"id"` // 唯一标识uid
Ids []string `json:"ids"` // 采集站id列表
Cid cron.EntryID `json:"cid"` // 定时任务Id
Time int `json:"time"` // 采集时长, 最新x小时更新的内容
Spec string `json:"spec"` // 执行周期 cron表达式
Model int `json:"model"` // 任务类型, 0 - 自动更新已启用站点 || 1 - 更新Ids中的资源站数据
State bool `json:"state"` // 状态 开启 | 禁用
Remark string `json:"remark"` // 任务备注信息
}
// SaveFilmTask 保存影视采集任务信息 {EntryId:FilmCollectTask}
func SaveFilmTask(t FilmCollectTask) {
data, _ := json.Marshal(t)
db.Rdb.HSet(db.Cxt, config.FilmCrontabKey, t.Id, data)
}
// GetAllFilmTask 获取所有的任务信息
func GetAllFilmTask() []FilmCollectTask {
var tl []FilmCollectTask
tMap := db.Rdb.HGetAll(db.Cxt, config.FilmCrontabKey).Val()
for _, v := range tMap {
var t = FilmCollectTask{}
_ = json.Unmarshal([]byte(v), &t)
tl = append(tl, t)
}
return tl
}
// GetFilmTaskById 通过Id获取当前任务信息
func GetFilmTaskById(id string) (FilmCollectTask, error) {
var ft = FilmCollectTask{}
// 如果Id对应的task不存在则返回错误信息
if !db.Rdb.HExists(db.Cxt, config.FilmCrontabKey, id).Val() {
return ft, errors.New(" The task does not exist ")
}
data := db.Rdb.HGet(db.Cxt, config.FilmCrontabKey, id).Val()
err := json.Unmarshal([]byte(data), &ft)
return ft, err
}
// UpdateFilmTask 更新定时任务信息(直接覆盖Id对应的定时任务信息) -- 后续待调整
func UpdateFilmTask(t FilmCollectTask) {
SaveFilmTask(t)
}
// DelFilmTask 通过Id删除对应的定时任务信息
func DelFilmTask(id string) {
db.Rdb.HDel(db.Cxt, config.FilmCrontabKey, id)
}
// ExistTask 是否存在定时任务相关信息
func ExistTask() bool {
return db.Rdb.Exists(db.Cxt, config.FilmCrontabKey).Val() == 1
}

View File

@@ -0,0 +1,160 @@
package system
import (
"encoding/json"
"fmt"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
"log"
"regexp"
"server/config"
"server/plugin/common/util"
"server/plugin/db"
)
// Picture 图片信息对象
type Picture struct {
gorm.Model
Link string `json:"link"` // 图片链接
Uid int `json:"uid"` // 上传人ID
RelevanceId int64 `json:"relevanceId"` // 关联资源ID
PicType int `json:"picType"` // 图片类型 (0 影片封面, 1 用户头像)
PicUid string `json:"picUid"` // 图片唯一标识, 通常为文件名
//Size int `json:"size"` // 图片大小
}
// VirtualPicture 采集入站,待同步的图片信息
type VirtualPicture struct {
Id int64 `json:"id"`
Link string `json:"link"`
}
//------------------------------------------------本地图库------------------------------------------------
// TableName 设置图片存储表的表名
func (p *Picture) TableName() string {
return config.PictureTableName
}
// CreatePictureTable 创建图片关联信息存储表
func CreatePictureTable() {
// 如果不存在则创建表 并设置自增ID初始值为10000
if !ExistPictureTable() {
err := db.Mdb.AutoMigrate(&Picture{})
if err != nil {
log.Println("Create Table Picture Failed: ", err)
}
}
}
// ExistPictureTable 是否存在Picture表
func ExistPictureTable() bool {
// 1. 判断表中是否存在当前表
return db.Mdb.Migrator().HasTable(&Picture{})
}
// SaveGallery 保存图片关联信息
func SaveGallery(p Picture) {
db.Mdb.Create(&p)
}
// ExistPictureByRid 查找图片信息是否存在
func ExistPictureByRid(rid int64) bool {
var count int64
db.Mdb.Model(&Picture{}).Where("relevance_id = ?", rid).Count(&count)
return count > 0
}
// GetPictureByRid 通过关联的资源id获取对应的图片信息
func GetPictureByRid(rid int64) Picture {
var p Picture
db.Mdb.Where("relevance_id = ?", rid).First(&p)
return p
}
func GetPicturePage(page *Page) []Picture {
var pl []Picture
query := db.Mdb.Model(&Picture{})
// 获取分页相关参数
GetPage(query, page)
// 获取分页数据
if err := query.Limit(page.PageSize).Offset((page.Current - 1) * page.PageSize).Find(&pl).Error; err != nil {
log.Println(err)
return nil
}
return pl
}
//------------------------------------------------图片同步------------------------------------------------
// SaveVirtualPic 保存待同步的图片信息
func SaveVirtualPic(pl []VirtualPicture) error {
// 保存对应的
var zl []redis.Z
for _, p := range pl {
// 首先查询 Gallery 表中是否存在当前ID对应的图片信息, 如果不存在则保存
if !ExistPictureByRid(p.Id) {
m, _ := json.Marshal(p)
zl = append(zl, redis.Z{Score: float64(p.Id), Member: m})
}
}
return db.Rdb.ZAdd(db.Cxt, config.VirtualPictureKey, zl...).Err()
}
// SyncFilmPicture 同步新采集入栈还未同步的图片
func SyncFilmPicture() {
// 扫描待同步图片的信息, 每次扫描count条
sl, cursor := db.Rdb.ZScan(db.Cxt, config.VirtualPictureKey, 0, "*", config.MaxScanCount).Val()
if len(sl) <= 0 {
return
}
// 获取 VirtualPicture
for i, s := range sl {
if i%2 == 0 {
// 获取图片信息
vp := VirtualPicture{}
_ = json.Unmarshal([]byte(s), &vp)
// 删除已经取出的数据
db.Rdb.ZRem(db.Cxt, config.VirtualPictureKey, []byte(s))
// 将图片同步到服务器
fileName, err := util.SaveOnlineFile(vp.Link, config.FilmPictureUploadDir)
if err != nil {
continue
}
// 完成同步后将图片信息保存到 Gallery 中
SaveGallery(Picture{
Link: fmt.Sprint(config.FilmPictureAccess, fileName),
Uid: config.UserIdInitialVal,
RelevanceId: vp.Id,
PicType: 0,
PicUid: regexp.MustCompile(`\.[^.]+$`).ReplaceAllString(fileName, ""),
})
}
}
// 如果 cursor != 0 则继续递归执行
if cursor > 0 {
SyncFilmPicture()
}
}
// ReplaceDetailPic 将影片详情中的图片地址替换为自己的
func ReplaceDetailPic(d *MovieDetail) {
// 查询影片对应的本地图片信息
if ExistPictureByRid(d.Id) {
// 如果存在关联的本地图片, 则查询对应的图片信息
p := GetPictureByRid(d.Id)
// 替换采集站的图片链接为本地链接
d.Picture = p.Link
}
}
// ReplaceBasicDetailPic 替换影片基本数据中的封面图为本地图片
func ReplaceBasicDetailPic(d *MovieBasicInfo) {
// 查询影片对应的本地图片信息
if ExistPictureByRid(d.Id) {
// 如果存在关联的本地图片, 则查询对应的图片信息
p := GetPictureByRid(d.Id)
// 替换采集站的图片链接为本地链接
d.Picture = p.Link
}
}

View File

@@ -0,0 +1,89 @@
package system
import (
"errors"
"fmt"
"github.com/golang-jwt/jwt/v5"
"log"
"server/config"
"server/plugin/common/util"
"server/plugin/db"
"time"
)
type UserClaims struct {
UserID uint `json:"userID"`
UserName string `json:"userName"`
jwt.RegisteredClaims
}
// GenToken 生成token
func GenToken(userId uint, userName string) (string, error) {
uc := UserClaims{
UserID: userId,
UserName: userName,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: config.Issuer,
Subject: userName,
Audience: jwt.ClaimStrings{"Auth_All"},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(config.AuthTokenExpires * time.Hour)),
NotBefore: jwt.NewNumericDate(time.Now().Add(-10 * time.Second)),
IssuedAt: jwt.NewNumericDate(time.Now()),
ID: util.GenerateSalt(),
},
}
priKey, err := util.ParsePriKeyBytes([]byte(config.PrivateKey))
token, err := jwt.NewWithClaims(jwt.SigningMethodRS256, uc).SignedString(priKey)
return token, err
}
// ParseToken 解析token
func ParseToken(tokenStr string) (*UserClaims, error) {
token, err := jwt.ParseWithClaims(tokenStr, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
pub, err := util.ParsePubKeyBytes([]byte(config.PublicKey))
if err != nil {
return nil, err
}
return pub, nil
})
if err != nil {
if errors.Is(err, jwt.ErrTokenExpired) {
claims, _ := token.Claims.(*UserClaims)
return claims, err
}
return nil, err
}
// 验证token是否有效
if !token.Valid {
return nil, errors.New("token is invalid")
}
// 解析token中的claims内容
claims, ok := token.Claims.(*UserClaims)
if !ok {
return nil, errors.New("invalid claim type error")
}
return claims, err
}
// RefreshToken 刷新 token
// SaveUserToken 将用户登录成功后的token字符串存放到redis中
func SaveUserToken(token string, userId uint) error {
// 设置redis中token的过期时间为 token过期时间后的7天
return db.Rdb.Set(db.Cxt, fmt.Sprintf(config.UserTokenKey, userId), token, (config.AuthTokenExpires+7*24)*time.Hour).Err()
}
// GetUserTokenById 从redis中获取指定userId对应的token
func GetUserTokenById(userId uint) string {
token, err := db.Rdb.Get(db.Cxt, fmt.Sprintf(config.UserTokenKey, userId)).Result()
if err != nil {
log.Println(err)
return ""
}
return token
}
// ClearUserToken 清楚指定id的用户的登录信息
func ClearUserToken(userId uint) error {
return db.Rdb.Del(db.Cxt, fmt.Sprintf(config.UserTokenKey, userId)).Err()
}

View File

@@ -0,0 +1,36 @@
package system
import (
"encoding/json"
"log"
"server/config"
"server/plugin/db"
)
type BasicConfig struct {
SiteName string `json:"siteName"` // 网站名称
Domain string `json:"domain"` // 网站域名
Logo string `json:"logo"` // 网站logo
Keyword string `json:"keyword"` // seo关键字
Describe string `json:"describe"` // 网站描述信息
State bool `json:"state"` // 网站状态 开启 || 关闭
Hint string `json:"hint"` // 网站关闭提示
}
// ------------------------------------------------------ Redis ------------------------------------------------------
// SaveSiteBasic 保存网站基本配置信息
func SaveSiteBasic(c BasicConfig) error {
data, _ := json.Marshal(c)
return db.Rdb.Set(db.Cxt, config.SiteConfigBasic, data, config.ManageConfigExpired).Err()
}
// GetSiteBasic 获取网站基本配置信息
func GetSiteBasic() BasicConfig {
c := BasicConfig{}
data := db.Rdb.Get(db.Cxt, config.SiteConfigBasic).Val()
if err := json.Unmarshal([]byte(data), &c); err != nil {
log.Println("GetSiteBasic Err", err)
}
return c
}

View File

@@ -1,14 +1,12 @@
package model
package system
import (
"encoding/json"
"fmt"
"github.com/redis/go-redis/v9"
"hash/fnv"
"path/filepath"
"regexp"
"server/config"
"server/plugin/common/util"
"server/plugin/db"
"strconv"
"strings"
@@ -93,23 +91,6 @@ type MovieDetail struct {
// ===================================Redis数据交互========================================================
// SaveMoviePic 保存影片图片到服务器
func SaveMoviePic(details ...*MovieDetail) {
for _, d := range details {
// 判断 detail 在redis中是否已经存在
if db.Rdb.Exists(db.Cxt, fmt.Sprintf(config.MovieDetailKey, d.Cid, d.Id)).Val() == 1 {
// 如果已经存在则直接continue
continue
}
// 将影片信息中的pic图片下载保存到resource/images 文件夹下
err := util.SaveOnlineFile(d.Picture, config.ImageDir)
// 如果没有异常则将detail的图片路径替换为本地的保存路径
if err == nil {
d.Picture = fmt.Sprintf("http://127.0.0.1:%s/static/image/%s", config.ListenerPort, filepath.Base(d.Picture))
}
}
}
// SaveDetails 保存影片详情信息到redis中 格式: MovieDetail:Cid?:Id?
func SaveDetails(list []MovieDetail) (err error) {
// 遍历list中的信息
@@ -135,6 +116,27 @@ func SaveDetails(list []MovieDetail) (err error) {
return err
}
// SaveDetail 保存单部影片信息
func SaveDetail(detail MovieDetail) (err error) {
// 序列化影片详情信息
data, _ := json.Marshal(detail)
// 保存影片信息到Redis
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
if err != nil {
return err
}
// 2. 同步保存简略信息到redis中
SaveMovieBasicInfo(detail)
// 转换 detail信息
searchInfo := ConvertSearchInfo(detail)
// 3. 保存 Search tag redis中
// 只存储用于检索对应影片的关键字信息
SaveSearchTag(searchInfo)
// 保存影片检索信息到searchTable
err = SaveSearchInfo(searchInfo)
return err
}
// SaveMovieBasicInfo 摘取影片的详情部分信息转存为影视基本信息
func SaveMovieBasicInfo(detail MovieDetail) {
basicInfo := MovieBasicInfo{
@@ -194,12 +196,6 @@ func BatchSaveSearchInfo(list []MovieDetail) {
}
// 将检索信息存入redis中做一次转存
RdbSaveSearchInfo(infoList)
// 废弃方案, 频繁大量入库容易引起主键冲突, 事务影响速率
// 批量插入时应对已存在数据进行检测, 使用mysql事务进行锁表
//BatchSave(infoList)
// 使用批量添加or更新
//BatchSaveOrUpdate(infoList)
}
// ConvertSearchInfo 将detail信息处理成 searchInfo
@@ -239,6 +235,8 @@ func GetBasicInfoByKey(key string) MovieBasicInfo {
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
basic := MovieBasicInfo{}
_ = json.Unmarshal(data, &basic)
// 执行本地图片匹配
ReplaceBasicDetailPic(&basic)
return basic
}
@@ -248,6 +246,9 @@ func GetDetailByKey(key string) MovieDetail {
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
detail := MovieDetail{}
_ = json.Unmarshal(data, &detail)
// 执行本地图片匹配
ReplaceDetailPic(&detail)
return detail
}
@@ -258,6 +259,9 @@ func GetBasicInfoBySearchInfos(infos ...SearchInfo) []MovieBasicInfo {
data := []byte(db.Rdb.Get(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)).Val())
basic := MovieBasicInfo{}
_ = json.Unmarshal(data, &basic)
// 执行本地图片匹配
ReplaceBasicDetailPic(&basic)
list = append(list, basic)
}
return list

View File

@@ -0,0 +1,76 @@
package system
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
)
/*
对 http response 做简单的封装
*/
const (
SUCCESS = 0
FAILED = -1
)
// Response http返回数据结构体
type Response struct {
Code int `json:"code"` // 状态 ok | failed
Data any `json:"data"` // 数据
Msg string `json:"msg"` // 提示信息
//Count int `json:"count"` // 内容长度
}
// PagingData 分页基本数据通用格式
type PagingData struct {
List []any `json:"list"`
Paging Page `json:"paging"`
}
// Page 分页信息结构体
type Page struct {
PageSize int `json:"pageSize"` // 每页大小
Current int `json:"current"` // 当前页
PageCount int `json:"pageCount"` // 总页数
Total int `json:"total"` // 总记录数
//List []interface{} `json:"list"` // 数据
}
// Result 构建response返回数据结构
func Result(code int, data any, msg string, c *gin.Context) {
c.JSON(http.StatusOK, Response{
Code: code,
Data: data,
Msg: msg,
})
}
// Success 成功响应 数据 + 成功提示
func Success(data any, message string, c *gin.Context) {
Result(SUCCESS, data, message, c)
}
// SuccessOnlyMsg 成功响应, 只返回成功信息
func SuccessOnlyMsg(message string, c *gin.Context) {
Result(SUCCESS, nil, message, c)
}
// Failed 响应失败 只返回错误信息
func Failed(message string, c *gin.Context) {
Result(FAILED, nil, message, c)
}
// FailedWithData 返回错误信息以及必要数据
func FailedWithData(data any, message string, c *gin.Context) {
Result(FAILED, data, message, c)
}
// GetPage 获取分页相关数据
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)
}

View File

@@ -1,4 +1,4 @@
package model
package system
/*
量子资源JSON解析

View File

@@ -1,4 +1,4 @@
package model
package system
import (
"encoding/json"
@@ -12,6 +12,7 @@ import (
"server/config"
"server/plugin/common/param"
"server/plugin/db"
"strconv"
"strings"
"time"
)
@@ -24,7 +25,7 @@ type SearchInfo struct {
Pid int64 `json:"pid"` //上级分类ID
Name string `json:"name"` // 片名
SubTitle string `json:"subTitle"` // 影片子标题
CName string `json:"CName"` // 分类名称
CName string `json:"cName"` // 分类名称
ClassTag string `json:"classTag"` //类型标签
Area string `json:"area"` // 地区
Language string `json:"language"` // 语言
@@ -38,15 +39,6 @@ type SearchInfo struct {
ReleaseStamp int64 `json:"releaseStamp"` //上映时间 时间戳
}
// Page 分页信息结构体
type Page struct {
PageSize int `json:"pageSize"` // 每页大小
Current int `json:"current"` // 当前页
PageCount int `json:"pageCount"` // 总页数
Total int `json:"total"` // 总记录数
//List []interface{} `json:"list"` // 数据
}
// Tag 影片分类标签结构体
type Tag struct {
Name string `json:"name"`
@@ -71,31 +63,31 @@ func RdbSaveSearchInfo(list []SearchInfo) {
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() {
// FilmZero 删除所有库存数据
func FilmZero() {
// 删除redis中当前库存储的所有数据
db.Rdb.FlushDB(db.Cxt)
//db.Rdb.FlushDB(db.Cxt)
db.Rdb.Del(db.Cxt, db.Rdb.Keys(db.Cxt, "MovieBasicInfoKey*").Val()...)
db.Rdb.Del(db.Cxt, db.Rdb.Keys(db.Cxt, "MovieDetail*").Val()...)
db.Rdb.Del(db.Cxt, db.Rdb.Keys(db.Cxt, "MultipleSource*").Val()...)
db.Rdb.Del(db.Cxt, db.Rdb.Keys(db.Cxt, "OriginalResource*").Val()...)
db.Rdb.Del(db.Cxt, db.Rdb.Keys(db.Cxt, "Search*").Val()...)
// 删除mysql中留存的检索表
var s *SearchInfo
//db.Mdb.Exec(fmt.Sprintf(`drop table if exists %s`, s.TableName()))
// 截断数据表 truncate table users
if ExistSearchTable() {
db.Mdb.Exec(fmt.Sprintf(`TRUNCATE table %s`, s.TableName()))
}
}
// ResetSearchTable 重置Search表
func ResetSearchTable() {
// 删除 Search 表
var s *SearchInfo
db.Mdb.Exec(fmt.Sprintf(`drop table if exists %s`, s.TableName()))
// 重新创建 Search 表
CreateSearchTable()
}
// DelMtPlay 清空附加播放源信息
@@ -207,7 +199,10 @@ func HandleSearchTags(preTags string, k string) {
f("、")
default:
// 获取 tag对应的score
if len(preTags) == 0 || preTags == "其它" {
if len(preTags) == 0 {
// 如果没有 tag信息则不进行缓存
//db.Rdb.ZAdd(db.Cxt, k, redis.Z{Score: 0, Member: fmt.Sprintf("%v:%v", "未知", "未知")})
} else if 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, fmt.Sprintf("%v:%v", preTags, preTags)).Val()
@@ -226,10 +221,8 @@ func BatchHandleSearchTag(infos ...SearchInfo) {
// CreateSearchTable 创建存储检索信息的数据表
func CreateSearchTable() {
// 1. 判断表中是否存在当前表
isExist := db.Mdb.Migrator().HasTable(&SearchInfo{})
// 如果不存在则创建表
if !isExist {
if !ExistSearchTable() {
err := db.Mdb.AutoMigrate(&SearchInfo{})
if err != nil {
log.Println("Create Table SearchInfo Failed: ", err)
@@ -237,6 +230,11 @@ func CreateSearchTable() {
}
}
func ExistSearchTable() bool {
// 1. 判断表中是否存在当前表
return db.Mdb.Migrator().HasTable(&SearchInfo{})
}
// AddSearchIndex search表中数据保存完毕后 将常用字段添加索引提高查询效率
func AddSearchIndex() {
var s *SearchInfo
@@ -298,22 +296,38 @@ func BatchSaveOrUpdate(list []SearchInfo) {
tx.Commit()
}
// SaveSearchData 添加影片检索信息
func SaveSearchData(s SearchInfo) {
// SaveSearchInfo 添加影片检索信息
func SaveSearchInfo(s SearchInfo) error {
// 先查询数据库中是否存在对应记录
isExist := SearchMovieInfo(s.Mid)
// 如果不存在对应记录则 保存当前记录
if !isExist {
db.Mdb.Create(&s)
tx := db.Mdb.Begin()
if !ExistSearchInfo(s.Mid) {
// 执行插入操作
if err := tx.Create(&s).Error; err != nil {
tx.Rollback()
return err
}
// 执行添加操作时保存一份tag信息
BatchHandleSearchTag(s)
} else {
// 如果已经存在当前记录则将当前记录进行更新
err := tx.Model(&SearchInfo{}).Where("mid", s.Mid).Updates(SearchInfo{UpdateStamp: s.UpdateStamp, Hits: s.Hits, State: s.State,
Remarks: s.Remarks, Score: s.Score, ReleaseStamp: s.ReleaseStamp}).Error
if err != nil {
tx.Rollback()
return err
}
}
// 提交事务
tx.Commit()
return nil
}
// 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{})
// ExistSearchInfo 通过Mid查询是否存在影片的检索信息
func ExistSearchInfo(mid int64) bool {
var count int64
db.Mdb.Model(&SearchInfo{}).Where("mid", mid).Count(&count)
return count > 0
}
// TunCateSearchTable 截断SearchInfo数据表
@@ -325,12 +339,56 @@ func TunCateSearchTable() {
}
}
// GetPage 获取分页相关数据
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)
// SyncSearchInfo 同步影片检索信息
func SyncSearchInfo(model int) {
switch model {
case 0:
// 重置Search表, (恢复为初始状态, 未添加索引)
ResetSearchTable()
// 批量添加 SearchInfo
SearchInfoToMdb(model)
// 保存完所有 SearchInfo 后添加字段索引
AddSearchIndex()
case 1:
// 批量更新或添加
SearchInfoToMdb(model)
}
}
// SearchInfoToMdb 扫描redis中的检索信息, 并批量存入mysql (model 执行模式 0-清空并保存 || 1-更新)
func SearchInfoToMdb(model int) {
// 1.从redis中批量扫描详情信息
list, cursor := db.Rdb.ZScan(db.Cxt, config.SearchInfoTemp, 0, "*", config.MaxScanCount).Val()
// 如果扫描到的信息为空则直接退出
if len(list) <= 0 {
return
}
// 2. 处理数据
var sl []SearchInfo
for i, s := range list {
// 3. 判断当前是否是元素
if i%2 == 0 {
info := SearchInfo{}
_ = json.Unmarshal([]byte(s), &info)
info.Model = gorm.Model{}
// 获取完则删除元素, 避免重复保存
db.Rdb.ZRem(db.Cxt, config.SearchInfoTemp, []byte(s))
sl = append(sl, info)
}
}
//
switch model {
case 0:
// 批量添加 SearchInfo
BatchSave(sl)
case 1:
// 批量更新或添加
BatchSaveOrUpdate(sl)
}
// 如果 SearchInfoTemp 依然存在数据, 则递归执行
if cursor > 0 {
SearchInfoToMdb(model)
}
}
// ================================= API 数据接口信息处理 =================================
@@ -597,7 +655,7 @@ func GetSearchInfosByTags(st SearchTagsVO, page *Page) []SearchInfo {
// 返回分页参数
GetPage(qw, page)
//
// 查询具体的searchInfo 分页数据
var sl []SearchInfo
if err := qw.Limit(page.PageSize).Offset((page.Current - 1) * page.PageSize).Find(&sl).Error; err != nil {
log.Println(err)
@@ -631,6 +689,76 @@ func GetMovieListBySort(t int, pid int64, page *Page) []MovieBasicInfo {
}
// ================================= Manage 管理后台 =================================
func GetSearchPage(s SearchVo) []SearchInfo {
// 构建 query查询条件
query := db.Mdb.Model(&SearchInfo{})
// 如果参数不为空则追加对应查询条件
if s.Name != "" {
query = query.Where("name LIKE ?", fmt.Sprintf("%%%s%%", s.Name))
}
// 分类ID为负数则默认不追加该条件
if s.Cid > 0 {
query = query.Where("cid = ?", s.Cid)
} else if s.Pid > 0 {
query = query.Where("pid = ?", s.Pid)
}
if s.Plot != "" {
query = query.Where("class_tag LIKE ?", fmt.Sprintf("%%%s%%", s.Plot))
}
if s.Area != "" {
query = query.Where("area = ?", s.Area)
}
if s.Language != "" {
query = query.Where("language = ?", s.Language)
}
if int(s.Year) > time.Now().Year()-12 {
query = query.Where("year = ?", s.Year)
}
switch s.Remarks {
case "完结":
query = query.Where("remarks IN ?", []string{"完结", "HD"})
case "":
default:
query = query.Not(map[string]interface{}{"remarks": []string{"完结", "HD"}})
}
if s.BeginTime > 0 {
query = query.Where("update_stamp >= ? ", s.BeginTime)
}
if s.EndTime > 0 {
query = query.Where("update_stamp <= ? ", s.EndTime)
}
// 返回分页参数
GetPage(query, s.Paging)
// 查询具体的数据
var sl []SearchInfo
if err := query.Limit(s.Paging.PageSize).Offset((s.Paging.Current - 1) * s.Paging.PageSize).Find(&sl).Error; err != nil {
log.Println(err)
return nil
}
return sl
}
// GetSearchOptions 获取全部影片的检索标签信息
func GetSearchOptions(pid int64) map[string]interface{} {
// 整合searchTag相关内容
titles := db.Rdb.HGetAll(db.Cxt, fmt.Sprintf(config.SearchTitle, pid)).Val()
// 处理单一分类的数据格式
tagMap := make(map[string]interface{})
for t, _ := range titles {
switch t {
// 只获取对应几个类型的标签
case "Plot", "Area", "Language", "Year":
tagMap[t] = HandleTagStr(t, GetTagsByTitle(pid, t)...)
default:
}
}
return tagMap
}
// ================================= 接口数据缓存 =================================
// DataCache API请求 数据缓存
@@ -654,3 +782,36 @@ func GetCacheData(key string) map[string]interface{} {
func RemoveCache(key string) {
db.Rdb.Del(db.Cxt, key)
}
// ================================= OpenApi请求处理 =================================
func FindFilmIds(params map[string]string, page *Page) ([]int64, error) {
var ids []int64
query := db.Mdb.Model(&SearchInfo{}).Select("mid")
for k, v := range params {
// 如果 v 为空则直接 continue
if len(v) <= 0 {
continue
}
switch k {
case "t":
if cid, err := strconv.ParseInt(v, 10, 64); err == nil {
query = query.Where("cid = ?", cid)
}
case "wd":
query = query.Where("name like ?", fmt.Sprintf("%%%s%%", v))
case "h":
if h, err := strconv.ParseInt(v, 10, 64); err == nil {
query = query.Where("update_stamp >= ?", time.Now().Unix()-h*3600)
}
}
}
// 返回分页参数
var count int64
query.Count(&count)
page.Total = int(count)
page.PageCount = int(page.Total+page.PageSize-1) / page.PageSize
// 返回满足条件的ids
err := query.Limit(page.PageSize).Offset(page.Current - 1).Order("update_stamp DESC").Find(&ids).Error
return ids, err
}

View File

@@ -0,0 +1,94 @@
package system
import (
"fmt"
"gorm.io/gorm"
"log"
"server/config"
"server/plugin/common/util"
"server/plugin/db"
)
type User struct {
gorm.Model
UserName string `json:"userName"` // 用户名
Password string `json:"password"` // 密码
Salt string `json:"salt"` // 盐值
Email string `json:"email"` // 邮箱
Gender int `json:"gender"` // 性别
NickName string `json:"nickName"` // 昵称
Avatar string `json:"avatar"` // 头像
Status int `json:"status"` // 状态
Reserve1 string `json:"reserve1"` // 预留字段 3
Reserve2 string `json:"reserve2"` // 预留字段 2
Reserve3 string `json:"reserve3"` // 预留字段 1
//LastLongTime time.Time `json:"LastLongTime"` // 最后登录时间
}
// TableName 设置user表的表名
func (u *User) TableName() string {
return config.UserTableName
}
// CreateUserTable 创建存储检索信息的数据表
func CreateUserTable() {
var u = &User{}
// 如果不存在则创建表 并设置自增ID初始值为10000
if !ExistUserTable() {
err := db.Mdb.AutoMigrate(u)
db.Mdb.Exec(fmt.Sprintf("alter table %s auto_Increment=%d", u.TableName(), config.UserIdInitialVal))
if err != nil {
log.Println("Create Table SearchInfo Failed: ", err)
}
}
}
// ExistUserTable 判断表中是否存在User表
func ExistUserTable() bool {
return db.Mdb.Migrator().HasTable(&User{})
}
// InitAdminAccount 初始化admin用户密码
func InitAdminAccount() {
// 先查询是否已经存在admin用户信息, 存在则直接退出
user := GetUserByNameOrEmail("admin")
if user != nil {
return
}
// 不存在管理员用户则进行初始化创建
u := &User{
UserName: "admin",
Password: "admin",
Salt: util.GenerateSalt(),
Email: "administrator@gmail.com",
Gender: 2,
NickName: "Zero",
Avatar: "empty",
Status: 0,
}
u.Password = util.PasswordEncrypt(u.Password, u.Salt)
db.Mdb.Create(u)
}
// GetUserByNameOrEmail 查询 username || email 对应的账户信息
func GetUserByNameOrEmail(userName string) *User {
var u *User
if err := db.Mdb.Where("user_name = ? OR email = ?", userName, userName).First(&u).Error; err != nil {
log.Println(err)
return nil
}
return u
}
func GetUserById(id uint) User {
var user = User{Model: gorm.Model{ID: id}}
db.Mdb.First(&user)
return user
}
// UpdateUserInfo 更新用户信息
func UpdateUserInfo(u User) {
// 值更新允许修改的部分字段, 零值会在更新时被自动忽略
db.Mdb.Model(&u).Updates(User{Password: u.Password, Email: u.Email, NickName: u.NickName, Status: u.Status})
}

View File

@@ -0,0 +1,102 @@
package system
type SearchTagsVO struct {
Pid int64 `json:"pid"`
Cid int64 `json:"cid"`
Plot string `json:"plot"`
Area string `json:"area"`
Language string `json:"language"`
Year int64 `json:"year"`
Sort string `json:"sort"`
}
// FilmCronVo 影视更新任务请求参数
type FilmCronVo struct {
Ids []string `json:"ids"` // 定时任务关联的资源站Id
Time int `json:"time"` // 更新最近几小时内更新的影片
Spec string `json:"spec"` // cron表达式
Model int `json:"model"` // 任务类型, 0 - 自动更新已启用站点 || 1 - 更新Ids中的资源站数据
State bool `json:"state"` // 任务状态 开启 | 关闭
Remark string `json:"remark"` // 备注信息
}
// CronTaskVo 定时任务数据response
type CronTaskVo struct {
FilmCollectTask
PreV string `json:"preV"` // 上次执行时间
Next string `json:"next"` // 下次执行时间
}
// FilmTaskOptions 影视采集任务添加时需要的options
type FilmTaskOptions struct {
Id string `json:"id"`
Name string `json:"name"`
}
// CollectParams 数据采集所需要的参数
type CollectParams struct {
Id string `json:"id"` // 资源站id
Ids []string `json:"ids"` // 资源站id列表
Time int `json:"time"` // 采集时长
Batch bool `json:"batch"` // 是否批量执行
}
// SearchVo 影片信息搜索参数
type SearchVo struct {
Name string `json:"name"` // 影片名
Pid int64 `json:"pid"` // 一级分类ID
Cid int64 `json:"cid"` // 二级分类ID
Plot string `json:"plot"` // 剧情
Area string `json:"area"` // 地区
Language string `json:"language"` // 语言
Year int64 `json:"year"` // 年份
//Score int64 `json:"score"` // 评分
Remarks string `json:"remarks"` // 完结 | 未完结
BeginTime int64 `json:"beginTime"` // 更新时间戳起始值
EndTime int64 `json:"endTime"` // 更新时间戳结束值
Paging *Page `json:"paging"` // 分页参数
}
// FilmDetailVo 添加影片对象
type FilmDetailVo 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
PlayLink string `json:"playLink"` //播放地址url
DownloadLink string `json:"downloadLink"` // 下载url地址
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"` //作者
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 string `json:"addTime"` //资源添加时间戳
DbId int64 `json:"dbId"` //豆瓣id
DbScore string `json:"dbScore"` // 豆瓣评分
Hits int64 `json:"hits"` //影片热度
Content string `json:"content"` //内容简介
}
// UserInfoVo 用户信息返回对象
type UserInfoVo struct {
Id uint `json:"id"`
UserName string `json:"userName"` // 用户名
Email string `json:"email"` // 邮箱
Gender int `json:"gender"` // 性别
NickName string `json:"nickName"` // 昵称
Avatar string `json:"avatar"` // 头像
Status int `json:"status"` // 状态
}