Compare commits
4 Commits
a525a2b4ac
...
0991b3de26
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0991b3de26 | ||
|
|
1876be1777 | ||
|
|
51fc1a6aae | ||
|
|
726c21feac |
34
Dockerfile
Normal file
34
Dockerfile
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# 使用中科大镜像源的 Python 基础镜像
|
||||||
|
FROM python:3.11.15-slim
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 设置环境变量
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV FLASK_RUN_HOST=0.0.0.0
|
||||||
|
|
||||||
|
# 安装系统依赖(用于 git 等工具)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 复制依赖文件
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# 安装 Python 依赖(显式安装,避免缓存导致遗漏)
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt \
|
||||||
|
&& pip install --no-cache-dir "GitPython>=3.1.0" "gitdb>=4.0.1" "smmap>=3.0.1"
|
||||||
|
|
||||||
|
# 复制应用代码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 创建报告目录
|
||||||
|
RUN mkdir -p reports
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
# 启动应用
|
||||||
|
CMD ["python", "app.py"]
|
||||||
@@ -49,13 +49,12 @@ ai:
|
|||||||
# AI 审查器配置
|
# AI 审查器配置
|
||||||
# 支持: "ollama" (本地) 或 "api" (在线API)
|
# 支持: "ollama" (本地) 或 "api" (在线API)
|
||||||
provider: "api"
|
provider: "api"
|
||||||
# 模型名称(硅基流动可用模型)- Qwen 最强语言模型
|
# 模型名称(阿里云通义千问)
|
||||||
model: "deepseek-ai/DeepSeek-V3.2"
|
model: "qwen3.5-plus"
|
||||||
# API 地址
|
# API 地址
|
||||||
# 硅基流动: https://api.siliconflow.cn/v1
|
api_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
api_url: "https://api.siliconflow.cn/v1"
|
|
||||||
# API 密钥
|
# API 密钥
|
||||||
api_key: "sk-cqxhnsxdxaalxlykfkjksyinjftdyejnblmgkfxmhwmmvdyu"
|
api_key: "sk-616332b2afa94699b4572d0fe6ac370a"
|
||||||
# 是否启用 AI 审查
|
# 是否启用 AI 审查
|
||||||
enabled: true
|
enabled: true
|
||||||
# 每次审查的最大代码行数
|
# 每次审查的最大代码行数
|
||||||
|
|||||||
9
docker-compose.yml
Normal file
9
docker-compose.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
code-scan:
|
||||||
|
image: dcr-by1jwyxk44.71826370.xyz/whlaoding/code-scan:latest
|
||||||
|
container_name: code-scan
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
restart: unless-stopped
|
||||||
24
pyproject.toml
Normal file
24
pyproject.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=45", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "code-scan"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "代码扫描工具"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = [
|
||||||
|
"flask>=2.0.0",
|
||||||
|
"pyyaml>=5.0",
|
||||||
|
"requests>=2.25.0",
|
||||||
|
"python-dotenv>=0.19.0",
|
||||||
|
"GitPython>=3.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
code-scan = "app:main"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["."]
|
||||||
|
include = ["scanner*"]
|
||||||
@@ -2,3 +2,9 @@ flask>=2.0.0
|
|||||||
pyyaml>=5.0
|
pyyaml>=5.0
|
||||||
requests>=2.25.0
|
requests>=2.25.0
|
||||||
python-dotenv>=0.19.0
|
python-dotenv>=0.19.0
|
||||||
|
GitPython>=3.1.0
|
||||||
|
gitdb>=4.0.1
|
||||||
|
smmap>=3.0.1
|
||||||
|
pylint>=2.17.0
|
||||||
|
flake8>=6.0.0
|
||||||
|
bandit>=1.7.0
|
||||||
|
|||||||
218
test.py
218
test.py
@@ -1,218 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
os.environ.setdefault('FLASK_RUN_HOST', '0.0.0.0')
|
|
||||||
|
|
||||||
from flask import Flask, request, jsonify
|
|
||||||
import yaml
|
|
||||||
from webhook.handler import GiteaWebhookHandler
|
|
||||||
from scanner.python_scanner import PythonScanner
|
|
||||||
from scanner.js_scanner import JavaScriptScanner
|
|
||||||
from scanner.security_scanner import SecurityScanner
|
|
||||||
from report.generator import ReportGenerator
|
|
||||||
from notify.feishu import FeishuNotifier
|
|
||||||
|
|
||||||
# 配置日志
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# 加载配置
|
|
||||||
def load_config():
|
|
||||||
"""加载配置文件"""
|
|
||||||
config_path = os.path.join(os.path.dirname(__file__), 'config.yaml')
|
|
||||||
with open(config_path, 'r', encoding='utf-8') as f:
|
|
||||||
return yaml.safe_load(f)
|
|
||||||
|
|
||||||
# 全局配置
|
|
||||||
config = load_config()
|
|
||||||
|
|
||||||
# 初始化应用
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config['SECRET_KEY'] = config.get('server', {}).get('secret_key', 'dev-secret-key')
|
|
||||||
|
|
||||||
# 初始化组件
|
|
||||||
webhook_handler = GiteaWebhookHandler(config['gitea'])
|
|
||||||
python_scanner = PythonScanner(config.get('scanner', {}))
|
|
||||||
js_scanner = JavaScriptScanner(config.get('scanner', {}))
|
|
||||||
security_scanner = SecurityScanner(config.get('scanner', {}))
|
|
||||||
report_generator = ReportGenerator(config.get('report', {}))
|
|
||||||
feishu_notifier = FeishuNotifier(config['feishu'])
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
"""健康检查接口"""
|
|
||||||
return jsonify({
|
|
||||||
'status': 'ok',
|
|
||||||
'service': 'AI Code Quality Scanner',
|
|
||||||
'version': '1.0.0'
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/webhook/gitea', methods=['POST'])
|
|
||||||
def handle_gitea_webhook():
|
|
||||||
"""处理 Gitea Webhook 请求"""
|
|
||||||
try:
|
|
||||||
# 验证签名
|
|
||||||
signature = request.headers.get('X-Gitea-Signature')
|
|
||||||
if signature:
|
|
||||||
if not webhook_handler.verify_signature(
|
|
||||||
request.data,
|
|
||||||
signature,
|
|
||||||
config['gitea']['webhook_secret']
|
|
||||||
):
|
|
||||||
logger.warning('Webhook 签名验证失败')
|
|
||||||
return jsonify({'error': 'Invalid signature'}), 401
|
|
||||||
|
|
||||||
# 解析 Webhook payload
|
|
||||||
payload = request.json
|
|
||||||
if not payload:
|
|
||||||
return jsonify({'error': 'No payload'}), 400
|
|
||||||
|
|
||||||
event_type = request.headers.get('X-Gitea-Event', 'push')
|
|
||||||
logger.info(f'收到 Gitea Webhook 事件: {event_type}')
|
|
||||||
|
|
||||||
# 只处理 push 事件
|
|
||||||
if event_type != 'push':
|
|
||||||
return jsonify({'message': 'Event ignored'}), 200
|
|
||||||
|
|
||||||
# 提取提交信息
|
|
||||||
commits = payload.get('commits', [])
|
|
||||||
if not commits:
|
|
||||||
return jsonify({'message': 'No commits'}), 200
|
|
||||||
|
|
||||||
repo = payload.get('repository', {})
|
|
||||||
repo_name = repo.get('full_name', 'unknown')
|
|
||||||
branch = payload.get('ref', '').replace('refs/heads/', '')
|
|
||||||
pusher = payload.get('pusher', {}).get('name', 'unknown')
|
|
||||||
|
|
||||||
logger.info(f'处理仓库 {repo_name} 的 {len(commits)} 个提交')
|
|
||||||
|
|
||||||
# 处理每个提交
|
|
||||||
for commit in commits:
|
|
||||||
commit_id = commit.get('id', '')[:8]
|
|
||||||
commit_message = commit.get('message', '')
|
|
||||||
author = commit.get('author', {}).get('name', 'unknown')
|
|
||||||
|
|
||||||
logger.info(f'扫描提交 {commit_id}: {commit_message}')
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 获取仓库 URL
|
|
||||||
clone_url = repo.get('clone_url')
|
|
||||||
if not clone_url:
|
|
||||||
# 尝试从 web_url 构建
|
|
||||||
web_url = repo.get('web_url', '')
|
|
||||||
if web_url:
|
|
||||||
clone_url = web_url.replace('http://', 'http://').replace('https://', 'https://')
|
|
||||||
clone_url = clone_url.rstrip('/') + '.git'
|
|
||||||
|
|
||||||
# 执行代码扫描
|
|
||||||
scan_results = {}
|
|
||||||
|
|
||||||
# Python 扫描
|
|
||||||
if 'python' in config.get('scanner', {}).get('languages', []):
|
|
||||||
scan_results['python'] = python_scanner.scan(
|
|
||||||
clone_url, commit_id, branch
|
|
||||||
)
|
|
||||||
|
|
||||||
# JavaScript/TypeScript 扫描
|
|
||||||
if any(lang in config.get('scanner', {}).get('languages', [])
|
|
||||||
for lang in ['javascript', 'typescript']):
|
|
||||||
scan_results['javascript'] = js_scanner.scan(
|
|
||||||
clone_url, commit_id, branch
|
|
||||||
)
|
|
||||||
|
|
||||||
# 安全扫描
|
|
||||||
scan_results['security'] = security_scanner.scan(
|
|
||||||
clone_url, commit_id, branch
|
|
||||||
)
|
|
||||||
|
|
||||||
# 生成报告
|
|
||||||
report = report_generator.generate(
|
|
||||||
repo_name=repo_name,
|
|
||||||
branch=branch,
|
|
||||||
commit_id=commit_id,
|
|
||||||
commit_message=commit_message,
|
|
||||||
author=author,
|
|
||||||
scan_results=scan_results
|
|
||||||
)
|
|
||||||
|
|
||||||
# 发送飞书通知
|
|
||||||
feishu_notifier.send_report(report)
|
|
||||||
|
|
||||||
logger.info(f'提交 {commit_id} 扫描完成')
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'扫描提交 {commit_id} 失败: {str(e)}')
|
|
||||||
# 继续处理其他提交
|
|
||||||
continue
|
|
||||||
|
|
||||||
return jsonify({'status': 'ok', 'message': 'Scan completed'}), 200
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'处理 Webhook 失败: {str(e)}', exc_info=True)
|
|
||||||
return jsonify({'error': str(e)}), 500
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/scan/manual', methods=['POST'])
|
|
||||||
def manual_scan():
|
|
||||||
"""手动触发扫描接口"""
|
|
||||||
try:
|
|
||||||
data = request.json
|
|
||||||
repo_url = data.get('repo_url')
|
|
||||||
branch = data.get('branch', 'main')
|
|
||||||
commit_id = data.get('commit_id')
|
|
||||||
|
|
||||||
if not repo_url:
|
|
||||||
return jsonify({'error': 'repo_url is required'}), 400
|
|
||||||
|
|
||||||
# 执行扫描
|
|
||||||
scan_results = {}
|
|
||||||
|
|
||||||
if 'python' in config.get('scanner', {}).get('languages', []):
|
|
||||||
scan_results['python'] = python_scanner.scan(repo_url, commit_id, branch)
|
|
||||||
|
|
||||||
if any(lang in config.get('scanner', {}).get('languages', [])
|
|
||||||
for lang in ['javascript', 'typescript']):
|
|
||||||
scan_results['javascript'] = js_scanner.scan(repo_url, commit_id, branch)
|
|
||||||
|
|
||||||
scan_results['security'] = security_scanner.scan(repo_url, commit_id, branch)
|
|
||||||
|
|
||||||
# 生成报告
|
|
||||||
report = report_generator.generate(
|
|
||||||
repo_name=repo_url.split('/')[-1].replace('.git', ''),
|
|
||||||
branch=branch,
|
|
||||||
commit_id=commit_id or 'manual',
|
|
||||||
commit_message='Manual scan',
|
|
||||||
author='manual',
|
|
||||||
scan_results=scan_results
|
|
||||||
)
|
|
||||||
|
|
||||||
# 发送飞书通知
|
|
||||||
feishu_notifier.send_report(report)
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
'status': 'ok',
|
|
||||||
'report': report
|
|
||||||
}), 200
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'手动扫描失败: {str(e)}', exc_info=True)
|
|
||||||
return jsonify({'error': str(e)}), 500
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# 强制监听所有网络接口
|
|
||||||
host = "0.0.0.0"
|
|
||||||
port = config.get('server', {}).get('port', 5000)
|
|
||||||
debug = config.get('server', {}).get('debug', True)
|
|
||||||
|
|
||||||
logger.info(f'启动服务: {host}:{port}')
|
|
||||||
app.run(host=host, port=port, debug=debug)
|
|
||||||
142
web/index.html
142
web/index.html
@@ -97,30 +97,9 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h6 class="mt-4 mb-2 text-uppercase" style="color: rgba(255,255,255,0.5); font-size: 11px;">AI 智能分析</h6>
|
|
||||||
<ul class="nav flex-column">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#" onclick="showPage('ai-quality')">
|
|
||||||
<i class="bi bi-stars me-2"></i>AI 质量评分
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#" onclick="showPage('ai-insights')">
|
|
||||||
<i class="bi bi-lightbulb me-2"></i>智能洞察
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="nav flex-column mt-3">
|
<ul class="nav flex-column mt-3">
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="mt-4 p-3" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px;">
|
|
||||||
<small><i class="bi bi-cpu me-1"></i> AI 引擎</small>
|
|
||||||
<div class="mt-2 small">基于大模型智能分析代码质量</div>
|
|
||||||
<div class="mt-2">
|
|
||||||
<span class="badge bg-success">在线</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 主内容区 -->
|
<!-- 主内容区 -->
|
||||||
@@ -289,126 +268,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 设置页面 -->
|
<!-- 设置页面 -->
|
||||||
<!-- AI 质量评分页面 -->
|
|
||||||
<div id="page-ai-quality" style="display:none;">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-stars text-primary"></i> AI 质量评分</h2>
|
|
||||||
<div class="alert alert-info py-2 mb-3">
|
|
||||||
<i class="bi bi-info-circle"></i> 基于 AI 大模型对 PR 代码进行多维度质量评估
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3">
|
|
||||||
<div class="card text-center bg-gradient-primary text-white">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="display-4 fw-bold" id="aiq-total">--</div>
|
|
||||||
<small>综合评分</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<div class="card text-center">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="h3 fw-bold text-success" id="aiq-security">--</div>
|
|
||||||
<small class="text-muted">安全性</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<div class="card text-center">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="h3 fw-bold text-warning" id="aiq-maintain">--</div>
|
|
||||||
<small class="text-muted">可维护性</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<div class="card text-center">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="h3 fw-bold text-info" id="aiq-readability">--</div>
|
|
||||||
<small class="text-muted">可读性</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="mb-0">评分说明</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<ul class="list-group list-group-flush">
|
|
||||||
<li class="list-group-item">
|
|
||||||
<strong class="text-success">安全性 (35%)</strong> - 检测 SQL 注入、XSS、密码泄露等安全风险
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<strong class="text-warning">可维护性 (30%)</strong> - 代码复杂度、重复代码、硬编码等问题
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<strong class="text-info">可读性 (15%)</strong> - 命名规范、注释、代码风格
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<strong class="text-primary">最佳实践 (20%)</strong> - 遵循语言最佳实践和设计模式
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- AI 智能洞察页面 -->
|
|
||||||
<div id="page-ai-insights" style="display:none;">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-lightbulb text-warning"></i> AI 智能洞察</h2>
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card border-primary">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<i class="bi bi-robot display-4 text-primary"></i>
|
|
||||||
<h5 class="mt-3">智能分析</h5>
|
|
||||||
<p class="text-muted small">基于 AI 大模型深度分析代码问题,提供精准修复建议</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card border-success">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<i class="bi bi-lightning display-4 text-success"></i>
|
|
||||||
<h5 class="mt-3">自动修复</h5>
|
|
||||||
<p class="text-muted small">一键生成修复代码,直接应用到项目中</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card border-info">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<i class="bi bi-graph-up display-4 text-info"></i>
|
|
||||||
<h5 class="mt-3">趋势预测</h5>
|
|
||||||
<p class="text-muted small">分析历史数据,预测代码质量变化趋势</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">AI 能力展示</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h6><i class="bi bi-check2-all text-success"></i> 已支持功能</h6>
|
|
||||||
<ul class="list-unstyled ms-3">
|
|
||||||
<li><i class="bi bi-check text-success me-2"></i>多维度代码质量评分</li>
|
|
||||||
<li><i class="bi bi-check text-success me-2"></i>问题根因分析</li>
|
|
||||||
<li><i class="bi bi-check text-success me-2"></i>智能修复建议生成</li>
|
|
||||||
<li><i class="bi bi-check text-success me-2"></i>历史趋势分析</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h6><i class="bi bi-gear text-primary"></i> 扫描器类型</h6>
|
|
||||||
<ul class="list-unstyled ms-3">
|
|
||||||
<li><i class="bi bi-code-slash me-2"></i>Python 代码分析</li>
|
|
||||||
<li><i class="bi bi-code-slash me-2"></i>JavaScript/TypeScript 分析</li>
|
|
||||||
<li><i class="bi bi-shield-check me-2"></i>安全漏洞检测</li>
|
|
||||||
<li><i class="bi bi-stars me-2"></i>AI 智能审查</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -660,7 +519,6 @@
|
|||||||
|
|
||||||
if (page === 'dashboard') loadDashboard();
|
if (page === 'dashboard') loadDashboard();
|
||||||
if (page === 'prs') loadPRs();
|
if (page === 'prs') loadPRs();
|
||||||
if (page === 'ai-quality') loadAIQualityOverview();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载概览数据
|
// 加载概览数据
|
||||||
|
|||||||
Reference in New Issue
Block a user