add new features

This commit is contained in:
mubai
2024-10-29 22:30:01 +08:00
parent 7ceb125746
commit d1433e4b5b
23 changed files with 525 additions and 129 deletions

View File

@@ -21,6 +21,7 @@ declare module '@vue/runtime-core' {
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']

View File

@@ -10,10 +10,10 @@
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@element-plus/icons-vue": "^2.3.1",
"@videojs-player/vue": "^1.0.0",
"axios": "^1.3.4",
"element-plus": "^2.4.4",
"element-plus": "^2.8.6",
"video.js": "^8.0.4",
"vue": "^3.4.31"
},

View File

@@ -14,7 +14,7 @@
<span>网站管理</span>
</template>
<el-menu-item index="/manage/system/webSite">站点管理</el-menu-item>
<!--<el-menu-item index="">预留配置</el-menu-item>-->
<el-menu-item index="/manage/system/banners">海报管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index="/manage/collect">
<template #title>

View File

@@ -50,8 +50,8 @@
</a>
<!-- wrap 导航-->
<a @click="visible = true" class="iconfont icon-caidan" style="font-size: 26px"/>
<el-drawer v-model="visible" append-to-body :show-close="false" size="45%" class="warp-drawer">
<a @click="handleDrawer" class="iconfont icon-caidan" style="font-size: 26px"/>
<el-drawer v-model="visible" append-to-body :show-close="false" size="45%" class="warp-drawer" @closed="handleDrawer" >
<template #header="{ close, titleId, titleClass }">
<h2 class="menu-title" :id="titleId" :class="titleClass">{{ data.site.siteName }}</h2>
</template>
@@ -62,7 +62,6 @@
</template>
</div>
</el-drawer>
</div>
<!--弹窗模块,显示按钮对应信息-->
</div>
@@ -78,6 +77,15 @@ import {cookieUtil, COOKIE_KEY_MAP} from "../../utils/cookie";
const visible = ref(false)
const handleDrawer = ()=>{
visible.value = !visible.value
if(visible.value) {
document.body.style.position = 'fixed'
} else {
document.body.style.position = ''
}
}
// 搜索关键字
const keyword = ref<string>('')
@@ -160,9 +168,12 @@ onMounted(() => {
.el-drawer__body {
--el-drawer-padding-primary: 20px;
}
.el-overlay {
max-height: 100vh;
}
.warp-drawer {
background-color: var(--bg-dark) !important;
max-height: 100vh;
}
.el-drawer__header {

View File

@@ -25,6 +25,7 @@ import Film from "../views/manage/film/Film.vue";
import FileUpload from "../views/manage/file/FileUpload.vue";
import FilmAdd from "../views/manage/film/FilmAdd.vue";
import CustomPlay from "../views/index/CustomPlay.vue";
import Banners from "../views/manage/system/Banners.vue";
// 2. 定义一个路由
@@ -52,6 +53,7 @@ const routes = [
{path: 'index', component: ManageIndex},
{path: 'collect/index', component: CollectManage},
{path: 'system/webSite', component: SiteConfig},
{path: 'system/banners', component: Banners},
{path: 'cron/index', component: CronManage},
{path: 'file/upload', component: FileUpload},
{path: 'file/gallery', component: Temp},

View File

@@ -1,169 +1,181 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, system-ui, sans-serif;
line-height: 1.5;
font-weight: 400;
font-family: Inter, Avenir, Helvetica, Arial, system-ui, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
/*公共颜色*/
--bg-light: #ffffff;
/*公共颜色*/
--bg-light: #ffffff;
--bg-dark: #21252b;
--bg-dark: #21252b;
--text-color-primary: #ffffff;
--content-text-color: #888888;
--text-color-primary: #ffffff;
--content-text-color: #888888;
--paging-parmary-color: #9b49e7d6;
--paging-parmary-color-hover: #a574b7;
--paging-parmary-color: #9b49e7d6;
--paging-parmary-color-hover: #a574b7;
--border-gray-color: #0000001f;
--border-gray-color: #0000001f;
--btn-primary-color: #8636cc;
--btn-pink-color: #d942bf;
--btn-bg-linght: #fff;
--btn-primary-color: #8636cc;
--btn-pink-color: #d942bf;
--btn-bg-linght: #fff;
--bg-fill-light: #8d00fb1a;
--bg-fill-light: #8d00fb1a;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
outline: none;
font-weight: 500;
color: #646cff;
text-decoration: inherit;
outline: none;
}
a:hover {
color: #535bf2;
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: none;
outline: none;
}
.card {
padding: 2em;
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
/*通用样式*/
.primary{
color: #99DBF5;
.primary {
color: #99DBF5;
}
.dark {
color: rgba(0,0,0,0.68);
color: rgba(0, 0, 0, 0.68);
}
.light {
color: rgba(255,255,255,0.68);
color: rgba(255, 255, 255, 0.68);
}
.silver{
color: rgba(255,255,255,0.75);
.silver {
color: rgba(255, 255, 255, 0.75);
}
/*滚动条样式*/
@media (min-width: 768px) {
::-webkit-scrollbar {
width: 3px;
}
::-webkit-scrollbar-thumb {
border-radius: 5px;
height: 10px;
background: rgba(255,255,255,0.25);
}
::-webkit-scrollbar {
width: 3px;
}
::-webkit-scrollbar-thumb {
border-radius: 5px;
height: 10px;
background: rgba(255, 255, 255, 0.25);
}
}
/*el-dialog样式*/
@media (min-width: 768px){
.el-dialog__body{
padding: 45px!important;
}
@media (min-width: 768px) {
.el-dialog__body {
padding: 45px !important;
}
}
@media (max-width: 768px){
.el-dialog__body{
padding: 20px!important;
}
.el-dialog__header{
padding: 12px 0 8px 0 !important;
}
@media (max-width: 768px) {
.el-dialog__body {
padding: 20px !important;
}
.el-dialog__header {
padding: 12px 0 8px 0 !important;
}
}
.el-dialog{
border-radius: 8px !important;
background-image: linear-gradient( 135deg, #81FFEF 10%, #F067B4 100%) !important;
}
.el-dialog__header{
border-bottom: 1px solid rgba(0,0,0,0.1);
margin-right: 0!important;
.el-dialog {
border-radius: 8px !important;
background-image: linear-gradient(135deg, #81FFEF 10%, #F067B4 100%) !important;
--el-text-color-primary: #5e1e99b8;
}
.el-dialog__headerbtn{
outline: none!important;
border: none;
.el-dialog__header {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin-right: 0 !important;
}
.el-dialog__headerbtn {
outline: none !important;
border: none;
}
/*分页插件颜色*/
.el-popper {
--el-color-primary: var(--paging-parmary-color);
--el-color-primary: var(--paging-parmary-color);
}

View File

@@ -2,7 +2,7 @@
<el-container>
<el-header>
<!--<Header/>-->
<Header />
<NewHeader />
</el-header>
<el-main>
<router-view></router-view>

View File

@@ -1,6 +1,6 @@
<template>
<div class="container">
<div v-if="false" class="hidden-sm-and-up banner_wrap" @touchstart="touchS" @touchend="touchE" >
<div v-if="true" class="hidden-sm-and-up banner_wrap" @touchstart="touchS" @touchend="touchE" >
<el-carousel v-model="data.banner.current" ref="wrap" :pause-on-hover="false" :interval="5000" trigger="hover" height="200px" arrow="never" >
<el-carousel-item v-for="item in banners" :key="item" >
<el-image style="width: 100%; height: 100%;" :src="item.picture" fit="fill"/>
@@ -8,7 +8,7 @@
</el-carousel-item>
</el-carousel>
</div>
<div v-if="false" class="banner hidden-sm-and-down"
<div v-if="true" class="banner hidden-sm-and-down"
:style="{background:`url(${data.banner.current.picture})`, backgroundRepeat: 'no-repeat', backgroundSize: 'cover'}">
<div class="preview">
<el-carousel @change="carousel" :interval="5000" height="240px" arrow="always">

View File

@@ -67,7 +67,8 @@
<div class="cus_util">
<el-button color="#9b49e7" :icon="CirclePlus" @click="dialogV.addV = true">添加采集站</el-button>
<el-button color="#d942bf" @click="openBatchCollect" :icon="Promotion">一键采集</el-button>
<el-button type="danger" :icon="BellFilled">ReZero</el-button>
<el-button type="danger" :icon="DeleteFilled" @click="dialogV.clear = true" >RemoveAll</el-button>
<el-button type="primary" :icon="BellFilled" @click="dialogV.reCollect = true" >AutoCollect</el-button>
</div>
<!--站点添加弹窗-->
<el-dialog v-model="dialogV.addV" title="添加采集站点">
@@ -191,6 +192,36 @@
</span>
</template>
</el-dialog>
<!--影片删除提示弹窗-->
<el-dialog v-model="dialogV.clear" title="是否清除所有影视数据 ?" width="500">
<el-form :model="form">
<el-form-item label="确认密码" >
<el-input v-model="data.password" type="password" placeholder="请输入账户密码并开确认执行" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogV.clear = false">取消</el-button>
<el-button type="primary" @click="clearFilms">确认执行</el-button>
</div>
</template>
</el-dialog>
<!--Re0 从零开始的自动全量采集-->
<el-dialog v-model="dialogV.reCollect" title="是否清除影片数据并重新采集 ?" width="500">
<el-form :model="form">
<el-form-item label="确认密码" >
<el-input v-model="data.password" type="password" placeholder="请输入账户密码并开确认执行" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogV.reCollect = false">取消</el-button>
<el-button type="primary" @click="reCollectFilm">确认执行</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@@ -200,7 +231,7 @@
import {onMounted, reactive} from "vue";
import {ApiGet, ApiPost} from "../../../utils/request";
import {ElMessage} from "element-plus";
import {Delete, Edit, SwitchButton, CirclePlus, Promotion, BellFilled} from "@element-plus/icons-vue";
import {Delete, Edit, SwitchButton, CirclePlus, Promotion, BellFilled, DeleteFilled} from "@element-plus/icons-vue";
const data = reactive({
siteList: [],
@@ -208,13 +239,16 @@ const data = reactive({
{time: 24, label: '采集今日'},
{time: 24 * 7, label: '采集本周'},
{time: -1, label: '采集全部'},
]
],
password: '',
})
const dialogV = reactive({
addV: false,
editV: false,
batchV:false,
clear: false,
reCollect: false,
})
interface FilmSource {
@@ -271,9 +305,6 @@ const startTask = (row:any)=>{
})
}
// 弹窗图片同步开关限制
const restrict = (t:number)=>{
// t 弹窗类型 0 - add | 1 - edit
@@ -327,7 +358,6 @@ const openEditDialog = (id:string)=>{
dialogV.editV = true
}
// switch 开关
const changeSourceState = (s:any)=>{
ApiPost(`/manage/collect/change`, {id:s.id, state: s.state, syncPictures: s.syncPictures}).then((resp: any) => {
@@ -373,6 +403,7 @@ const cancelDialog = ()=>{
form.add = {name: '', uri: '', resultModel: 0, grade: 1, collectType: 0, syncPictures: false, state: false, interval: 0}
}
// 获取采集列表信息
const getCollectList = ()=>{
ApiGet(`/manage/collect/list`).then((resp: any) => {
if (resp.code === 0) {
@@ -402,6 +433,41 @@ const getCollectList = ()=>{
}
})
}
// 清除影片信息
const clearFilms = ()=>{
if (data.password.length <= 0) {
ElMessage.error({message: '操作失败, 密钥信息缺失'})
return
}
ApiGet(`/manage/spider/clear`, {password: data.password}).then((resp:any)=>{
if (resp.code === 0) {
ElMessage.success({message: resp.msg})
} else {
ElMessage.error({message: resp.msg})
}
dialogV.clear = false
data.password = ''
})
}
// 从零开始重新采集
const reCollectFilm = ()=>{
if (data.password.length <= 0) {
ElMessage.error({message: '操作失败, 密钥信息缺失'})
return
}
ApiGet(`/manage/spider/zero`, {password: data.password}).then((resp:any)=>{
if (resp.code === 0) {
ElMessage.success({message: resp.msg})
} else {
ElMessage.error({message: resp.msg})
}
dialogV.reCollect = false
data.password = ''
})
}
onMounted(() => {
getCollectList()
})
@@ -409,8 +475,7 @@ onMounted(() => {
</script>
<style scoped>
.container {
}
.cus_util {
display: flex;

View File

@@ -0,0 +1,79 @@
<template>
<h2 style="color: #8e48b4">首页横幅管理界面</h2>
<el-table
:data="data.banners" style="width: 100%" border size="default"
:row-class-name="'cus-tr'" table-layout="auto">
<el-table-column prop="name" label="影片名称"/>
<el-table-column prop="collectType" align="center" label="影片类型">
<template #default="scope">
<el-tag type="warning">{{ scope.row.cName }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="collectType" align="center" label="上映年份">
<template #default="scope">
<el-tag type="warning">{{ scope.row.year }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="collectType" align="center" label="影片海报">
<template #default="scope">
<el-image style="width: 120px; height: 80px" :src="scope.row.poster" :preview-src-list="[scope.row.poster]" preview-teleported fit="contain" />
</template>
</el-table-column>
<el-table-column prop="collectType" align="center" label="影片封面">
<template #default="scope">
<el-image style="width: 60px; height: 80px" :src="scope.row.picture" :preview-src-list="[scope.row.picture]" preview-teleported fit="cover" />
</template>
</el-table-column>
<el-table-column prop="collectType" align="center" label="排序">
<template #default="scope">
<el-tag disable-transitions>{{ scope.row.sort }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="resultModel" align="center" label="连载状态">
<template #default="scope">
<el-tag v-if="(scope.row.remarks+'').search('更新') == -1" type="success" >{{ scope.row.remark}}</el-tag>
<el-tag v-else type="primary" >{{ scope.row.remark}}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="success" :icon="SwitchButton" plain circle @click="" />
<el-button type="primary" :icon="Edit" plain circle @click="" />
<el-button type="danger" :icon="Delete" plain circle @click="" />
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import {Delete, Edit, SwitchButton} from "@element-plus/icons-vue";
import {onMounted, reactive} from "vue";
import {ApiGet} from "../../../utils/request";
import {ElMessage} from "element-plus";
const data = reactive({
banners: [],
})
const getBanners = ()=>{
ApiGet(`/manage/banner/list`).then((resp:any)=>{
if(resp.code === 0){
data.banners = resp.data
} else {
ElMessage.error({message: resp.msg})
}
})
}
onMounted(()=>{
// 初始化banners数据
getBanners()
})
</script>
<style scoped>
</style>

View File

@@ -23,8 +23,8 @@ const (
// -------------------------redis key-----------------------------------
const (
// CategoryTreeKey 分类树 key
CategoryTreeKey = "CategoryTree"
CategoryTreeExpired = time.Hour * 24 * 90
CategoryTreeKey = "CategoryTree"
FilmExpired = time.Hour * 24 * 365 * 10
// MovieListInfoKey movies分类列表 key
MovieListInfoKey = "MovieList:Cid%d"
@@ -62,6 +62,8 @@ const (
ManageConfigExpired = time.Hour * 24 * 365 * 10
// SiteConfigBasic 网站参数配置
SiteConfigBasic = "SystemConfig:SiteConfig:Basic"
// BannersKey 轮播组件key 你
BannersKey = "SystemConfig:Banners"
// FilmCrontabKey 定时任务列表信息
FilmCrontabKey = "Cron:Task:Film"

View File

@@ -9,6 +9,7 @@ import (
"server/plugin/SystemInit"
"server/plugin/common/util"
"server/plugin/spider"
"strconv"
)
func ManageIndex(c *gin.Context) {
@@ -235,6 +236,99 @@ func ResetSiteBasic(c *gin.Context) {
system.SuccessOnlyMsg("配置信息重置成功", c)
}
// ------------------------------------------------------ 轮播数据配置 ------------------------------------------------------
// BannerList 获取轮播图数据
func BannerList(c *gin.Context) {
bl := logic.ML.GetBanners()
system.Success(bl, "配置信息重置成功", c)
}
// BannerFind 返回ID对应的横幅信息
func BannerFind(c *gin.Context) {
idStr := c.Query("id")
if idStr == "" {
system.Failed("Banner信息获取失败, ID信息异常", c)
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
system.Failed("删除失败,参数类型异常", c)
return
}
bl := logic.ML.GetBanners()
for _, b := range bl {
if b.Id == id {
system.Success(b, "Banner信息获取成功", c)
return
}
}
system.Failed("Banner信息获取失败", c)
}
// BannerAdd 添加海报数据
func BannerAdd(c *gin.Context) {
var b system.Banner
if err := c.ShouldBindJSON(&b); err != nil {
system.Failed("Banner参数提交异常", c)
return
}
bl := logic.ML.GetBanners()
bl = append(bl, b)
if err := logic.ML.SaveBanners(bl); err != nil {
system.Failed(fmt.Sprintln("Banners信息添加失败,", err), c)
return
}
system.SuccessOnlyMsg("海报信息添加成功", c)
}
// BannerUpdate 更新海报数据
func BannerUpdate(c *gin.Context) {
var banner system.Banner
if err := c.ShouldBindJSON(&banner); err != nil {
system.Failed("Banner参数提交异常", c)
return
}
bl := logic.ML.GetBanners()
for i, b := range bl {
if b.Id == banner.Id {
bl[i] = banner
if err := logic.ML.SaveBanners(bl); err != nil {
system.Failed("海报信息更新失败", c)
} else {
system.SuccessOnlyMsg("海报信息更新成功", c)
return
}
}
}
system.Failed("海报信息更新失败, 未匹配对应Banner信息", c)
}
// BannerDel 删除海报数据
func BannerDel(c *gin.Context) {
idStr := c.Query("id")
if idStr == "" {
system.Failed("Banner信息获取失败, ID信息异常", c)
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
system.Failed("删除失败,参数类型异常", c)
return
}
bl := logic.ML.GetBanners()
for i, b := range bl {
if b.Id == id {
bl = append(bl[:i], bl[i+1:]...)
_ = logic.ML.SaveBanners(bl)
system.SuccessOnlyMsg("海报信息删除成功", c)
return
}
}
system.Failed("海报信息删除失败", c)
}
// ------------------------------------------------------ 参数校验 ------------------------------------------------------
func validFilmSource(fs system.FilmSource) error {
// 资源名称不能为空 且长度不能超过20

View File

@@ -3,6 +3,7 @@ package controller
import (
"fmt"
"github.com/gin-gonic/gin"
"server/config"
"server/logic"
"server/model/system"
"strconv"
@@ -64,13 +65,27 @@ func StarSpider(c *gin.Context) {
system.SuccessOnlyMsg("采集任务已成功开启!!!", c)
}
// ClearAllFilm 删除所有film信息
func ClearAllFilm(c *gin.Context) {
// 清空采集数据进行重新采集前校验输入的密码是否正确
pwd := c.DefaultQuery("password", "")
// 如密码错误则不执行后续操作
if !verifyPassword(c, pwd) {
system.Failed("重置失败, 密钥校验失败!!!", c)
return
}
// 删除已采集的所有影片信息
logic.SL.ClearFilms()
system.SuccessOnlyMsg("影视数据已删除!!!", c)
}
// SpiderReset 重置影视数据, 清空库存, 从零开始
func SpiderReset(c *gin.Context) {
// 清空采集数据进行重新采集前校验输入的密码是否正确
key := c.DefaultQuery("accessKey", "")
pwd := c.DefaultQuery("password", "")
// 如密码错误则不执行后续操作
if len(key) <= 0 || key != "Re0" {
system.Failed("重置失败, 密校验失败!!!", c)
if !verifyPassword(c, pwd) {
system.Failed("重置失败, 密校验失败!!!", c)
return
}
// 前置校验通过则清空采集数据并对已启用站点进行 全量采集
@@ -87,3 +102,17 @@ func CoverFilmClass(c *gin.Context) {
}
system.SuccessOnlyMsg("影视分类信息重置成功, 请稍等片刻后刷新页面", c)
}
// 校验密码有效性
func verifyPassword(c *gin.Context, password string) bool {
// 获取已登录的用户信息
v, ok := c.Get(config.AuthUserClaims)
if !ok {
system.Failed("操作失败,登录信息异常!!!", c)
return false
}
// 从context中获取用户的登录信息
uc := v.(*system.UserClaims)
// 校验密码
return logic.UL.VerifyUserPassword(uc.UserID, password)
}

View File

@@ -16,18 +16,22 @@ func (ml *ManageLogic) GetFilmSourceList() []system.FilmSource {
return system.GetCollectSourceList()
}
// GetFilmSource 获取ID对应的采集源信息
func (ml *ManageLogic) GetFilmSource(id string) *system.FilmSource {
return system.FindCollectSourceById(id)
}
// UpdateFilmSource 更新采集源信息
func (ml *ManageLogic) UpdateFilmSource(s system.FilmSource) error {
return system.UpdateCollectSource(s)
}
// SaveFilmSource 保存采集源信息
func (ml *ManageLogic) SaveFilmSource(s system.FilmSource) error {
return system.AddCollectSource(s)
}
// DelFilmSource 删除采集源信息
func (ml *ManageLogic) DelFilmSource(id string) error {
// 先查找是否存在对应ID的站点信息
s := system.FindCollectSourceById(id)
@@ -51,3 +55,13 @@ func (ml *ManageLogic) GetSiteBasicConfig() system.BasicConfig {
func (ml *ManageLogic) UpdateSiteBasic(c system.BasicConfig) error {
return system.SaveSiteBasic(c)
}
// GetBanners 获取轮播组件信息
func (ml *ManageLogic) GetBanners() system.Banners {
return system.GetBanners()
}
// SaveBanners 保存轮播信息
func (ml *ManageLogic) SaveBanners(bl system.Banners) error {
return system.SaveBanners(bl)
}

View File

@@ -38,6 +38,11 @@ func (sl *SpiderLogic) AutoCollect(time int) {
go spider.AutoCollect(time)
}
// ClearFilms 删除采集的数据信息
func (sl *SpiderLogic) ClearFilms() {
go spider.ClearSpider()
}
// ZeroCollect 数据清除从零开始采集
func (sl *SpiderLogic) ZeroCollect(time int) {
go spider.StarZero(time)

View File

@@ -57,6 +57,7 @@ func (ul *UserLogic) ChangePassword(account, password, newPassword string) error
return nil
}
// GetUserInfo 获取用户基本信息
func (ul *UserLogic) GetUserInfo(id uint) system.UserInfoVo {
// 通过用户ID查询对应的用户信息
u := system.GetUserById(id)
@@ -64,3 +65,11 @@ func (ul *UserLogic) GetUserInfo(id uint) system.UserInfoVo {
var vo = system.UserInfoVo{Id: u.ID, UserName: u.UserName, Email: u.Email, Gender: u.Gender, NickName: u.NickName, Avatar: u.Avatar, Status: u.Status}
return vo
}
// VerifyUserPassword 校验密码
func (ul *UserLogic) VerifyUserPassword(id uint, password string) bool {
// 获取当前登录的用户全部信息
u := system.GetUserById(id)
// 校验密码是否正确
return util.PasswordEncrypt(password, u.Salt) == u.Password
}

View File

@@ -32,6 +32,7 @@ func start() {
// 启动前先执行数据库内容的初始化工作
DefaultDataInit()
SystemInit.BannersInit()
// 开启路由监听
r := router.SetupRouter()
_ = r.Run(fmt.Sprintf(":%s", config.ListenerPort))

View File

@@ -26,7 +26,7 @@ type CategoryTree struct {
// SaveCategoryTree 保存影片分类信息
func SaveCategoryTree(tree *CategoryTree) error {
data, _ := json.Marshal(tree)
return db.Rdb.Set(db.Cxt, config.CategoryTreeKey, data, config.CategoryTreeExpired).Err()
return db.Rdb.Set(db.Cxt, config.CategoryTreeKey, data, config.FilmExpired).Err()
}
// GetCategoryTree 获取影片分类信息

View File

@@ -5,8 +5,10 @@ import (
"log"
"server/config"
"server/plugin/db"
"sort"
)
// BasicConfig 网站基本信息
type BasicConfig struct {
SiteName string `json:"siteName"` // 网站名称
Domain string `json:"domain"` // 网站域名
@@ -17,6 +19,30 @@ type BasicConfig struct {
Hint string `json:"hint"` // 网站关闭提示
}
// Banner 首页横幅信息
type Banner struct {
Id int64 `json:"id"` // 绑定所属影片Id
Name string `json:"name"` // 影片名称
Year int64 `json:"year"` // 上映年份
CName string `json:"cName"` // 分类名称
Poster string `json:"poster"` // 海报图片链接
Picture string `json:"picture"` // 横幅大图链接
Remark string `json:"remark"` // 更新状态描述信息
Sort int64 `json:"sort"` // 排序分值
}
type Banners []Banner
func (bl Banners) Len() int {
return len(bl)
}
func (bl Banners) Less(i, j int) bool {
return bl[i].Sort < bl[j].Sort
}
func (bl Banners) Swap(i, j int) {
bl[i], bl[j] = bl[j], bl[i]
}
// ------------------------------------------------------ Redis ------------------------------------------------------
// SaveSiteBasic 保存网站基本配置信息
@@ -34,3 +60,21 @@ func GetSiteBasic() BasicConfig {
}
return c
}
// GetBanners 获取轮播配置信息
func GetBanners() Banners {
var bl Banners
data := db.Rdb.Get(db.Cxt, config.BannersKey).Val()
if err := json.Unmarshal([]byte(data), &bl); err != nil {
log.Println("Get Banners Error", err)
}
// 通过 sort 对banners进行排序
sort.Sort(bl)
return bl
}
// SaveBanners 保存轮播配置信息
func SaveBanners(bl Banners) error {
data, _ := json.Marshal(bl)
return db.Rdb.Set(db.Cxt, config.BannersKey, data, config.ManageConfigExpired).Err()
}

View File

@@ -99,7 +99,7 @@ func SaveDetails(list []MovieDetail) (err error) {
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()
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
// 2. 同步保存简略信息到redis中
SaveMovieBasicInfo(detail)
// 3. 保存 Search tag redis中
@@ -121,7 +121,7 @@ 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()
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
if err != nil {
return err
}
@@ -156,7 +156,7 @@ func SaveMovieBasicInfo(detail MovieDetail) {
Year: detail.Year,
}
data, _ := json.Marshal(basicInfo)
_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
}
// SaveSitePlayList 仅保存播放url列表信息到当前站点

View File

@@ -20,3 +20,13 @@ func BasicConfigInit() {
}
_ = system.SaveSiteBasic(bc)
}
func BannersInit() {
var bl = system.Banners{
system.Banner{Name: "樱花庄的宠物女孩", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
system.Banner{Name: "从零开始的异世界生活", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
system.Banner{Name: "五等分的花嫁", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
system.Banner{Name: "我的青春恋爱物语果然有问题", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
}
_ = system.SaveBanners(bl)
}

View File

@@ -229,6 +229,11 @@ func AutoCollect(h int) {
}
}
// ClearSpider 删除已采集的影片信息
func ClearSpider() {
system.FilmZero()
}
// StarZero 情况站点内所有影片信息
func StarZero(h int) {
// 首先清除影视信息

View File

@@ -34,14 +34,26 @@ func SetupRouter() *gin.Engine {
manageRoute.Use(middleware.AuthToken())
{
manageRoute.GET(`/index`, controller.ManageIndex)
// 系统相关
sysConfig := manageRoute.Group(`/config`)
{
sysConfig.GET("/basic", controller.SiteBasicConfig)
sysConfig.POST("/basic/update", controller.UpdateSiteBasic)
sysConfig.GET("/basic/reset", controller.ResetSiteBasic)
sysConfig.GET(`/basic`, controller.SiteBasicConfig)
sysConfig.POST(`/basic/update`, controller.UpdateSiteBasic)
sysConfig.GET(`/basic/reset`, controller.ResetSiteBasic)
}
// 轮播相关
banner := manageRoute.Group(`banner`)
{
banner.GET(`/list`, controller.BannerList)
banner.GET(`/find`, controller.BannerFind)
banner.POST(`/add`, controller.BannerAdd)
banner.GET(`/update`, controller.BannerUpdate)
banner.GET(`/del`, controller.BannerDel)
}
// 用户相关
userRoute := manageRoute.Group(`/user`)
{
userRoute.GET(`/info`, controller.UserInfo)
@@ -77,6 +89,7 @@ func SetupRouter() *gin.Engine {
{
spiderRoute.POST(`/start`, controller.StarSpider)
spiderRoute.GET(`/zero`, controller.SpiderReset)
spiderRoute.GET(`/clear`, controller.ClearAllFilm)
spiderRoute.GET(`/class/cover`, controller.CoverFilmClass)
}
// filmManage 影视管理