mirror of
https://github.com/Geniusay/ChopperBot.git
synced 2026-06-01 19:41:02 +08:00
HotGuard插件页面
HeatRecommend插件页面
This commit is contained in:
@@ -31,6 +31,7 @@ public class BarrageEvent {
|
||||
private String date;
|
||||
|
||||
private String fileName;
|
||||
|
||||
private String suffix=".flv";
|
||||
private List<Barrage> barrages;
|
||||
private boolean isSort = false;
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.example.core.bgevnet.bgscore.BarragePoint;
|
||||
import org.example.core.bgevnet.bgscore.BarrageScoreCurvePlugin;
|
||||
import org.example.core.section.SectionRequest;
|
||||
import org.example.core.section.VideoSectionWorkShop;
|
||||
import org.example.init.InitPluginRegister;
|
||||
import org.example.plugin.PluginCheckAndDo;
|
||||
import org.example.plugin.SpringBootPlugin;
|
||||
import org.example.bean.Barrage;
|
||||
@@ -83,6 +84,7 @@ public class BarrageEventCenter extends SpringBootPlugin {
|
||||
return ((BarragePopularRangePlugin) plugin).findRange(points);
|
||||
}, PluginName.BARRAGE_POPULAR_RANGE_PLUGIN, List.class
|
||||
);
|
||||
|
||||
if(popularRanges!=null){
|
||||
PluginCheckAndDo.CheckAndDo(plugin -> {
|
||||
for (PopularRange popularRange : popularRanges) {
|
||||
@@ -100,5 +102,6 @@ public class BarrageEventCenter extends SpringBootPlugin {
|
||||
}
|
||||
//TODO 弹幕标签插件
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public class BarrageUtil {
|
||||
List<Barrage> list = ((JSONArray) barrages).toJavaList(Barrage.class);
|
||||
list = list.stream().sorted(Comparator.comparing(Barrage::getTimeReal)).collect(Collectors.toList());
|
||||
if(list.isEmpty()){
|
||||
return false;
|
||||
JsonFileUtil.writeJsonFile(outputFilePath,Map.of("data",list,"updateTime",TimeUtil.getNowTime_YMDHMS()));
|
||||
}else{
|
||||
long startTime = list.get(0).getTimeReal() + start;
|
||||
long endTime = list.get(0).getTimeReal() + end;
|
||||
|
||||
@@ -15,14 +15,15 @@ import java.util.concurrent.*;
|
||||
/**
|
||||
* ChopperBot系统中专门用来处理异步事件的类
|
||||
*/
|
||||
public class OddJobBoy implements ChopperBotGuardianTask {
|
||||
public class OddJobBoy {
|
||||
|
||||
private static volatile OddJobBoy Instance;
|
||||
|
||||
private BlockingQueue<OddJob> oddjobs;
|
||||
|
||||
private ExecutorService pool;
|
||||
|
||||
private OddJobBoy(){
|
||||
oddjobs = new ArrayBlockingQueue<>(1024);
|
||||
pool = Executors.newCachedThreadPool(new NamedThreadFactory("oddjob"));
|
||||
}
|
||||
|
||||
public static OddJobBoy Boy(){
|
||||
@@ -37,20 +38,6 @@ public class OddJobBoy implements ChopperBotGuardianTask {
|
||||
}
|
||||
|
||||
public void addWork(OddJob job) throws InterruptedException {
|
||||
oddjobs.put(job);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void threadTask() {
|
||||
while(true){
|
||||
try {
|
||||
OddJob job = oddjobs.take();
|
||||
ChopperLogFactory.getLogger(LoggerType.System).info("<OddJobBoy> boy get a odd job:{},Processing...",job);
|
||||
job.doJob();
|
||||
}catch (InterruptedException e){
|
||||
|
||||
}
|
||||
}
|
||||
pool.submit(job::doJob);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public class HotController {
|
||||
@CheckPlugin(needPlugin = {PluginName.HOT_GUARD_PLUGIN})
|
||||
@GetMapping("/hotGuard/guard")
|
||||
public Result hotGuards(){
|
||||
List<Guard> guards = hotModuleService.hotModuleGuardApi().getGuards();
|
||||
List<GuardVO> guards = hotModuleService.hotModuleGuardApi().getGuards();
|
||||
return Result.success(
|
||||
Map.of("list",guards)
|
||||
);
|
||||
@@ -126,10 +126,15 @@ public class HotController {
|
||||
@CheckPlugin(needPlugin = {PluginName.HOT_RECOMMENDATION_PLUGIN})
|
||||
@PostMapping("/hotRecommendation/add")
|
||||
public Result addFollowDogs(@RequestBody FollowDog dog){
|
||||
dog.setId(null);
|
||||
boolean success = hotModuleService.heatRecommendApi().addFollowDog(dog);
|
||||
return Result.success(
|
||||
Map.of("success",success)
|
||||
);
|
||||
if(success){
|
||||
return Result.success(
|
||||
Map.of("add",dog)
|
||||
);
|
||||
}else{
|
||||
return Result.error("403","添加失败");
|
||||
}
|
||||
}
|
||||
|
||||
@CheckPlugin(needPlugin = {PluginName.HOT_RECOMMENDATION_PLUGIN})
|
||||
|
||||
@@ -27,7 +27,6 @@ public class WorldInitMachine extends ModuleInitMachine{
|
||||
@Override
|
||||
public boolean init() {
|
||||
ChopperBotGuardPool.init();
|
||||
OddJobBoy.Boy().guardian();
|
||||
try {
|
||||
initMachines = PluginUtil.getAllModuleInit();
|
||||
return initLogger(()->{
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Genius
|
||||
@@ -22,6 +23,7 @@ public class HeatRecommendApi {
|
||||
HeatRecommendation heatRecommendation;
|
||||
|
||||
public boolean addFollowDog(FollowDog dog){
|
||||
dog.setDogId(UUID.randomUUID().toString());
|
||||
if (followDogService.addFollowDog(dog)) {
|
||||
return heatRecommendation.addFollowDog(dog);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package org.example.api;
|
||||
|
||||
import org.example.bean.GuardVO;
|
||||
import org.example.bean.HotModuleSetting;
|
||||
import org.example.core.guard.Guard;
|
||||
import org.example.core.guard.HotModuleGuard;
|
||||
import org.example.service.HotModuleSettingService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Genius
|
||||
@@ -33,7 +36,11 @@ public class HotModuleGuardApi {
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Guard> getGuards(){
|
||||
return hotModuleGuard.getGuards();
|
||||
public List<GuardVO> getGuards(){
|
||||
return hotModuleGuard.getGuards().stream().map(guard->{
|
||||
GuardVO guardVO = new GuardVO();
|
||||
BeanUtils.copyProperties(guard,guardVO);
|
||||
return guardVO;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
21
chopperbot-hot/src/main/java/org/example/bean/GuardVO.java
Normal file
21
chopperbot-hot/src/main/java/org/example/bean/GuardVO.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package org.example.bean;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author Genius
|
||||
* @date 2023/10/09 16:03
|
||||
**/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GuardVO {
|
||||
private String guardName;
|
||||
private long delayTime;
|
||||
private int failRetryTimes;
|
||||
private LocalDateTime preGuardTime;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.example.log.ResultLogger;
|
||||
import org.example.plugin.PluginCheckAndDo;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -34,11 +35,14 @@ public class Guard<T extends HotModuleLoadTask> implements Runnable, ResultLogge
|
||||
private long delayTime;
|
||||
|
||||
private int failRetryTimes;
|
||||
|
||||
private LocalDateTime preGuardTime;
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
int retryTimes = 0;
|
||||
Object data = task.start();
|
||||
preGuardTime = LocalDateTime.now();
|
||||
if(task.isFinish() == HotModuleLoadTask.FinishFlag.FINISH){
|
||||
successLog();
|
||||
}else{
|
||||
|
||||
@@ -85,7 +85,7 @@ public class HotModuleGuard extends SpringBootPlugin {
|
||||
if(loadTask!=null){
|
||||
addGuard(new Guard(this.logger,groupName,loadTask,
|
||||
hotModuleSetting.getUpdateHotModuleTimes(),
|
||||
hotModuleSetting.getFailRetryTimes()));
|
||||
hotModuleSetting.getFailRetryTimes(),null));
|
||||
}else{
|
||||
this.error(String.format("Unable to listen %s hot module,cause: invalid loadTask!", groupName));
|
||||
return false;
|
||||
@@ -99,7 +99,7 @@ public class HotModuleGuard extends SpringBootPlugin {
|
||||
if(loadTask!=null){
|
||||
addGuard(new Guard(this.logger,groupName,loadTask,
|
||||
hotModuleSetting.getUpdateHotLivesTimes(),
|
||||
hotModuleSetting.getFailRetryTimes()));
|
||||
hotModuleSetting.getFailRetryTimes(),null));
|
||||
}else{
|
||||
this.error(String.format("Unable to listen %s hot live,cause: invalid loadTask!", groupName));
|
||||
return false;
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.example.plugin.SpringBootPlugin;
|
||||
import org.example.service.FollowDogService;
|
||||
import org.example.service.HotModuleSettingService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
@@ -146,25 +147,26 @@ public class HeatRecommendation extends SpringBootPlugin {
|
||||
followDogs.add(dog);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeFollowDog(String platform,String dogId){
|
||||
List<FollowDog> followDogs = platformFollowDogMap.get(platform);
|
||||
if(followDogs!=null){
|
||||
return followDogs.removeIf(dog -> {
|
||||
followDogs.removeIf(dog -> {
|
||||
return dog.getDogId().equals(dogId);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean updateFollowDog(FollowDog dog){
|
||||
List<FollowDog> followDogs = platformFollowDogMap.get(dog.getPlatform());
|
||||
if(followDogs!=null){
|
||||
return followDogs.removeIf(dog1 -> {return dog1.getDogId().equals(dog.getDogId());})&&followDogs.add(dog);
|
||||
followDogs.removeIf(dog1 -> {return dog1.getDogId().equals(dog.getDogId());});
|
||||
followDogs.add(dog);
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean openPlatformFollowDog(String platform,boolean isOpen){
|
||||
@@ -222,12 +224,12 @@ public class HeatRecommendation extends SpringBootPlugin {
|
||||
}
|
||||
|
||||
private List<String> getBanList(String banLiver){
|
||||
if(banLiver==null){
|
||||
return new ArrayList<>();
|
||||
}else{
|
||||
if(StringUtils.hasText(banLiver)){
|
||||
String s = banLiver.substring(1, banLiver.length() - 1);
|
||||
String[] ss = s.split(",");
|
||||
return List.of(ss);
|
||||
}else{
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class FollowDogServiceImpl extends ServiceImpl<FollowDogMapper, FollowDog
|
||||
|
||||
@Override
|
||||
public boolean addFollowDog(FollowDog dog) {
|
||||
return mapper.insert(dog)==1;
|
||||
return save(dog);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,23 +39,25 @@ public class VideoSectionWorkShop extends SpringGuardPlugin {
|
||||
public void start() {
|
||||
try {
|
||||
SectionRequest request = requests.poll(1000, TimeUnit.SECONDS);
|
||||
long videoStartTime = TimeUtil.getTimeNaos(request.getDate());
|
||||
long start = (timeBias(request.getStartTime())-videoStartTime)/1000;
|
||||
start = start<0?0:start;
|
||||
long end = (timeBias(request.getEndTime())-videoStartTime)/1000;
|
||||
if(request!=null){
|
||||
long videoStartTime = TimeUtil.getTimeNaos(request.getDate());
|
||||
long start = (timeBias(request.getStartTime())-videoStartTime)/1000;
|
||||
start = start<0?0:start;
|
||||
long end = (timeBias(request.getEndTime())-videoStartTime)/1000;
|
||||
|
||||
String root = Paths.get(liveRoot,request.getAction(),request.getPlatform()).toString();
|
||||
String startTime = VideoUtil.formatTimeToFFMpeg(start);
|
||||
String endTime = VideoUtil.formatTimeToFFMpeg(end);
|
||||
String videoName = request.getVideoName();
|
||||
String[] split = videoName.split("\\.");
|
||||
String oldPath = Paths.get(root,videoName).toString();
|
||||
String liver = request.getLiver();
|
||||
String date = request.getDate();
|
||||
String newVideoName = FileNameBuilder.buildVideoFileNameNoSuffix(liver, date)+"_section("+ FileUtil.convertTimeToFile(startTime) +"-"+FileUtil.convertTimeToFile(endTime)+")."+split[1];
|
||||
String newPath = Paths.get(root,newVideoName).toString();
|
||||
if (VideoUtil.cutVideoByFFMpeg(oldPath,newPath,startTime,endTime)) {
|
||||
VideoSection videoSection = new VideoSection(newVideoName,request.getTag(),request.getLiver(),request.getPlatform());
|
||||
String root = Paths.get(liveRoot,request.getAction(),request.getPlatform()).toString();
|
||||
String startTime = VideoUtil.formatTimeToFFMpeg(start);
|
||||
String endTime = VideoUtil.formatTimeToFFMpeg(end);
|
||||
String videoName = request.getVideoName();
|
||||
String[] split = videoName.split("\\.");
|
||||
String oldPath = Paths.get(root,videoName).toString();
|
||||
String liver = request.getLiver();
|
||||
String date = request.getDate();
|
||||
String newVideoName = FileNameBuilder.buildVideoFileNameNoSuffix(liver, date)+"_section("+ FileUtil.convertTimeToFile(startTime) +"-"+FileUtil.convertTimeToFile(endTime)+")."+split[1];
|
||||
String newPath = Paths.get(root,newVideoName).toString();
|
||||
if (VideoUtil.cutVideoByFFMpeg(oldPath,newPath,startTime,endTime)) {
|
||||
VideoSection videoSection = new VideoSection(newVideoName,request.getTag(),request.getLiver(),request.getPlatform());
|
||||
}
|
||||
}
|
||||
}catch (InterruptedException e){
|
||||
return;
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.bytedeco.ffmpeg.global.avcodec;
|
||||
import org.bytedeco.javacv.*;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.example.constpool.ConstPool;
|
||||
import org.example.thread.oddjob.OddJobBoy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ws.schild.jave.Encoder;
|
||||
@@ -20,8 +21,10 @@ import ws.schild.jave.MultimediaObject;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
@@ -416,17 +419,44 @@ public class VideoUtil {
|
||||
}
|
||||
|
||||
public static boolean cutVideoByFFMpeg(String inputFilePath,String outputFilePath,String start,String end){
|
||||
long videoTime = getVideoTime(inputFilePath);
|
||||
System.out.println(inputFilePath+"视频时长为:"+videoTime+" "+formatTimeToFFMpeg(videoTime));
|
||||
try {
|
||||
String ffmpegCmd = FFMPEG_PATH + " -i " + "\""+inputFilePath+"\"" + " -ss " + start + " -to " + end + " -c copy " + "\""+outputFilePath+"\"" +" -y";
|
||||
|
||||
System.out.println(ffmpegCmd);
|
||||
// 执行FFmpeg命令
|
||||
Process process = Runtime.getRuntime().exec(ffmpegCmd);
|
||||
|
||||
// 等待命令执行完成
|
||||
int exitCode = process.waitFor();
|
||||
|
||||
OddJobBoy.Boy().addWork(()->{
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
try {
|
||||
while ((in.readLine()) != null) {}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OddJobBoy.Boy().addWork(()->{
|
||||
BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||
try {
|
||||
while ((err.readLine()) != null) {}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
err.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode == 0) {
|
||||
System.out.printf("in:%s 视频分割完成。 out:%s\n",inputFilePath, outputFilePath);
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.example.barrage;
|
||||
|
||||
import org.example.ConsoleApplication;
|
||||
import org.example.constpool.PluginName;
|
||||
import org.example.core.bgevnet.BarrageEvent;
|
||||
import org.example.core.bgevnet.bghot.BarragePopularRangePlugin;
|
||||
import org.example.core.bgevnet.bghot.PopularRange;
|
||||
import org.example.core.bgevnet.bgscore.BarragePoint;
|
||||
import org.example.core.bgevnet.bgscore.BarrageScoreCurvePlugin;
|
||||
import org.example.init.InitPluginRegister;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Genius
|
||||
* @date 2023/10/09 00:27
|
||||
**/
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = ConsoleApplication.class,webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
public class BarragePoupularRangeTest {
|
||||
|
||||
@Test
|
||||
public void Test(){
|
||||
BarrageEvent event = new BarrageEvent("douyu","online",null,null,"即将拥有人鱼线的PDD_2023-09-14 14_37_54.json");
|
||||
List<BarragePoint> points = InitPluginRegister.getPlugin(PluginName.BARRAGE_SCORE_CURVE_PLUGIN, BarrageScoreCurvePlugin.class).generateCurve(event);
|
||||
List<PopularRange> ranges = InitPluginRegister.getPlugin(PluginName.BARRAGE_POPULAR_RANGE_PLUGIN, BarragePopularRangePlugin.class).findRange(points);
|
||||
System.out.println(ranges);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,12 @@
|
||||
"HotRecommendation":true,
|
||||
"BarrageScoreCurve":true,
|
||||
"FileCacheManager":true,
|
||||
"LiverFollower":true,
|
||||
"LiverFollower":false,
|
||||
"TaskMonitor":true,
|
||||
"HotConfig":true,
|
||||
"TaskCenter":true,
|
||||
"CreeperConfig":true
|
||||
}
|
||||
},
|
||||
"updateTime":"2023-10-07 23:05:43"
|
||||
"updateTime":"2023-10-09 22:41:45"
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import request from '@/utils/request';
|
||||
import {FollowDog} from "@/views/app/hot/heat_recommend/FollowDogTypes";
|
||||
|
||||
export function openFollowDog(platform:string,isOpen:boolean) {
|
||||
return request({
|
||||
@@ -10,3 +11,29 @@ export function openFollowDog(platform:string,isOpen:boolean) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getFollowDogs(){
|
||||
return request({
|
||||
url: '/hot/hotRecommendation/list',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function addFollowDog(dog:FollowDog){
|
||||
return request.post("/hot/hotRecommendation/add",dog)
|
||||
}
|
||||
|
||||
export function updateFollowDog(dog:FollowDog){
|
||||
return request.post("/hot/hotRecommendation/update",dog)
|
||||
}
|
||||
|
||||
export function deleteFollowDog(dogId:string,platform:string){
|
||||
return request({
|
||||
url: '/hot/hotRecommendation/delete',
|
||||
method: 'get',
|
||||
params: {
|
||||
dogId,
|
||||
platform
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export function updateHotGuardSetting(setting:Setting) {
|
||||
return request.post('/hot/hotGuard/update',setting)
|
||||
}
|
||||
|
||||
export function guards() {
|
||||
export function getGuards() {
|
||||
return request({
|
||||
url: '/hot/hotGuard/guard',
|
||||
method: 'get',
|
||||
|
||||
@@ -27,7 +27,7 @@ export default [
|
||||
icon:"mdi mdi-bird",
|
||||
key:"menu.heat_recommend",
|
||||
text:"Heat Recommend",
|
||||
link: "/apps/heat_recommend"
|
||||
link: "/apps/heatRecommend"
|
||||
},
|
||||
{
|
||||
icon:"mdi mdi-star-face",
|
||||
|
||||
@@ -3,6 +3,7 @@ import todoRoutes from "@/views/app/todo/todoRoutes";
|
||||
import emailRoutes from "@/views/app/email/emailRoutes";
|
||||
import chatRoutes from "@/views/app/chat/chatRoutes";
|
||||
import taskRoutes from "@/views/app/taskcenter/taskRoutes";
|
||||
import hotGuardRoutes from "@/views/app/hot/hot_guard/hotGuardRoutes";
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -69,6 +70,7 @@ export default [
|
||||
import(
|
||||
/* webpackChunkName: "utility-board" */ "@/views/app/hot/hot_guard/HotGuardView.vue"
|
||||
),
|
||||
children: [...hotGuardRoutes],
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "Hot Guard",
|
||||
@@ -76,6 +78,20 @@ export default [
|
||||
category: "APP",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/apps/heatRecommend",
|
||||
name: "app-heat-recommend",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "utility-board" */ "@/views/app/hot/heat_recommend/HeatRecommendationView.vue"
|
||||
),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "Heat Recommend",
|
||||
layout: "ui",
|
||||
category: "APP",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/apps/email",
|
||||
meta: {
|
||||
|
||||
@@ -6,3 +6,18 @@ export const nanosToHMS_CN = (milliseconds: number) => {
|
||||
|
||||
return `${hours}小时${minutes}分钟${seconds}秒`;
|
||||
}
|
||||
|
||||
|
||||
export function isoStrToNormal(isoString: string): string {
|
||||
const date = new Date(isoString);
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||
const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface FollowDog{
|
||||
id:number;
|
||||
dogId:string;
|
||||
platform:string;
|
||||
moduleName:string;
|
||||
top:number;
|
||||
banLiver:string;
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
<template>
|
||||
<!-- board column -->
|
||||
<v-row style="min-width: 1000px">
|
||||
<v-col
|
||||
cols="4"
|
||||
v-for="column in columns"
|
||||
:key="column.platform"
|
||||
class="pa-3 flex-1"
|
||||
>
|
||||
<div class="d-flex align-center">
|
||||
<h5 class="font-weight-bold">{{ column.platform }}</h5>
|
||||
<v-spacer></v-spacer>
|
||||
<!-- add new card button -->
|
||||
<v-btn
|
||||
variant="text"
|
||||
rounded
|
||||
icon="mdi-plus"
|
||||
size="small"
|
||||
color="primary"
|
||||
class="mr-n3"
|
||||
@click="column.isAddVisible = !column.isAddVisible"
|
||||
>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<!-- add new card form -->
|
||||
<v-card v-show="column.isAddVisible" class="pa-5 my-2">
|
||||
<v-text-field
|
||||
color="primary"
|
||||
v-model="column.platform"
|
||||
label="平台"
|
||||
variant="underlined"
|
||||
hideDetails
|
||||
disabled
|
||||
placeholder="Input title for this card"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
color="primary"
|
||||
v-model="column.addCard.moduleName"
|
||||
label="类型"
|
||||
variant="underlined"
|
||||
hideDetails
|
||||
placeholder="输入你要推荐的直播类型(例如:英雄联盟,以平台类型为基准)"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
color="primary"
|
||||
v-model="column.addCard.top"
|
||||
label="Top"
|
||||
variant="underlined"
|
||||
hideDetails
|
||||
placeholder="输入该推荐该类型的前几个直播"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
color="primary"
|
||||
v-model="column.addCard.banLiver"
|
||||
label="Ban"
|
||||
variant="underlined"
|
||||
hideDetails
|
||||
placeholder="输入需要ban掉的主播,支持正则表达式"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<div class="mt-3 d-flex flex-md-row flex-column">
|
||||
<v-btn
|
||||
class="flex-1 ma-1"
|
||||
size="small"
|
||||
@click="column.isAddVisible = !column.isAddVisible"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn
|
||||
class="flex-1 ma-1"
|
||||
size="small"
|
||||
color="primary"
|
||||
@click="addCard(column)"
|
||||
>Add</v-btn
|
||||
>
|
||||
</div>
|
||||
</v-card>
|
||||
|
||||
<!-- draggable cards -->
|
||||
<vue-draggable
|
||||
v-model="column.cards"
|
||||
v-bind="dragOptions"
|
||||
class="list-group"
|
||||
@change="column.callback"
|
||||
itemKey="id"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<follow-dog-card
|
||||
:key="index"
|
||||
:card="element"
|
||||
class="board-item my-2 pa-2"
|
||||
@edit="showEdit(element)"
|
||||
@delete="showDelete(element)"
|
||||
/>
|
||||
</template>
|
||||
</vue-draggable>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!-- edit card dialog -->
|
||||
<v-dialog persistent v-model="editDialog" width="600">
|
||||
<v-card>
|
||||
<v-card-title class="pa-4 d-flex align-center">
|
||||
<span class="flex-1">Edit FollowDog 🐶</span>
|
||||
<v-btn
|
||||
variant="text"
|
||||
rounded
|
||||
icon="mdi-close"
|
||||
size="small"
|
||||
color="primary"
|
||||
class="mr-n3"
|
||||
@click="editDialog = false"
|
||||
>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<div class="pa-4">
|
||||
<v-text-field
|
||||
class="py-2 px-1"
|
||||
color="primary"
|
||||
v-model="cardToEdit.platform"
|
||||
label="平台"
|
||||
variant="plain"
|
||||
hideDetails
|
||||
placeholder="输入平台"
|
||||
autofocus
|
||||
disabled
|
||||
></v-text-field>
|
||||
<v-divider></v-divider>
|
||||
<v-text-field
|
||||
class="py-2 px-1"
|
||||
color="primary"
|
||||
v-model="cardToEdit.moduleName"
|
||||
label="类型"
|
||||
variant="plain"
|
||||
hideDetails
|
||||
placeholder="输入你要推荐的直播类型(例如:英雄联盟,以平台类型为基准)"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<v-divider></v-divider>
|
||||
<v-text-field
|
||||
class="py-2 px-1"
|
||||
color="primary"
|
||||
v-model="cardToEdit.top"
|
||||
label="Top"
|
||||
variant="plain"
|
||||
hideDetails
|
||||
placeholder="输入该推荐该类型的前几个直播"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<v-divider></v-divider>
|
||||
<v-text-field
|
||||
class="py-2 px-1"
|
||||
color="primary"
|
||||
v-model="cardToEdit.banLiver"
|
||||
label="Ban"
|
||||
variant="plain"
|
||||
hideDetails
|
||||
placeholder="输入需要ban掉的主播,支持正则表达式"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
</div>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions class="pa-4">
|
||||
<v-btn variant="outlined" @click="editDialog = false">Cancel</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="flat" color="primary" @click="saveCard">Save</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- delete card dialog -->
|
||||
<v-dialog v-model="deleteDialog" max-width="600">
|
||||
<v-card>
|
||||
<v-card-title class="text-headline">Delete</v-card-title>
|
||||
<v-card-text>确定要删除{{cardToDelete.dogId}}吗?</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="plain" color="primary" @click="deleteDialog = false"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn variant="flat" color="error" @click="deleteCard()">Delete</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import VueDraggable from "vuedraggable";
|
||||
import FollowDogCard from "@/views/app/hot/heat_recommend/compoments/FollowDogCard.vue";
|
||||
import {getAllConfigModule,getAllConfigFiles} from "@/api/FileController";
|
||||
import {addFollowDog, deleteFollowDog, getFollowDogs, updateFollowDog} from "@/api/hot/heatRecommendApi";
|
||||
import {useHotRecommendStore} from "@/views/app/hot/heat_recommend/heatRecommendationStore";
|
||||
import {FollowDog} from "@/views/app/hot/heat_recommend/FollowDogTypes";
|
||||
import {useSnackbarStore} from "@/stores/snackbarStore";
|
||||
const hotRecommendStore = useHotRecommendStore()
|
||||
const snackbarStore = useSnackbarStore();
|
||||
const dragOptions = computed(() => {
|
||||
return {
|
||||
animation: 200,
|
||||
group: "loadTask",
|
||||
disabled: false,
|
||||
ghostClass: "ghost",
|
||||
};
|
||||
});
|
||||
|
||||
const platforms = hotRecommendStore.platforms
|
||||
|
||||
const default_card = ref<FollowDog>({
|
||||
id:0,
|
||||
dogId:'',
|
||||
platform: '',
|
||||
moduleName: 'all',
|
||||
top: 5,
|
||||
banLiver:'',
|
||||
})
|
||||
|
||||
type Column = {
|
||||
platform: string,
|
||||
cards: FollowDog[],
|
||||
isAddVisible: false,
|
||||
callback: Function,
|
||||
addCard: FollowDog
|
||||
}
|
||||
|
||||
const columns = ref<Column[]>([]);
|
||||
const cards = ref<FollowDog[]>([]);
|
||||
const deleteDialog = ref(false);
|
||||
const cardToDelete = ref<FollowDog>();
|
||||
const cardToEdit = ref<FollowDog>();
|
||||
const title = ref("");
|
||||
const description = ref("");
|
||||
const editDialog = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
await initFollowDog();
|
||||
initColumns();
|
||||
cards.value = hotRecommendStore.followDogs;
|
||||
parseCards(hotRecommendStore.followDogs);
|
||||
});
|
||||
// Init 模块类型
|
||||
const initFollowDog = async()=>{
|
||||
await getFollowDogs().then(res=>{
|
||||
hotRecommendStore.followDogs = res.data['list'];
|
||||
})
|
||||
}
|
||||
// Init 配置文件
|
||||
|
||||
|
||||
const initColumns = () => {
|
||||
platforms.forEach((state, index) => {
|
||||
columns.value.push({
|
||||
platform: state,
|
||||
cards: [],
|
||||
isAddVisible: false,
|
||||
callback: (e) => changeState(e, index),
|
||||
addCard: Object.assign({},default_card.value),
|
||||
} );
|
||||
});
|
||||
};
|
||||
|
||||
const parseCards = (cards) => {
|
||||
if (!cards) return columns.value.map((column) => (column.cards = []));
|
||||
columns.value.forEach((column) => {
|
||||
column.cards = cards
|
||||
.filter((card) => card.platform === column.platform)
|
||||
.sort((a, b) => (a.order < b.order ? -1 : 0));
|
||||
});
|
||||
};
|
||||
|
||||
// Add
|
||||
const addCard = async (column) => {
|
||||
if (!column.addCard) return;
|
||||
column.addCard.platform = column.platform
|
||||
await addFollowDog(column.addCard).then(res=>{
|
||||
if(res.code==='200'){
|
||||
column.cards.push(res.data["add"])
|
||||
hotRecommendStore.addFollowDog(res.data["add"])
|
||||
snackbarStore.showSuccessMessage("添加成功")
|
||||
}else{
|
||||
snackbarStore.showErrorMessage("添加失败")
|
||||
}
|
||||
})
|
||||
column.addCard = Object.assign({},default_card.value)
|
||||
column.isAddVisible = false
|
||||
};
|
||||
|
||||
const changeState = async (e, colIndex) => {
|
||||
if (e.added || e.moved) {
|
||||
const column = columns.value[colIndex];
|
||||
const state = column.platform;
|
||||
for (let i = 0; i < column.cards.length; i++) {
|
||||
column.cards[i].order = i;
|
||||
if(column.cards[i].platform!=state){
|
||||
column.cards[i].platform = state;
|
||||
await updateFollowDog(column.cards[i]).then(res=>{
|
||||
if(res?.data['success']){
|
||||
hotRecommendStore.updateFollowDog(column.cards[i])
|
||||
snackbarStore.showSuccessMessage("更新成功")
|
||||
}else{
|
||||
snackbarStore.showErrorMessage("更新失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Edit
|
||||
|
||||
const showEdit = (card) => {
|
||||
cardToEdit.value = Object.assign({},card);
|
||||
editDialog.value = true;
|
||||
};
|
||||
|
||||
const saveCard = async () => {
|
||||
if(cardToEdit.value!=null){
|
||||
await updateFollowDog(cardToEdit.value).then(res=>{
|
||||
if(res?.data['success']){
|
||||
const column = columns.value.find(column=>column.platform===cardToEdit.value.platform)
|
||||
if(column!=null){
|
||||
const old = column.cards.find(dog=>dog.dogId===cardToEdit.value.dogId)
|
||||
column.cards.splice(column.cards.indexOf(old),1,cardToEdit.value)
|
||||
}
|
||||
hotRecommendStore.updateFollowDog(cardToEdit.value)
|
||||
snackbarStore.showSuccessMessage("更新成功")
|
||||
}else{
|
||||
snackbarStore.showErrorMessage("更新失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
editDialog.value = false;
|
||||
};
|
||||
|
||||
// Delete
|
||||
|
||||
const showDelete = (card) => {
|
||||
cardToDelete.value = card;
|
||||
deleteDialog.value = true;
|
||||
};
|
||||
|
||||
const deleteCard = async () => {
|
||||
deleteDialog.value = false;
|
||||
await deleteFollowDog(cardToDelete.value.dogId,cardToDelete.value.platform).then(res=>{
|
||||
if(res?.data['success']){
|
||||
hotRecommendStore.deleteFollowDog(cardToDelete.value)
|
||||
columns.value.forEach((column) => {
|
||||
if(column.platform===cardToDelete.value.platform){
|
||||
column.cards = column.cards
|
||||
.filter((card) => cardToDelete.value.dogId !== card.dogId)
|
||||
}
|
||||
});
|
||||
snackbarStore.showSuccessMessage("删除成功")
|
||||
}else{
|
||||
snackbarStore.showErrorMessage("删除失败")
|
||||
}
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
watch(hotRecommendStore.followDogs, (newCards) => {
|
||||
parseCards(newCards);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ghost {
|
||||
opacity: 0.5;
|
||||
background: #c8ebfb;
|
||||
}
|
||||
|
||||
.board-item {
|
||||
transition: transform 0.2s;
|
||||
&:hover {
|
||||
transition: transform 0.2s;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
}
|
||||
.list-group {
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<v-card @click="$emit('edit')" class="pa-5 mt-4 card-shadow">
|
||||
<div class="d-flex align-start font-weight-bold text-title">
|
||||
<span class="flex-1">🐶:{{ props.card.platform }}-{{ props.card.moduleName }}</span>
|
||||
<v-menu location="bottom end" transition="slide-x-transition">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
size="small"
|
||||
variant="text"
|
||||
icon="mdi-dots-vertical"
|
||||
rounded
|
||||
color="primary"
|
||||
class="my-n2"
|
||||
></v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="$emit('edit')">
|
||||
<v-list-item-title class="d-inline-flex align-center">
|
||||
<Icon
|
||||
icon="flat-color-icons:edit-image"
|
||||
:rotate="2"
|
||||
:horizontalFlip="true"
|
||||
:verticalFlip="true"
|
||||
class="mr-1"
|
||||
/>
|
||||
<span> Edit</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="$emit('delete')">
|
||||
<v-list-item-title class="d-inline-flex align-center">
|
||||
<Icon
|
||||
icon="flat-color-icons:full-trash"
|
||||
:rotate="2"
|
||||
:horizontalFlip="true"
|
||||
:verticalFlip="true"
|
||||
:inline="true"
|
||||
class="mr-1"
|
||||
/>
|
||||
Delete</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
<div class="text-content">平台🌟: {{ props.card.platform }}</div>
|
||||
<div class="text-content">类型🎪: {{ props.card.moduleName }}</div>
|
||||
<div class="text-content">Top🏆: {{ props.card.top }}</div>
|
||||
<div class="text-content">Ban🚫:{{ !props.card.banLiver?"暂无":props.card.banLiver }}</div>
|
||||
<div class="text-content">dogId🔖: {{ props.card.dogId }}</div>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue";
|
||||
import {FollowDog} from "@/views/app/hot/heat_recommend/FollowDogTypes";
|
||||
const props = defineProps<{
|
||||
// Card content to display
|
||||
card: FollowDog
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-shadow {
|
||||
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
import { defineStore } from "pinia";
|
||||
import {useSnackbarStore} from "@/stores/snackbarStore";
|
||||
import {FollowDog} from "@/views/app/hot/heat_recommend/FollowDogTypes";
|
||||
|
||||
|
||||
export const useHotRecommendStore = defineStore({
|
||||
id: "hotRecommend",
|
||||
state:()=>({
|
||||
followDogs: ref<FollowDog[]>([]),
|
||||
platforms: ['douyu','huya','bilibili','douyin','tiktok','twitch'],
|
||||
|
||||
}),
|
||||
|
||||
getters:{
|
||||
getFollowDogs(){
|
||||
return this.followDogs
|
||||
}
|
||||
},
|
||||
|
||||
actions:{
|
||||
deleteFollowDog(dog:FollowDog){
|
||||
this.followDogs = this.followDogs.filter(oldDog=>oldDog.dogId!==dog.dogId)
|
||||
},
|
||||
addFollowDog(dog:FollowDog){
|
||||
this.followDogs.push(dog)
|
||||
},
|
||||
updateFollowDog(dog:FollowDog){
|
||||
let old = this.followDogs.find(oldDog=>dog.dogId===oldDog.dogId)
|
||||
this.followDogs.splice(this.followDogs.indexOf(old), 1, dog);
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,92 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import {getHotGuardSetting} from "@/api/hot/hotGuardApi";
|
||||
import {openFollowDog} from "@/api/hot/heatRecommendApi";
|
||||
import router from "@/router";
|
||||
import {useHotGuardStore} from "@/views/app/hot/hot_guard/hotGuardStore";
|
||||
import {nanosToHMS_CN} from "@/utils/timeUtils";
|
||||
import {Setting} from "@/views/app/hot/hot_guard/hotSettingTypes";
|
||||
import {useSnackbarStore} from "@/stores/snackbarStore";
|
||||
const hotGuardStore = useHotGuardStore();
|
||||
|
||||
const hotGuardStore = useHotGuardStore()
|
||||
const snackbarStore = useSnackbarStore();
|
||||
const currentLabel = ref<string>("设置")
|
||||
|
||||
onMounted(async()=>{
|
||||
await getHotGuardSetting().then(res=>{
|
||||
hotGuardStore.settings = res.data['list'].map((setting: any)=>setting as Setting)
|
||||
})
|
||||
})
|
||||
const timeRules = [
|
||||
(v)=> !!v || "时间必须存在",
|
||||
(v)=> v > 0 || "时间必须大于0",
|
||||
(v)=> v >= 1000 || "时间必须大于1s",
|
||||
]
|
||||
if (router.currentRoute.value.path === '/apps/hotGuard/guards') {
|
||||
// 当前路由路径是/apps/hotGuard/guards
|
||||
currentLabel.value = '监控守卫'
|
||||
} else {
|
||||
// 当前路由路径不是/apps/hotGuard/guards
|
||||
currentLabel.value = '设置'
|
||||
}
|
||||
|
||||
const dialog = ref(false);
|
||||
const search = ref("");
|
||||
const refForm = ref<Setting>();
|
||||
const editedItem = ref<Setting>();
|
||||
const editedIndex = ref(-1);
|
||||
const defaultItem = ref<Setting>({
|
||||
id: 0,
|
||||
platform:"",
|
||||
failRetryTimes:0,
|
||||
enableHotModule:false,
|
||||
enableHotLive:false,
|
||||
followDogEnable:false,
|
||||
updateHotModuleTimes:0,
|
||||
updateHotLivesTimes:0
|
||||
watch(currentLabel, (newLabel, oldLabel) => {
|
||||
if(newLabel==='监控守卫'){
|
||||
router.push('/apps/hotGuard/guards')
|
||||
}else if(newLabel==='设置'){
|
||||
router.push('/apps/hotGuard/settings')
|
||||
}
|
||||
});
|
||||
|
||||
//Methods
|
||||
|
||||
function saveBtnDisable(item: any){
|
||||
const setting = item as Setting
|
||||
return timeRules.some((rule)=>typeof rule(setting.updateHotModuleTimes) === "string")||
|
||||
timeRules.some((rule)=>typeof rule(setting.updateHotLivesTimes) === "string")
|
||||
|
||||
}
|
||||
|
||||
function editItem(item: Setting,index: number) {
|
||||
editedIndex.value = index;
|
||||
editedItem.value = Object.assign({}, item);
|
||||
dialog.value = true;
|
||||
}
|
||||
|
||||
function updateSwitch(item: Setting){
|
||||
hotGuardStore.updateSettingNoIndex(item)
|
||||
}
|
||||
|
||||
function updateFollowDogSwitch(item: Setting){
|
||||
openFollowDog(item.platform,!item.followDogEnable).then(res=>{
|
||||
if(res?.data?.success){
|
||||
if(item.followDogEnable){
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式已打开!")
|
||||
}else{
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式已关闭!")
|
||||
}
|
||||
|
||||
}else{
|
||||
if(item.followDogEnable){
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式打开失败!")
|
||||
}else{
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式已关闭失败!")
|
||||
}
|
||||
item.followDogEnable = !item.followDogEnable
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function close() {
|
||||
dialog.value = false;
|
||||
editedItem.value = Object.assign({}, defaultItem.value);
|
||||
editedIndex.value = -1;
|
||||
}
|
||||
|
||||
function save() {
|
||||
const setting = Object.assign({}, editedItem.value)
|
||||
hotGuardStore.updateSetting(editedIndex.value,setting)
|
||||
close();
|
||||
}
|
||||
|
||||
//Computed Property
|
||||
</script>
|
||||
<template>
|
||||
@@ -95,150 +29,30 @@ function save() {
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" lg="4" md="6">
|
||||
<v-text-field
|
||||
density="compact"
|
||||
v-model="search"
|
||||
label="Search Contacts"
|
||||
hide-details
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" lg="8" md="6" class="text-right">
|
||||
<v-dialog v-model="dialog" max-width="700">
|
||||
<v-card>
|
||||
<v-card-title class="pa-4 bg-secondary">
|
||||
<span class="title text-white">Edit Setting</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
class="mt-5"
|
||||
ref="form"
|
||||
v-model="refForm"
|
||||
lazy-validation
|
||||
>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
density="compact"
|
||||
:rules="timeRules"
|
||||
v-model="editedItem.updateHotModuleTimes"
|
||||
label="热门模块更新时间(毫秒)"
|
||||
type="email"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
density="compact"
|
||||
:rules="timeRules"
|
||||
v-model="editedItem.updateHotLivesTimes"
|
||||
label="热门直播更新时间(毫秒)"
|
||||
type="phone"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="error" @click="close">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="secondary"
|
||||
:disabled="saveBtnDisable(editedItem)"
|
||||
variant="flat"
|
||||
@click="save"
|
||||
>Save</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-chip-group v-model="currentLabel" mandatory>
|
||||
<v-chip
|
||||
filter
|
||||
label
|
||||
variant="text"
|
||||
color="primary"
|
||||
v-for="label in hotGuardStore.fieldLabelList"
|
||||
:key="label"
|
||||
:value="label"
|
||||
>
|
||||
{{ label }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card class="mt-2">
|
||||
<v-table class="mt-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-subtitle-1 font-weight-semibold">Id</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">直播平台</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门模块监控</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门直播监控</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">跟风狗模式</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门模块更新时间</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门直播更新时间</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-body-1">
|
||||
<tr v-for="(item,index) in hotGuardStore.getSettings" :key="item.id">
|
||||
<td class="font-weight-bold">{{ item.id }}</td>
|
||||
<td>
|
||||
<div class="d-flex align-center py-1">
|
||||
<div class="ml-5">
|
||||
<p class="font-weight-bold">{{ item.platform }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<v-switch class="flex-col"
|
||||
size="small"
|
||||
style="padding-right: 65%"
|
||||
v-model="item.enableHotModule"
|
||||
color="rgb(33,150,243)"
|
||||
@click="updateSwitch(item)"
|
||||
inset
|
||||
></v-switch>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch class="flex-col"
|
||||
style="padding-right: 65%"
|
||||
size="small"
|
||||
v-model="item.enableHotLive"
|
||||
color="rgb(33,150,243)"
|
||||
@click="updateSwitch(item)"
|
||||
inset
|
||||
></v-switch>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch class="flex-col"
|
||||
style="padding-right: 65%"
|
||||
size="small"
|
||||
v-model="item.followDogEnable"
|
||||
color="green"
|
||||
@click="updateFollowDogSwitch(item)"
|
||||
inset
|
||||
></v-switch>
|
||||
</td>
|
||||
<td>{{nanosToHMS_CN(item.updateHotModuleTimes)}}</td>
|
||||
<td>{{nanosToHMS_CN(item.updateHotLivesTimes)}}</td>
|
||||
<td>
|
||||
<div class="d-flex align-center">
|
||||
<v-tooltip text="Edit">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
color="blue"
|
||||
icon
|
||||
variant="text"
|
||||
@click="editItem(item,index)"
|
||||
v-bind="props"
|
||||
>
|
||||
<v-icon>mdi-pencil-outline</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
22
console-ui/src/views/app/hot/hot_guard/hotGuardRoutes.ts
Normal file
22
console-ui/src/views/app/hot/hot_guard/hotGuardRoutes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export default [
|
||||
{
|
||||
path: "",
|
||||
redirect: "/apps/hotGuard/settings",
|
||||
},
|
||||
{
|
||||
path: "settings",
|
||||
name: "hot-guard-settings",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "apps-todo-tasks" */ "@/views/app/hot/hot_guard/pages/HotSettingsPage.vue"
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "guards",
|
||||
name: "hot-guard-guards",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "apps-todo-completed" */ "@/views/app/hot/hot_guard/pages/HotGuardsPage.vue"
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -9,7 +9,12 @@ export const useHotGuardStore = defineStore({
|
||||
id: "hotGuard",
|
||||
state:()=>({
|
||||
settings: ref<Setting[]>([]),
|
||||
guards:[]
|
||||
guards:[],
|
||||
fieldLabelList: ref<string[]>([
|
||||
"设置",
|
||||
"监控守卫"
|
||||
]),
|
||||
|
||||
}),
|
||||
|
||||
getters:{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<v-table class="mt-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-subtitle-1 font-weight-semibold">监控守卫名称</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">轮询时间</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">失败重试次数</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">上次更新</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-body-1">
|
||||
<tr v-for="(item,index) in hotGuardStore.guards" :key="item.id">
|
||||
<td class="font-weight-bold">
|
||||
<div class="d-flex align-center py-1">
|
||||
<div class="ml-5">
|
||||
<p class="font-weight-bold">{{ item.guardName }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-center py-1">
|
||||
<div class="ml-5">
|
||||
<p class="font-weight-bold">{{ nanosToHMS_CN(item.delayTime) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-center py-1">
|
||||
<div class="ml-5">
|
||||
<p class="font-weight-bold">{{ item.failRetryTimes }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-center py-1">
|
||||
<div class="ml-5">
|
||||
<p class="font-weight-bold">{{isoStrToNormal(item.preGuardTime)}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {getGuards, getHotGuardSetting} from "@/api/hot/hotGuardApi";
|
||||
import {useHotGuardStore} from "@/views/app/hot/hot_guard/hotGuardStore";
|
||||
import {nanosToHMS_CN,isoStrToNormal} from "@/utils/timeUtils";
|
||||
|
||||
const hotGuardStore = useHotGuardStore()
|
||||
|
||||
onMounted(async()=>{
|
||||
await getGuards().then(res=>{
|
||||
hotGuardStore.guards = res.data['list']
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
223
console-ui/src/views/app/hot/hot_guard/pages/HotSettingsPage.vue
Normal file
223
console-ui/src/views/app/hot/hot_guard/pages/HotSettingsPage.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<v-dialog v-model="dialog" max-width="700">
|
||||
<v-card>
|
||||
<v-card-title class="pa-4 bg-secondary">
|
||||
<span class="title text-white">Edit Setting</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
class="mt-5"
|
||||
ref="form"
|
||||
v-model="refForm"
|
||||
lazy-validation
|
||||
>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
density="compact"
|
||||
:rules="timeRules"
|
||||
v-model="editedItem.updateHotModuleTimes"
|
||||
label="热门模块更新时间(毫秒)"
|
||||
type="email"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
density="compact"
|
||||
:rules="timeRules"
|
||||
v-model="editedItem.updateHotLivesTimes"
|
||||
label="热门直播更新时间(毫秒)"
|
||||
type="phone"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="error" @click="close">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="secondary"
|
||||
:disabled="saveBtnDisable(editedItem)"
|
||||
variant="flat"
|
||||
@click="save"
|
||||
>Save</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-table class="mt-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-subtitle-1 font-weight-semibold">Id</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">直播平台</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门模块监控</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门直播监控</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">跟风狗模式</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门模块更新时间</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">热门直播更新时间</th>
|
||||
<th class="text-subtitle-1 font-weight-semibold">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-body-1">
|
||||
<tr v-for="(item,index) in hotGuardStore.getSettings" :key="item.id">
|
||||
<td class="font-weight-bold">{{ item.id }}</td>
|
||||
<td>
|
||||
<div class="d-flex align-center py-1">
|
||||
<div class="ml-5">
|
||||
<p class="font-weight-bold">{{ item.platform }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<v-switch class="flex-col"
|
||||
size="small"
|
||||
style="padding-right: 65%"
|
||||
v-model="item.enableHotModule"
|
||||
color="rgb(33,150,243)"
|
||||
@click="updateSwitch(item)"
|
||||
inset
|
||||
></v-switch>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch class="flex-col"
|
||||
style="padding-right: 65%"
|
||||
size="small"
|
||||
v-model="item.enableHotLive"
|
||||
color="rgb(33,150,243)"
|
||||
@click="updateSwitch(item)"
|
||||
inset
|
||||
></v-switch>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch class="flex-col"
|
||||
style="padding-right: 65%"
|
||||
size="small"
|
||||
v-model="item.followDogEnable"
|
||||
color="green"
|
||||
@click="updateFollowDogSwitch(item)"
|
||||
inset
|
||||
></v-switch>
|
||||
</td>
|
||||
<td>{{nanosToHMS_CN(item.updateHotModuleTimes)}}</td>
|
||||
<td>{{nanosToHMS_CN(item.updateHotLivesTimes)}}</td>
|
||||
<td>
|
||||
<div class="d-flex align-center">
|
||||
<v-tooltip text="Edit">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
color="blue"
|
||||
icon
|
||||
variant="text"
|
||||
@click="editItem(item,index)"
|
||||
v-bind="props"
|
||||
>
|
||||
<v-icon>mdi-pencil-outline</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {getGuards, getHotGuardSetting} from "@/api/hot/hotGuardApi";
|
||||
import {openFollowDog} from "@/api/hot/heatRecommendApi";
|
||||
import {useHotGuardStore} from "@/views/app/hot/hot_guard/hotGuardStore";
|
||||
import {nanosToHMS_CN} from "@/utils/timeUtils";
|
||||
import {Setting} from "@/views/app/hot/hot_guard/hotSettingTypes";
|
||||
import {useSnackbarStore} from "@/stores/snackbarStore";
|
||||
|
||||
const hotGuardStore = useHotGuardStore()
|
||||
const snackbarStore = useSnackbarStore();
|
||||
|
||||
onMounted(async()=>{
|
||||
await getHotGuardSetting().then(res=>{
|
||||
hotGuardStore.settings = res.data['list'].map((setting: any)=>setting as Setting)
|
||||
})
|
||||
})
|
||||
const timeRules = [
|
||||
(v)=> !!v || "时间必须存在",
|
||||
(v)=> v > 0 || "时间必须大于0",
|
||||
(v)=> v >= 1000 || "时间必须大于1s",
|
||||
]
|
||||
|
||||
const dialog = ref(false);
|
||||
const refForm = ref<Setting>();
|
||||
const editedItem = ref<Setting>();
|
||||
const editedIndex = ref(-1);
|
||||
const defaultItem = ref<Setting>({
|
||||
id: 0,
|
||||
platform:"",
|
||||
failRetryTimes:0,
|
||||
enableHotModule:false,
|
||||
enableHotLive:false,
|
||||
followDogEnable:false,
|
||||
updateHotModuleTimes:0,
|
||||
updateHotLivesTimes:0
|
||||
});
|
||||
|
||||
//Methods
|
||||
|
||||
function saveBtnDisable(item: any){
|
||||
const setting = item as Setting
|
||||
return timeRules.some((rule)=>typeof rule(setting.updateHotModuleTimes) === "string")||
|
||||
timeRules.some((rule)=>typeof rule(setting.updateHotLivesTimes) === "string")
|
||||
|
||||
}
|
||||
|
||||
function editItem(item: Setting,index: number) {
|
||||
editedIndex.value = index;
|
||||
editedItem.value = Object.assign({}, item);
|
||||
dialog.value = true;
|
||||
}
|
||||
|
||||
function updateSwitch(item: Setting){
|
||||
hotGuardStore.updateSettingNoIndex(item)
|
||||
}
|
||||
|
||||
function updateFollowDogSwitch(item: Setting){
|
||||
openFollowDog(item.platform,!item.followDogEnable).then(res=>{
|
||||
if(res?.data?.success){
|
||||
if(item.followDogEnable){
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式已打开!")
|
||||
}else{
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式已关闭!")
|
||||
}
|
||||
|
||||
}else{
|
||||
if(item.followDogEnable){
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式打开失败!")
|
||||
}else{
|
||||
snackbarStore.showSuccessMessage(item.platform+"跟风狗模式已关闭失败!")
|
||||
}
|
||||
item.followDogEnable = !item.followDogEnable
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function close() {
|
||||
dialog.value = false;
|
||||
editedItem.value = Object.assign({}, defaultItem.value);
|
||||
editedIndex.value = -1;
|
||||
}
|
||||
|
||||
function save() {
|
||||
const setting = Object.assign({}, editedItem.value)
|
||||
hotGuardStore.updateSetting(editedIndex.value,setting)
|
||||
close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
BIN
database.db
BIN
database.db
Binary file not shown.
Reference in New Issue
Block a user