Compare commits
No commits in common. 'main' and 'guoning' have entirely different histories.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,51 +0,0 @@
|
||||
# Node.js
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# 项目特定
|
||||
projects_data/
|
||||
out/
|
||||
*.log
|
||||
|
||||
# 临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
temp_*.py
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# 环境变量
|
||||
.env
|
||||
.env.local
|
||||
|
||||
@ -0,0 +1,235 @@
|
||||
|
||||
# 基于多agent协同的军工Python代码合规性检查系统 - FortifyCode
|
||||
|
||||
## 🎯 系统特色
|
||||
|
||||
### 核心功能
|
||||
- **多 Agent 协同检查**: 集成 pylint、flake8、bandit 三大专业工具
|
||||
- **项目导入管理**: 支持 GitHub、Gitee 仓库导入和本地文件上传
|
||||
- **实时检查报告**: 详细的代码质量、安全漏洞、合规性分析
|
||||
- **军事级安全**: 专门针对军事代码的特殊安全要求
|
||||
|
||||
### 技术架构
|
||||
- **前端**: HTML5 + CSS3 + JavaScript (ES6+)
|
||||
- **后端**: Flask + SQLAlchemy + MySQL
|
||||
- **代码检查**: pylint + flake8 + bandit
|
||||
- **版本控制**: Git 集成支持
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
- Python 3.8+
|
||||
- MySQL 5.7+
|
||||
- Node.js (可选,用于前端开发)
|
||||
|
||||
### 安装步骤
|
||||
|
||||
1. **克隆项目**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd FortifyCode
|
||||
```
|
||||
|
||||
2. **安装 Python 依赖**
|
||||
```bash
|
||||
cd backend
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. **安装代码检查工具**
|
||||
```bash
|
||||
pip install pylint flake8 bandit
|
||||
```
|
||||
|
||||
4. **配置数据库**
|
||||
```bash
|
||||
# 启动 MySQL 服务
|
||||
# 创建数据库用户 (可选)
|
||||
mysql -u root -p
|
||||
SOURCE database_init.sql
|
||||
```
|
||||
|
||||
5. **启动系统**
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
6. **访问系统**
|
||||
- 前端界面: http://localhost:3000 (或直接打开 index.html)
|
||||
- 后端API: http://localhost:5000
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
FortifyCode/
|
||||
├── index.html # 主页面
|
||||
├── css/
|
||||
│ └── style.css # 样式文件
|
||||
├── js/
|
||||
│ └── app.js # 前端逻辑
|
||||
├── backend/
|
||||
│ ├── app.py # Flask 后端服务
|
||||
│ ├── config.py # 配置文件
|
||||
│ ├── start.py # 启动脚本
|
||||
│ ├── requirements.txt # Python 依赖
|
||||
│ └── database_init.sql # 数据库初始化脚本
|
||||
├── uploads/ # 文件上传目录
|
||||
├── projects/ # 项目存储目录
|
||||
├── reports/ # 检查报告目录
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### 数据库配置
|
||||
在 `backend/config.py` 中修改数据库连接信息:
|
||||
```python
|
||||
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://用户名:密码@localhost/fortifycode'
|
||||
```
|
||||
|
||||
### 工具配置
|
||||
在 `backend/app.py` 中修改工具配置:
|
||||
```python
|
||||
TOOLS_CONFIG = {
|
||||
'pylint': {
|
||||
'command': 'pylint',
|
||||
'args': ['--output-format=json', '--reports=no'],
|
||||
'enabled': True
|
||||
},
|
||||
# ... 其他工具配置
|
||||
}
|
||||
```
|
||||
|
||||
## 📖 使用指南
|
||||
|
||||
### 1. 项目管理
|
||||
- **新建项目**: 点击"新建项目"按钮,选择项目来源
|
||||
- GitHub/Gitee: 输入仓库 URL
|
||||
- 文件上传: 选择本地文件或文件夹
|
||||
- **项目列表**: 查看所有项目的基本信息和检查状态
|
||||
- **项目搜索**: 使用搜索框快速查找项目
|
||||
|
||||
### 2. 代码检查
|
||||
- **快捷检查**: 在仪表板直接上传文件进行快速检查
|
||||
- **项目检查**: 在项目详情页面运行完整的代码检查
|
||||
- **检查历史**: 查看项目的所有检查记录
|
||||
|
||||
### 3. 报告查看
|
||||
- **检查结果**: 查看详细的代码问题列表
|
||||
- **问题分类**: 按错误、警告、信息分类查看
|
||||
- **修复建议**: 获取具体的问题修复建议
|
||||
|
||||
### 4. 系统设置
|
||||
- **Agent配置**: 配置各个检查工具的参数
|
||||
- **检查规则**: 自定义代码风格和安全规则
|
||||
- **通知设置**: 配置检查完成通知
|
||||
|
||||
## 🛡️ 安全特性
|
||||
|
||||
### 军事级安全要求
|
||||
- **敏感信息检测**: 自动识别硬编码密码、API密钥等
|
||||
- **安全漏洞扫描**: SQL注入、XSS、CSRF等安全漏洞检测
|
||||
- **合规性检查**: 军事代码规范合规性验证
|
||||
- **访问控制**: 用户权限管理和操作日志记录
|
||||
|
||||
### 数据安全
|
||||
- **文件隔离**: 每个项目独立存储,避免数据泄露
|
||||
- **临时文件清理**: 自动清理检查过程中的临时文件
|
||||
- **数据加密**: 敏感数据加密存储
|
||||
|
||||
## 🔍 检查工具说明
|
||||
|
||||
### pylint
|
||||
- **功能**: Python 代码质量检查
|
||||
- **检查项**: 代码风格、错误检测、复杂度分析
|
||||
- **输出**: JSON 格式的结构化报告
|
||||
|
||||
### flake8
|
||||
- **功能**: Python 代码风格检查
|
||||
- **检查项**: PEP8 规范、语法错误、未使用变量
|
||||
- **输出**: 文本格式的详细报告
|
||||
|
||||
### bandit
|
||||
- **功能**: Python 安全漏洞扫描
|
||||
- **检查项**: 安全漏洞、敏感信息泄露、危险函数调用
|
||||
- **输出**: JSON 格式的安全报告
|
||||
|
||||
## 📊 报告格式
|
||||
|
||||
### 检查结果结构
|
||||
```json
|
||||
{
|
||||
"total_issues": 25,
|
||||
"error_count": 5,
|
||||
"warning_count": 15,
|
||||
"info_count": 5,
|
||||
"tools_status": {
|
||||
"pylint": "completed",
|
||||
"flake8": "completed",
|
||||
"bandit": "completed"
|
||||
},
|
||||
"all_issues": [
|
||||
{
|
||||
"file": "src/database.py",
|
||||
"line": 45,
|
||||
"type": "error",
|
||||
"severity": "high",
|
||||
"message": "SQL注入风险",
|
||||
"rule": "B608"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **MySQL 连接失败**
|
||||
- 检查 MySQL 服务是否启动
|
||||
- 验证用户名和密码是否正确
|
||||
- 确认数据库是否存在
|
||||
|
||||
2. **代码检查工具未找到**
|
||||
- 运行 `pip install pylint flake8 bandit`
|
||||
- 检查 PATH 环境变量
|
||||
- 使用绝对路径配置工具
|
||||
|
||||
3. **文件上传失败**
|
||||
- 检查 uploads 目录权限
|
||||
- 确认文件大小限制
|
||||
- 验证文件格式是否支持
|
||||
|
||||
4. **前端页面无法访问**
|
||||
- 检查文件路径是否正确
|
||||
- 确认浏览器支持现代 JavaScript
|
||||
- 查看浏览器控制台错误信息
|
||||
|
||||
### 日志查看
|
||||
系统日志输出在控制台,包含:
|
||||
- 请求处理日志
|
||||
- 错误信息
|
||||
- 检查进度信息
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
1. Fork 项目
|
||||
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 打开 Pull Request
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
|
||||
|
||||
## 📞 支持
|
||||
|
||||
如有问题或建议,请:
|
||||
- 提交 Issue
|
||||
- 发送邮件至项目维护者
|
||||
- 查看项目文档
|
||||
|
||||
---
|
||||
|
||||
**FortifyCode** - 让代码更安全,让开发更放心 🛡️
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,30 @@
|
||||
多Agent状态面板
|
||||
<div class="agent-status">
|
||||
<div class="panel-header">
|
||||
<h2 class="panel-title">Agent 状态</h2>
|
||||
</div>
|
||||
<div class="agent-list">
|
||||
<div class="agent-item">
|
||||
<div class="agent-avatar">A1</div>
|
||||
<div class="agent-info">
|
||||
<div class="agent-name">pylint Agent</div>
|
||||
<span class="agent-status-badge status-idle">空闲</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="agent-item">
|
||||
<div class="agent-avatar">A2</div>
|
||||
<div class="agent-info">
|
||||
<div class="agent-name">flake8 Agent</div>
|
||||
<span class="agent-status-badge status-idle">空闲</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="agent-item">
|
||||
<div class="agent-avatar">A3</div>
|
||||
<div class="agent-info">
|
||||
<div class="agent-name">bandit Agent</div>
|
||||
<span class="agent-status-badge status-idle">空闲</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,70 @@
|
||||
-- 设置字符集
|
||||
SET NAMES utf8mb4;
|
||||
SET CHARACTER SET utf8mb4;
|
||||
|
||||
-- 创建数据库
|
||||
CREATE DATABASE IF NOT EXISTS fortifycode CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- 使用数据库
|
||||
USE fortifycode;
|
||||
|
||||
-- 创建项目表
|
||||
CREATE TABLE IF NOT EXISTS project (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
source_type VARCHAR(50) NOT NULL COMMENT 'github, gitee, upload',
|
||||
source_url VARCHAR(500),
|
||||
local_path VARCHAR(500),
|
||||
language VARCHAR(50) DEFAULT 'python',
|
||||
status VARCHAR(50) DEFAULT 'active' COMMENT 'active, archived, deleted',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
created_by VARCHAR(100) DEFAULT 'admin',
|
||||
INDEX idx_name (name),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_created_at (created_at)
|
||||
);
|
||||
|
||||
-- 创建代码检查表
|
||||
CREATE TABLE IF NOT EXISTS code_check (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
project_id INT NOT NULL,
|
||||
check_type VARCHAR(50) NOT NULL COMMENT 'pylint, flake8, bandit, combined',
|
||||
status VARCHAR(50) DEFAULT 'pending' COMMENT 'pending, running, completed, failed',
|
||||
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMP NULL,
|
||||
total_files INT DEFAULT 0,
|
||||
total_issues INT DEFAULT 0,
|
||||
error_count INT DEFAULT 0,
|
||||
warning_count INT DEFAULT 0,
|
||||
info_count INT DEFAULT 0,
|
||||
report_path VARCHAR(500),
|
||||
summary TEXT,
|
||||
FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE CASCADE,
|
||||
INDEX idx_project_id (project_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_started_at (started_at)
|
||||
);
|
||||
|
||||
-- 创建检查结果表
|
||||
CREATE TABLE IF NOT EXISTS check_result (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
check_id INT NOT NULL,
|
||||
file_path VARCHAR(500) NOT NULL,
|
||||
line_number INT,
|
||||
column_number INT,
|
||||
issue_type VARCHAR(50) NOT NULL COMMENT 'error, warning, info',
|
||||
severity VARCHAR(20) DEFAULT 'medium' COMMENT 'high, medium, low',
|
||||
rule_id VARCHAR(100),
|
||||
message TEXT NOT NULL,
|
||||
suggestion TEXT,
|
||||
FOREIGN KEY (check_id) REFERENCES code_check(id) ON DELETE CASCADE,
|
||||
INDEX idx_check_id (check_id),
|
||||
INDEX idx_issue_type (issue_type),
|
||||
INDEX idx_severity (severity)
|
||||
);
|
||||
|
||||
-- 字符集设置正确
|
||||
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@ -0,0 +1,92 @@
|
||||
-- 创建数据库
|
||||
CREATE DATABASE IF NOT EXISTS df2_0 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
|
||||
USE df2_0;
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for users
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
CREATE TABLE `users` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(50) NOT NULL COMMENT '用户名',
|
||||
`password` varchar(100) NOT NULL COMMENT '密码',
|
||||
`name` varchar(50) NOT NULL COMMENT '姓名',
|
||||
`gender` varchar(10) NOT NULL COMMENT '性别',
|
||||
`unit` varchar(100) NOT NULL COMMENT '单位',
|
||||
`role` varchar(20) NOT NULL DEFAULT 'user' COMMENT '角色',
|
||||
`contact` varchar(50) DEFAULT NULL COMMENT '联系方式',
|
||||
`last_login` datetime DEFAULT NULL COMMENT '最后登录时间',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for dues
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `dues`;
|
||||
CREATE TABLE `dues` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL COMMENT '用户ID',
|
||||
`due_amount` decimal(10,2) NOT NULL COMMENT '应缴金额',
|
||||
`actual_amount` decimal(10,2) NOT NULL COMMENT '实缴金额',
|
||||
`payment_type` varchar(20) NOT NULL COMMENT '缴费类型',
|
||||
`payment_date` date NOT NULL COMMENT '缴费日期',
|
||||
`status` varchar(20) NOT NULL COMMENT '状态',
|
||||
`remark` text COMMENT '备注',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `user_id` (`user_id`),
|
||||
CONSTRAINT `dues_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='党费记录表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for payment_types
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `payment_types`;
|
||||
CREATE TABLE `payment_types` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`type_code` varchar(20) NOT NULL COMMENT '类型代码',
|
||||
`type_name` varchar(50) NOT NULL COMMENT '类型名称',
|
||||
`description` varchar(200) DEFAULT NULL COMMENT '描述',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `type_code` (`type_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='缴费类型表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_logs
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `system_logs`;
|
||||
CREATE TABLE `system_logs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL COMMENT '用户ID',
|
||||
`action` varchar(50) NOT NULL COMMENT '操作类型',
|
||||
`description` text NOT NULL COMMENT '操作描述',
|
||||
`ip_address` varchar(50) DEFAULT NULL COMMENT 'IP地址',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `user_id` (`user_id`),
|
||||
CONSTRAINT `system_logs_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of users
|
||||
-- ----------------------------
|
||||
INSERT INTO `users` VALUES (1, 'admin', 'a123', '张三', '男', '系统管理部', 'admin', 'admin@example.com', NOW(), NOW(), NOW());
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of payment_types
|
||||
-- ----------------------------
|
||||
INSERT INTO `payment_types` VALUES
|
||||
(1, 'monthly', '月度缴费', '每月固定党费缴纳', NOW()),
|
||||
(2, 'special', '特殊党费', '特殊时期或特殊用途的党费缴纳', NOW()),
|
||||
(3, 'other', '其他', '其他类型的党费缴纳', NOW());
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
@ -0,0 +1,5 @@
|
||||
Flask==2.0.1
|
||||
Flask-Cors==3.0.10
|
||||
PyMySQL==1.0.2
|
||||
python-dotenv==0.19.0
|
||||
Werkzeug==2.0.1
|
||||
@ -0,0 +1,165 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>新增党费记录</title>
|
||||
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.form-container {
|
||||
max-width: 800px;
|
||||
margin: 2rem auto;
|
||||
padding: 2rem;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.required-label::after {
|
||||
content: "*";
|
||||
color: red;
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<h2 class="mb-4">新增党费记录</h2>
|
||||
|
||||
<!-- 新增记录表单 -->
|
||||
<form id="addForm">
|
||||
<!-- 姓名 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required-label">姓名</label>
|
||||
<input type="text" class="form-control" id="name" required>
|
||||
</div>
|
||||
|
||||
<!-- ID -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required-label">ID</label>
|
||||
<input type="text" class="form-control" id="userId" required>
|
||||
</div>
|
||||
|
||||
<!-- 性别 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required-label">性别</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="gender" id="male" value="男" required>
|
||||
<label class="form-check-label" for="male">男</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="gender" id="female" value="女">
|
||||
<label class="form-check-label" for="female">女</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 单位 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required-label">单位</label>
|
||||
<input type="text" class="form-control" id="unit" required>
|
||||
</div>
|
||||
|
||||
<!-- 金额组 -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label required-label">应缴金额(元)</label>
|
||||
<input type="number" class="form-control" id="dueAmount" min="0" step="0.01" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label required-label">实缴金额(元)</label>
|
||||
<input type="number" class="form-control" id="actualAmount" min="0" step="0.01" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 缴费类型 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required-label">缴费类型</label>
|
||||
<select class="form-select" id="paymentType" required>
|
||||
<option value="">请选择</option>
|
||||
<option value="monthly">月度缴费</option>
|
||||
<option value="special">特殊党费</option>
|
||||
<option value="other">其他</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 缴费日期 -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required-label">缴费日期</label>
|
||||
<input type="date" class="form-control" id="paymentDate" required>
|
||||
</div>
|
||||
|
||||
<!-- 备注 -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">备注</label>
|
||||
<textarea class="form-control" id="remark" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-secondary" onclick="goBack()">取消</button>
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
|
||||
<script>
|
||||
// 返回管理主页
|
||||
function goBack() {
|
||||
window.location.href = "{{ url_for('admin_main') }}";
|
||||
}
|
||||
|
||||
// 表单提交处理
|
||||
$('#addForm').submit(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// 表单验证
|
||||
const due = parseFloat($('#dueAmount').val());
|
||||
const actual = parseFloat($('#actualAmount').val());
|
||||
|
||||
if (actual > due) {
|
||||
alert('实缴金额不能超过应缴金额');
|
||||
return;
|
||||
}
|
||||
|
||||
// 自动计算状态
|
||||
const status = actual >= due ? '已交齐' : '未交齐';
|
||||
|
||||
// 构建数据对象
|
||||
const formData = {
|
||||
userId: $('#userId').val(),
|
||||
dueAmount: due,
|
||||
actualAmount: actual,
|
||||
paymentType: $('#paymentType').val(),
|
||||
paymentDate: $('#paymentDate').val(),
|
||||
remark: $('#remark').val(),
|
||||
status: status
|
||||
};
|
||||
|
||||
// 发送请求
|
||||
$.ajax({
|
||||
url: '/api/dues',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(formData),
|
||||
success: function() {
|
||||
alert('提交成功!');
|
||||
goBack();
|
||||
},
|
||||
error: function(xhr) {
|
||||
alert(`提交失败:${xhr.responseJSON?.message || '服务器错误'}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 输入验证
|
||||
$('#dueAmount, #actualAmount').on('input', function() {
|
||||
const value = parseFloat($(this).val());
|
||||
if (value < 0) {
|
||||
$(this).val(0);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,264 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<!-- 基础元信息 -->
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>党费管理系统 - 管理后台</title>
|
||||
|
||||
<!-- 引入Bootstrap CSS框架 -->
|
||||
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- 引入Font Awesome图标库 -->
|
||||
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
|
||||
<!-- 自定义样式 -->
|
||||
<style>
|
||||
/* 状态显示样式 */
|
||||
.status-paid { color: #28a745; } /* 已交齐状态绿色 */
|
||||
.status-unpaid { color: #dc3545; } /* 未交齐状态红色 */
|
||||
.operation-btns { margin: 20px 0; gap: 10px; } /* 操作按钮容器边距+间隔 */
|
||||
.search-box { max-width: 300px; } /* 搜索框宽度限制 */
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container-fluid">
|
||||
<!-- 系统Logo -->
|
||||
<a class="navbar-brand" href="#">
|
||||
<i class="fas fa-home me-2"></i>党费管理系统
|
||||
</a>
|
||||
|
||||
<!-- 功能导航区 -->
|
||||
<div class="d-flex">
|
||||
<!-- 统计分析按钮 -->
|
||||
<button class="btn btn-outline-light me-2" onclick="window.location.href='{{ url_for('stats_page') }}'">
|
||||
<i class="fas fa-chart-bar me-1"></i>统计分析
|
||||
</button>
|
||||
<!-- 导入导出按钮 -->
|
||||
<button class="btn btn-outline-light me-2" onclick="window.location.href='{{ url_for('data_handle_page') }}'">
|
||||
<i class="fas fa-file-import me-1"></i>数据导入/导出
|
||||
</button>
|
||||
<!-- 退出登录按钮 -->
|
||||
<button class="btn btn-outline-light" onclick="logout()">
|
||||
<i class="fas fa-sign-out-alt me-1"></i>退出
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 数据管理区 -->
|
||||
<div class="container mt-4">
|
||||
<!-- 操作按钮组 -->
|
||||
<div class="operation-btns d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<button class="btn btn-primary me-2" style="box-shadow: 0 2px 4px rgba(0,0,0,0.1);" onclick="window.location.href='{{ url_for('add_record_page') }}'">
|
||||
<i class="fas fa-plus me-1"></i>新增记录
|
||||
</button>
|
||||
<button class="btn btn-danger me-2" style="box-shadow: 0 2px 4px rgba(0,0,0,0.1);" onclick="deleteSelected()">
|
||||
<i class="fas fa-trash me-1"></i>删除记录
|
||||
</button>
|
||||
<button class="btn btn-warning" onclick="editSelected()">
|
||||
<i class="fas fa-edit me-1"></i>修改记录
|
||||
</button>
|
||||
</div>
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-box">
|
||||
<div class="input-group">
|
||||
<input type="text" id="searchInput" class="form-control" placeholder="输入姓名或ID">
|
||||
<button class="btn btn-primary" onclick="searchRecords()">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"></th>
|
||||
<th>姓名</th>
|
||||
<th>ID</th>
|
||||
<th>性别</th>
|
||||
<th>单位</th>
|
||||
<th>应缴金额</th>
|
||||
<th>实缴金额</th>
|
||||
<th>缴费日期</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dataBody">
|
||||
<!-- 数据通过JavaScript动态加载 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引入jQuery库 -->
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
|
||||
|
||||
<script>
|
||||
// 初始化数据存储
|
||||
let currentData = [];
|
||||
|
||||
// 数据加载函数
|
||||
// function loadData() {
|
||||
// $.get('/api/dues', data => {
|
||||
// currentData = data;
|
||||
// renderTable(data);
|
||||
// }).fail(() => {
|
||||
// alert('加载数据失败,请刷新页面重试');
|
||||
// });
|
||||
// }
|
||||
function loadData() {
|
||||
$.ajax({
|
||||
url: '/api/dues',
|
||||
method: 'GET',
|
||||
success: function(data){
|
||||
currentData = data;
|
||||
renderTable(data);
|
||||
},
|
||||
error :function(xhr, status, error){
|
||||
alert('加载数据失败:' + error + '。请刷新页面重试');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 表格渲染函数
|
||||
function renderTable(data) {
|
||||
const tbody = $('#dataBody').empty();//表格体
|
||||
data.forEach(item => {
|
||||
// 动态计算状态
|
||||
const status = item.actual_amount >= item.due_amount ?
|
||||
'<span class="status-paid">已交齐</span>' :
|
||||
'<span class="status-unpaid">未交齐</span>';
|
||||
|
||||
// 构建表格行
|
||||
tbody.append(`
|
||||
<tr data-id="${item.id}">
|
||||
<td><input type="checkbox" class="rowCheck"></td>
|
||||
<td>${item.name}</td>
|
||||
<td>${item.user_id}</td>
|
||||
<td>${item.gender || '-'}</td>
|
||||
<td>${item.unit}</td>
|
||||
<td>¥${item.due_amount.toFixed(2)}</td>
|
||||
<td>¥${item.actual_amount.toFixed(2)}</td>
|
||||
<td>${new Date(item.payment_date).toLocaleDateString()}</td>
|
||||
<td>${status}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-info me-1" onclick="viewDetail('${item.user_id}')">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning me-1" onclick="editRecord('${item.id}')">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" onclick="deleteRecord('${item.id}')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
// 全选/取消全选
|
||||
$('#selectAll').change(function() {
|
||||
$('.rowCheck').prop('checked', this.checked);
|
||||
});
|
||||
|
||||
// 查看详情
|
||||
function viewDetail(id) {
|
||||
window.location.href = `{{ url_for('user_detail') }}?id=${id}`;
|
||||
}
|
||||
|
||||
// 编辑记录
|
||||
function editRecord(id) {
|
||||
window.location.href = `{{ url_for('revise_page') }}?id=${id}`;
|
||||
}
|
||||
|
||||
// 删除记录
|
||||
function deleteRecord(id) {
|
||||
//console.log(id);
|
||||
if (confirm('确定要删除这条记录吗?')) {
|
||||
$.ajax({
|
||||
url: `/api/dues/${id}`,
|
||||
method: 'DELETE',
|
||||
success: () => {
|
||||
alert('删除成功');
|
||||
loadData();
|
||||
},
|
||||
error: () => alert('删除失败')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 删除选中记录
|
||||
function deleteSelected() {
|
||||
const selected = $('.rowCheck:checked').length;
|
||||
if (selected === 0) {
|
||||
alert('请选择要删除的记录');
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm(`确定要删除选中的${selected}条记录吗?`)) {
|
||||
const ids = [];
|
||||
$('.rowCheck:checked').each(function() {
|
||||
ids.push($(this).closest('tr').data('id'));
|
||||
});
|
||||
|
||||
// 批量删除
|
||||
Promise.all(ids.map(id =>
|
||||
$.ajax({
|
||||
url: `/api/dues/${id}`,
|
||||
method: 'DELETE'
|
||||
})
|
||||
)).then(() => {
|
||||
alert('删除成功');
|
||||
loadData();
|
||||
}).catch(() => alert('删除失败'));
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑选中记录
|
||||
function editSelected() {
|
||||
const selected = $('.rowCheck:checked');
|
||||
if (selected.length !== 1) {
|
||||
alert('请选择一条记录进行编辑');
|
||||
return;
|
||||
}
|
||||
const id = selected.closest('tr').data('id');
|
||||
editRecord(id);
|
||||
}
|
||||
|
||||
// 搜索记录
|
||||
function searchRecords() {
|
||||
const keyword = $('#searchInput').val().toLowerCase();
|
||||
if (!keyword) {
|
||||
renderTable(currentData);
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = currentData.filter(item =>
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.user_id.toString().includes(keyword)
|
||||
);
|
||||
renderTable(filtered);
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
function logout() {
|
||||
if (confirm('确定要退出登录吗?')) {
|
||||
window.location.href = '/logout';
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时初始化数据
|
||||
$(document).ready(() => loadData());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,174 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>党费管理系统 - 仪表板</title>
|
||||
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.sidebar { /* 调整为与导航栏协调的深蓝色 */
|
||||
min-height: 100vh;
|
||||
background-color: #0d6efd; /* 与导航栏同色 */
|
||||
padding-top: 20px;
|
||||
}
|
||||
.sidebar .nav-link {
|
||||
color: #fff;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.sidebar .nav-link:hover {
|
||||
background-color: #495057;
|
||||
}
|
||||
.sidebar .nav-link.active {
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
.main-content {
|
||||
padding: 20px;
|
||||
}
|
||||
.stat-card { /* 优化卡片样式 */
|
||||
border-radius: 12px; /* 更大圆角 */
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1); /* 更明显阴影 */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- 侧边栏 -->
|
||||
<div class="col-md-2 sidebar">
|
||||
<h3 class="text-white text-center mb-4">党费管理系统</h3>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#">
|
||||
<i class="fas fa-home me-2"></i>首页
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<i class="fas fa-users me-2"></i>学生管理
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<i class="fas fa-money-bill me-2"></i>党费管理
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<i class="fas fa-cog me-2"></i>系统设置
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-5">
|
||||
<a class="nav-link" href="#" id="logoutBtn">
|
||||
<i class="fas fa-sign-out-alt me-2"></i>退出登录
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<div class="col-md-10 main-content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>仪表板</h2>
|
||||
<div class="user-info">
|
||||
<span id="username"></span>
|
||||
<span class="ms-2">|</span>
|
||||
<span id="role" class="ms-2"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card bg-primary text-white">
|
||||
<h3>总学生数</h3>
|
||||
<h2 id="totalStudents">0</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card bg-success text-white">
|
||||
<h3>本月缴费</h3>
|
||||
<h2 id="monthlyPayment">¥0</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card bg-warning text-white">
|
||||
<h3>待缴费</h3>
|
||||
<h2 id="pendingPayment">¥0</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card bg-info text-white">
|
||||
<h3>已缴费</h3>
|
||||
<h2 id="paidAmount">¥0</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近活动 -->
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">最近活动</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>学生</th>
|
||||
<th>活动</th>
|
||||
<th>金额</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="recentActivities">
|
||||
<!-- 动态填充数据 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// 检查登录状态
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
window.location.href = '/login';
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
$.ajax({
|
||||
url: '/api/user',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
success: function(response) {
|
||||
$('#username').text(response.username);
|
||||
$('#role').text(response.role === 'admin' ? '管理员' : '普通用户');
|
||||
},
|
||||
error: function() {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
});
|
||||
|
||||
// 退出登录
|
||||
$('#logoutBtn').click(function(e) {
|
||||
e.preventDefault();
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,154 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- 声明文档类型为HTML5 -->
|
||||
<html lang="zh-CN">
|
||||
<!-- 指定文档语言为中文 -->
|
||||
<head>
|
||||
<!-- 基础元信息设置 -->
|
||||
<meta charset="UTF-8">
|
||||
<!-- 设置字符编码为UTF-8 -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- 响应式视口设置 -->
|
||||
<title>党费管理系统 - 登录</title>
|
||||
<!-- 页面标题 -->
|
||||
|
||||
<!-- 引入Bootstrap 5 CSS框架 -->
|
||||
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- 自定义样式 -->
|
||||
<style>
|
||||
/* 页面整体样式 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); /* 渐变背景 */
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* 登录容器样式 */
|
||||
.login-container {
|
||||
max-width: 400px;
|
||||
margin: 100px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
/* 系统标题样式 */
|
||||
.login-title {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
/* 表单标签样式 */
|
||||
.form-label {
|
||||
font-weight: 500; /* 中等字重标签 */
|
||||
}
|
||||
/* 登录按钮样式 */
|
||||
.btn-primary {
|
||||
width: 100%;
|
||||
background-color: #0d6efd; /* 统一使用Bootstrap主蓝色 */
|
||||
border: none;
|
||||
padding: 12px;
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* 添加轻微阴影 */
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: #0b5ed7; /* 更柔和的悬停色 */
|
||||
color: white;
|
||||
transform: translateY(-1px); /* 悬停微抬效果 */
|
||||
}
|
||||
/* 错误消息样式 */
|
||||
.error-msg {
|
||||
color: #dc3545; /* 错误提示红色 */
|
||||
font-size: 0.9em; /* 较小字号 */
|
||||
margin-top: 5px; /* 顶部边距 */
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="login-container">
|
||||
<h2 class="login-title">党费管理系统</h2>
|
||||
<form id="loginForm">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">用户名</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">密码</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">登录</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('register_page') }}'">注册</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引入jQuery库 -->
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
|
||||
|
||||
<script>
|
||||
// 页面加载完成后执行
|
||||
$(document).ready(function() {
|
||||
// 表单提交处理
|
||||
$('#loginForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// 清除之前的错误信息
|
||||
$('.error-msg').hide();
|
||||
|
||||
// 表单验证
|
||||
let isValid = true;
|
||||
const username = $('#username').val().trim();
|
||||
const password = $('#password').val().trim();
|
||||
|
||||
if (!username) {
|
||||
$('#usernameError').text('请输入用户名').show();
|
||||
isValid = false;
|
||||
}
|
||||
if (!password) {
|
||||
$('#passwordError').text('请输入密码').show();
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!isValid) return;
|
||||
|
||||
// 发送登录请求
|
||||
$.ajax({
|
||||
url: '/login',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
username: username,
|
||||
password: password
|
||||
}),
|
||||
success: function(response) {
|
||||
if (response.status === 'success') {
|
||||
// 登录成功,根据角色跳转
|
||||
if (response.role === 'admin') {
|
||||
window.location.href = '/admin_main';
|
||||
} else {
|
||||
window.location.href = '/user_main';
|
||||
}
|
||||
} else {
|
||||
$('#generalError').text(response.message).show();
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
$('#generalError').text('登录失败,请稍后重试').show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 输入框获得焦点时清除错误信息
|
||||
$('input').on('focus', function() {
|
||||
$(this).siblings('.error-msg').hide();
|
||||
$('#generalError').hide();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,259 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>统计分析</title>
|
||||
<!-- 引入Bootstrap -->
|
||||
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- 引入Chart.js -->
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
|
||||
<!-- 引入jQuery -->
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
|
||||
<style>
|
||||
.card { margin-bottom: 20px; }
|
||||
.stats-value { font-size: 1.2em; font-weight: bold; }
|
||||
.chart-container { position: relative; height: 300px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 主容器 -->
|
||||
<div class="container mt-4">
|
||||
<!-- 时间范围选择 -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">时间范围选择</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label>开始日期</label>
|
||||
<input type="date" id="startDate" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label>结束日期</label>
|
||||
<input type="date" id="endDate" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label> </label>
|
||||
<button class="btn btn-primary w-100" onclick="calculateStats()">统计</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- 基础统计卡片 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">基础统计</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p>总记录数:<span class="stats-value" id="totalCount">-</span></p>
|
||||
<p>总金额:<span class="stats-value" id="totalAmount">-</span></p>
|
||||
<p>平均金额:<span class="stats-value" id="avgAmount">-</span></p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p>已交齐人数:<span class="stats-value" id="paidCount">-</span></p>
|
||||
<p>未交齐人数:<span class="stats-value" id="unpaidCount">-</span></p>
|
||||
<p>缴纳率:<span class="stats-value" id="paymentRate">-</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 未缴纳统计卡片 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">未缴纳人员统计</div>
|
||||
<div class="card-body">
|
||||
<button class="btn btn-danger mb-3" onclick="getUnpaidList()">统计未缴纳人员</button>
|
||||
<div id="unpaidList" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- 按单位统计图表 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">按单位统计</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container">
|
||||
<canvas id="unitChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 按月统计图表 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">按月统计</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container">
|
||||
<canvas id="monthlyChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 图表实例
|
||||
let unitChart = null;
|
||||
let monthlyChart = null;
|
||||
|
||||
// 格式化金额
|
||||
function formatAmount(amount) {
|
||||
return '¥' + amount.toFixed(2);
|
||||
}
|
||||
|
||||
// 时间范围统计函数
|
||||
function calculateStats() {
|
||||
const start = $('#startDate').val();
|
||||
const end = $('#endDate').val();
|
||||
|
||||
if (!start || !end) {
|
||||
alert('请选择开始和结束日期');
|
||||
return;
|
||||
}
|
||||
|
||||
// 请求统计接口
|
||||
$.get(`/api/stats?start=${start}&end=${end}`)
|
||||
.done(data => {
|
||||
// 更新基础统计
|
||||
const basic = data.basic;
|
||||
$('#totalCount').text(basic.total_count);
|
||||
$('#totalAmount').text(formatAmount(basic.total_amount));
|
||||
$('#avgAmount').text(formatAmount(basic.avg_amount));
|
||||
$('#paidCount').text(basic.paid_count);
|
||||
$('#unpaidCount').text(basic.unpaid_count);
|
||||
$('#paymentRate').text(((basic.paid_count / basic.total_count) * 100).toFixed(1) + '%');
|
||||
|
||||
// 更新单位统计图表
|
||||
updateUnitChart(data.by_unit);
|
||||
|
||||
// 更新月度统计图表
|
||||
updateMonthlyChart(data.by_month);
|
||||
})
|
||||
.fail(() => {
|
||||
alert('获取统计数据失败');
|
||||
});
|
||||
}
|
||||
|
||||
// 更新单位统计图表
|
||||
function updateUnitChart(unitData) {
|
||||
const ctx = document.getElementById('unitChart').getContext('2d');
|
||||
|
||||
if (unitChart) {
|
||||
unitChart.destroy();
|
||||
}
|
||||
|
||||
unitChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: unitData.map(item => item.unit),
|
||||
datasets: [{
|
||||
label: '缴纳金额',
|
||||
data: unitData.map(item => item.total_amount),
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.5)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return '¥' + value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新月度统计图表
|
||||
function updateMonthlyChart(monthlyData) {
|
||||
const ctx = document.getElementById('monthlyChart').getContext('2d');
|
||||
|
||||
if (monthlyChart) {
|
||||
monthlyChart.destroy();
|
||||
}
|
||||
|
||||
monthlyChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: monthlyData.map(item => item.month),
|
||||
datasets: [{
|
||||
label: '缴纳金额',
|
||||
data: monthlyData.map(item => item.total_amount),
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
tension: 0.1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return '¥' + value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 未缴纳人员统计
|
||||
function getUnpaidList() {
|
||||
$.get('/api/unpaid')
|
||||
.done(data => {
|
||||
let html = `<p>未缴纳人数: ${data.count}</p><ul class="list-group">`;
|
||||
data.users.forEach(user => {
|
||||
html += `<li class="list-group-item">
|
||||
${user.name} (${user.id}) - ${user.unit}
|
||||
</li>`;
|
||||
});
|
||||
html += '</ul>';
|
||||
$('#unpaidList').html(html);
|
||||
})
|
||||
.fail(() => {
|
||||
alert('获取未缴纳人员列表失败');
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载时设置默认日期范围(当前月份)
|
||||
$(document).ready(function() {
|
||||
const now = new Date();
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
$('#startDate').val(firstDay.toISOString().split('T')[0]);
|
||||
$('#endDate').val(lastDay.toISOString().split('T')[0]);
|
||||
|
||||
// 自动加载统计数据
|
||||
calculateStats();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,278 @@
|
||||
{
|
||||
"total_issues": 29,
|
||||
"error_count": 0,
|
||||
"warning_count": 29,
|
||||
"info_count": 0,
|
||||
"tools_status": {
|
||||
"pylint": "completed",
|
||||
"flake8": "completed",
|
||||
"bandit": "completed"
|
||||
},
|
||||
"all_issues": [
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 15,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Line too long (153/100)",
|
||||
"rule": "C0301",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 48,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Trailing whitespace",
|
||||
"rule": "C0303",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 51,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Trailing whitespace",
|
||||
"rule": "C0303",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 15,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Constant name \"very_long_variable_name_that_exceeds_the_recommended_line_length_limit\" doesn't conform to UPPER_CASE naming style",
|
||||
"rule": "C0103",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 38,
|
||||
"column": 13,
|
||||
"type": "warning",
|
||||
"message": "Use of eval",
|
||||
"rule": "W0123",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 52,
|
||||
"column": 4,
|
||||
"type": "warning",
|
||||
"message": "Missing function or method docstring",
|
||||
"rule": "C0116",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 46,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Too few public methods (1/2)",
|
||||
"rule": "R0903",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 8,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Unused import os",
|
||||
"rule": "W0611",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 9,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Unused import sys",
|
||||
"rule": "W0611",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Unused import subprocess",
|
||||
"rule": "W0611",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 12,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Unused urlparse imported from urllib.parse",
|
||||
"rule": "W0611",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 8,
|
||||
"type": "warning",
|
||||
"message": "1: F401 'os' imported but unused",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 9,
|
||||
"type": "warning",
|
||||
"message": "1: F401 'sys' imported but unused",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 11,
|
||||
"type": "warning",
|
||||
"message": "1: F401 'subprocess' imported but unused",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 12,
|
||||
"type": "warning",
|
||||
"message": "1: F401 'urllib.parse.urlparse' imported but unused",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 15,
|
||||
"type": "warning",
|
||||
"message": "80: E501 line too long (153 > 79 characters)",
|
||||
"rule": "80:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 17,
|
||||
"type": "warning",
|
||||
"message": "1: E302 expected 2 blank lines, found 1",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 25,
|
||||
"type": "warning",
|
||||
"message": "1: E302 expected 2 blank lines, found 1",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 31,
|
||||
"type": "warning",
|
||||
"message": "1: E302 expected 2 blank lines, found 1",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 35,
|
||||
"type": "warning",
|
||||
"message": "1: E302 expected 2 blank lines, found 1",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 41,
|
||||
"type": "warning",
|
||||
"message": "1: E302 expected 2 blank lines, found 1",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 46,
|
||||
"type": "warning",
|
||||
"message": "1: E302 expected 2 blank lines, found 1",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 48,
|
||||
"type": "warning",
|
||||
"message": "1: W293 blank line contains whitespace",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 51,
|
||||
"type": "warning",
|
||||
"message": "1: W293 blank line contains whitespace",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C",
|
||||
"line": 0,
|
||||
"column": 56,
|
||||
"type": "warning",
|
||||
"message": "1: E305 expected 2 blank lines after class or function definition, found 1",
|
||||
"rule": "1:",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Consider possible security implications associated with the subprocess module.",
|
||||
"rule": "B404",
|
||||
"severity": "low",
|
||||
"confidence": "HIGH"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 21,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Possible SQL injection vector through string-based query construction.",
|
||||
"rule": "B608",
|
||||
"severity": "medium",
|
||||
"confidence": "LOW"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 38,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Use of possibly insecure function - consider using safer ast.literal_eval.",
|
||||
"rule": "B307",
|
||||
"severity": "medium",
|
||||
"confidence": "HIGH"
|
||||
},
|
||||
{
|
||||
"file": "C:\\Users\\51736\\AppData\\Local\\Temp\\tmpj1y1hh_8\\test_sample.py",
|
||||
"line": 43,
|
||||
"column": 0,
|
||||
"type": "warning",
|
||||
"message": "Possible hardcoded password: 'admin123'",
|
||||
"rule": "B105",
|
||||
"severity": "low",
|
||||
"confidence": "MEDIUM"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,5 @@
|
||||
Flask==2.3.3
|
||||
Flask-CORS==4.0.0
|
||||
PyMySQL==1.1.0
|
||||
GitPython==3.1.40
|
||||
Werkzeug==2.3.7
|
||||
@ -0,0 +1,361 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>基于多agent协同的军工Python代码合规性检查系统 - FortifyCode</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 顶部导航栏 -->
|
||||
<nav class="navbar">
|
||||
<div class="navbar-content">
|
||||
<div class="logo">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
FortifyCode
|
||||
</div>
|
||||
<ul class="nav-menu">
|
||||
<li><a href="#dashboard" class="nav-link active">仪表板</a></li>
|
||||
<li><a href="#projects" class="nav-link">项目管理</a></li>
|
||||
<li><a href="#rules" class="nav-link">规则集管理</a></li>
|
||||
<li><a href="#settings" class="nav-link">配置中心</a></li>
|
||||
</ul>
|
||||
<div class="user-info">
|
||||
<div class="notification">
|
||||
<i class="fas fa-bell"></i>
|
||||
<span class="notification-badge">3</span>
|
||||
</div>
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-circle" style="font-size: 24px;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<div class="main-content">
|
||||
<!-- 仪表板页面 -->
|
||||
<div id="dashboard-page" class="page-content active">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header fade-in">
|
||||
<h1 class="page-title">基于多agent协同的军工Python代码合规性检查系统</h1>
|
||||
<p class="page-subtitle">基于多 Agent 协同的智能代码安全检测平台</p>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-grid fade-in">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon primary">
|
||||
<i class="fas fa-code"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<h3 id="totalFiles">1,247</h3>
|
||||
<p>已检查文件</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon success">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<h3 id="complianceRate">98.5%</h3>
|
||||
<p>合规率</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<h3 id="pendingIssues">23</h3>
|
||||
<p>待修复问题</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon danger">
|
||||
<i class="fas fa-bug"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<h3 id="highRiskIssues">5</h3>
|
||||
<p>高危漏洞</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 功能区域 -->
|
||||
<div class="function-grid fade-in">
|
||||
<!-- 代码上传和检查区域 -->
|
||||
<div class="main-panel">
|
||||
<div class="panel-header">
|
||||
<h2 class="panel-title">快捷代码检查</h2>
|
||||
<div class="filter-tabs">
|
||||
<span class="filter-tab active">Python</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<div class="upload-area" id="uploadArea">
|
||||
<div class="upload-icon">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
</div>
|
||||
<div class="upload-text">拖拽文件到此处或点击上传</div>
|
||||
<div class="upload-hint">支持 .py, .pyx, .pyi 文件,最大 100MB</div>
|
||||
<input type="file" class="file-input" id="fileInput" multiple accept=".py,.pyx,.pyi">
|
||||
</div>
|
||||
|
||||
<div class="progress-container" id="progressContainer" style="display: none;">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="progressFill" style="width: 0%;"></div>
|
||||
</div>
|
||||
<div class="progress-text" id="progressText">准备检查...</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px; text-align: center;">
|
||||
<button class="btn btn-primary" id="startCheckBtn" style="display: none;">
|
||||
<i class="fas fa-play"></i> 开始检查
|
||||
</button>
|
||||
<button class="btn btn-secondary" id="stopCheckBtn" style="display: none;">
|
||||
<i class="fas fa-stop"></i> 停止检查
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 多Agent状态面板 -->
|
||||
<div class="agent-status">
|
||||
<div class="panel-header">
|
||||
<h2 class="panel-title">Agent 状态</h2>
|
||||
</div>
|
||||
<div class="agent-list">
|
||||
<div class="agent-item">
|
||||
<div class="agent-avatar">A1</div>
|
||||
<div class="agent-info">
|
||||
<div class="agent-name">pylint Agent</div>
|
||||
<span class="agent-status-badge status-idle">空闲</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="agent-item">
|
||||
<div class="agent-avatar">A2</div>
|
||||
<div class="agent-info">
|
||||
<div class="agent-name">flake8 Agent</div>
|
||||
<span class="agent-status-badge status-idle">空闲</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="agent-item">
|
||||
<div class="agent-avatar">A3</div>
|
||||
<div class="agent-info">
|
||||
<div class="agent-name">bandit Agent</div>
|
||||
<span class="agent-status-badge status-idle">空闲</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检查结果区域 -->
|
||||
<div class="results-section fade-in" id="resultsSection" style="display: none;">
|
||||
<div class="results-header">
|
||||
<h2 class="results-title">检查结果</h2>
|
||||
<div class="filter-tabs">
|
||||
<span class="filter-tab active" data-filter="all">全部</span>
|
||||
<span class="filter-tab" data-filter="error">错误</span>
|
||||
<span class="filter-tab" data-filter="warning">警告</span>
|
||||
<span class="filter-tab" data-filter="info">信息</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="results-content" id="resultsContent">
|
||||
<!-- 结果项将通过 JavaScript 动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 项目管理页面 -->
|
||||
<div id="projects-page" class="page-content">
|
||||
<div class="page-header fade-in">
|
||||
<h1 class="page-title">项目管理</h1>
|
||||
<p class="page-subtitle">管理您的代码检查项目和任务</p>
|
||||
</div>
|
||||
|
||||
<div class="projects-container">
|
||||
<div class="projects-header">
|
||||
<div class="projects-actions">
|
||||
<button class="btn btn-primary" id="createProjectBtn">
|
||||
<i class="fas fa-plus"></i> 新建项目
|
||||
</button>
|
||||
</div>
|
||||
<div class="projects-search">
|
||||
<input type="text" placeholder="搜索项目..." class="search-input" id="projectSearch">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="projects-grid" id="projectsGrid">
|
||||
<!-- 项目卡片将通过 JavaScript 动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 项目详情页面 -->
|
||||
<div id="project-detail-page" class="page-content">
|
||||
<div class="page-header fade-in">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<button class="btn btn-secondary" id="backToProjectsBtn">
|
||||
<i class="fas fa-arrow-left"></i> 返回项目列表
|
||||
</button>
|
||||
<div>
|
||||
<h1 class="page-title" id="projectDetailTitle">项目详情</h1>
|
||||
<p class="page-subtitle" id="projectDetailSubtitle">查看项目信息和运行代码检查</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-detail-container">
|
||||
<div class="project-info-panel">
|
||||
<div class="panel-header">
|
||||
<h2 class="panel-title">项目信息</h2>
|
||||
<div class="project-actions">
|
||||
<button class="btn btn-primary" id="runCheckBtn">
|
||||
<i class="fas fa-play"></i> 运行检查
|
||||
</button>
|
||||
<button class="btn btn-secondary" id="editProjectBtn">
|
||||
<i class="fas fa-edit"></i> 编辑项目
|
||||
</button>
|
||||
<button class="btn btn-danger" id="deleteProjectBtn">
|
||||
<i class="fas fa-trash"></i> 删除项目
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content" id="projectInfoContent">
|
||||
<!-- 项目信息将通过 JavaScript 动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="check-history-panel">
|
||||
<div class="panel-header">
|
||||
<h2 class="panel-title">检查历史</h2>
|
||||
</div>
|
||||
<div class="panel-content" id="checkHistoryContent">
|
||||
<!-- 检查历史将通过 JavaScript 动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件浏览器面板 -->
|
||||
<div class="file-browser-panel">
|
||||
<div class="panel-header">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<h2 class="panel-title">文件浏览器</h2>
|
||||
<div class="file-actions">
|
||||
<button class="btn btn-primary" id="uploadFileBtn">
|
||||
<i class="fas fa-upload"></i> 上传文件
|
||||
</button>
|
||||
<button class="btn btn-secondary" id="createFileBtn">
|
||||
<i class="fas fa-plus"></i> 新建文件
|
||||
</button>
|
||||
<button class="btn btn-secondary" id="createFolderBtn">
|
||||
<i class="fas fa-folder-plus"></i> 新建文件夹
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-path">
|
||||
<span id="currentPath">/</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-browser-content">
|
||||
<div class="file-tree" id="fileTree">
|
||||
<!-- 文件树将通过 JavaScript 动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 代码编辑器面板 -->
|
||||
<div class="code-editor-panel" id="codeEditorPanel" style="display: none;">
|
||||
<div class="panel-header">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<h2 class="panel-title" id="editorTitle">代码编辑器</h2>
|
||||
<div class="editor-actions">
|
||||
<button class="btn btn-success" id="saveFileBtn">
|
||||
<i class="fas fa-save"></i> 保存
|
||||
</button>
|
||||
<button class="btn btn-secondary" id="closeEditorBtn">
|
||||
<i class="fas fa-times"></i> 关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-info">
|
||||
<span id="currentFilePath">未选择文件</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-content">
|
||||
<textarea id="codeEditor" placeholder="选择文件开始编辑..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检查结果详情 -->
|
||||
<div class="check-results-panel" id="checkResultsPanel" style="display: none;">
|
||||
<div class="panel-header">
|
||||
<h2 class="panel-title">检查结果详情</h2>
|
||||
<div class="filter-tabs">
|
||||
<span class="filter-tab active" data-filter="all">全部</span>
|
||||
<span class="filter-tab" data-filter="error">错误</span>
|
||||
<span class="filter-tab" data-filter="warning">警告</span>
|
||||
<span class="filter-tab" data-filter="info">信息</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content" id="checkResultsContent">
|
||||
<!-- 检查结果将通过 JavaScript 动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新建项目模态框 -->
|
||||
<div id="createProjectModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>新建项目</h2>
|
||||
<span class="close">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="createProjectForm">
|
||||
<div class="form-group">
|
||||
<label for="projectName">项目名称 *</label>
|
||||
<input type="text" id="projectName" name="name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="projectDescription">项目描述</label>
|
||||
<textarea id="projectDescription" name="description" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>项目来源 *</label>
|
||||
<div class="source-type-tabs">
|
||||
<input type="radio" id="sourceGithub" name="source_type" value="github" checked>
|
||||
<label for="sourceGithub">GitHub</label>
|
||||
|
||||
<input type="radio" id="sourceGitee" name="source_type" value="gitee">
|
||||
<label for="sourceGitee">Gitee</label>
|
||||
|
||||
<input type="radio" id="sourceUpload" name="source_type" value="upload">
|
||||
<label for="sourceUpload">文件上传</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="gitUrlGroup">
|
||||
<label for="gitUrl">Git URL *</label>
|
||||
<input type="url" id="gitUrl" name="source_url" placeholder="https://github.com/username/repository.git">
|
||||
</div>
|
||||
<div class="form-group" id="fileUploadGroup" style="display: none;">
|
||||
<label for="fileUpload">选择文件或文件夹</label>
|
||||
<input type="file" id="fileUpload" name="files" multiple webkitdirectory>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="cancelCreateProject">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmCreateProject">创建项目</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
||||
{
|
||||
"useLocalAi": false,
|
||||
"selectedModelId": null
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "model_1763556444768_81a9s6dh0",
|
||||
"name": "qwen:7b",
|
||||
"description": "测试",
|
||||
"apiUrl": "http://localhost:11434/v1",
|
||||
"createdAt": "2025-11-19T12:47:24.768Z",
|
||||
"filePath": null
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,202 +0,0 @@
|
||||
const API_BASE_URL = window.location.origin + '/api';
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
function showSuccessMessage(message) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.style.cssText = `position: fixed; top: 20px; right: 20px; background: #d4edda; color: #155724; border: 1px solid #c3e6cb; border-radius: 6px; padding: 15px 20px; z-index: 3000; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 10px;`;
|
||||
messageDiv.innerHTML = `<i class="fas fa-check-circle"></i><span>${message}</span>`;
|
||||
document.body.appendChild(messageDiv);
|
||||
setTimeout(() => messageDiv.remove(), 3000);
|
||||
}
|
||||
|
||||
function showErrorMessage(message) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.style.cssText = `position: fixed; top: 20px; right: 20px; background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; border-radius: 6px; padding: 15px 20px; z-index: 3000; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 10px;`;
|
||||
messageDiv.innerHTML = `<i class="fas fa-exclamation-circle"></i><span>${message}</span>`;
|
||||
document.body.appendChild(messageDiv);
|
||||
setTimeout(() => messageDiv.remove(), 5000);
|
||||
}
|
||||
|
||||
async function loadRules() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/rules`);
|
||||
const data = await response.json();
|
||||
if (!data.success) throw new Error(data.error || '加载失败');
|
||||
renderRulesList(data.data || []);
|
||||
} catch (e) {
|
||||
console.error('加载规则失败:', e);
|
||||
showErrorMessage('加载规则失败');
|
||||
}
|
||||
}
|
||||
|
||||
function renderRulesList(files) {
|
||||
const container = document.getElementById('rulesList');
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
if (!files.length) {
|
||||
container.innerHTML = '<div style="text-align:center; padding: 20px; color:#666;">暂无规则文件</div>';
|
||||
return;
|
||||
}
|
||||
files.forEach(f => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'project-card';
|
||||
const size = formatFileSize(f.size || 0);
|
||||
card.innerHTML = `
|
||||
<div class="project-header">
|
||||
<div>
|
||||
<div class="project-title">${f.name}</div>
|
||||
<div class="project-description">大小: ${size} | 更新: ${new Date(f.modified_at).toLocaleString()}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-actions">
|
||||
<button class="btn btn-secondary">编辑</button>
|
||||
<button class="btn btn-primary">下载</button>
|
||||
<button class="btn btn-danger">删除</button>
|
||||
</div>
|
||||
`;
|
||||
const [editBtn, downloadBtn, deleteBtn] = card.querySelectorAll('.project-actions .btn');
|
||||
editBtn.addEventListener('click', (e) => { e.stopPropagation(); editRule(f.name); });
|
||||
downloadBtn.addEventListener('click', (e) => { e.stopPropagation(); downloadRule(f.name); });
|
||||
deleteBtn.addEventListener('click', (e) => { e.stopPropagation(); deleteRule(f.name); });
|
||||
container.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function showRuleUploadDialog() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.multiple = true;
|
||||
input.accept = '.cfg,.ini,.toml,.yaml,.yml,.json,.flake8,.pylintrc,setup.cfg,pyproject.toml,tox.ini';
|
||||
input.onchange = async (e) => {
|
||||
const files = Array.from(e.target.files || []);
|
||||
if (!files.length) return;
|
||||
const formData = new FormData();
|
||||
files.forEach(f => formData.append('files', f));
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE_URL}/rules/upload`, { method: 'POST', body: formData });
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
showSuccessMessage('规则上传成功');
|
||||
loadRules();
|
||||
} else {
|
||||
showErrorMessage('规则上传失败:' + (data.error || ''));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('上传规则失败:', e);
|
||||
showErrorMessage('上传规则失败');
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
|
||||
let currentEditingRule = '';
|
||||
|
||||
async function editRule(name) {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(name)}`);
|
||||
const data = await resp.json();
|
||||
if (!data.success) throw new Error(data.error || '读取失败');
|
||||
currentEditingRule = name;
|
||||
document.getElementById('editRuleTitle').textContent = `编辑规则 - ${name}`;
|
||||
document.getElementById('editRuleTextarea').value = data.data.content || '';
|
||||
document.getElementById('editRuleModal').style.display = 'block';
|
||||
} catch (e) {
|
||||
console.error('读取规则失败:', e);
|
||||
showErrorMessage('读取规则失败');
|
||||
}
|
||||
}
|
||||
|
||||
function hideEditRuleModal() {
|
||||
const modal = document.getElementById('editRuleModal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
currentEditingRule = '';
|
||||
}
|
||||
|
||||
async function saveEditingRule() {
|
||||
if (!currentEditingRule) return;
|
||||
const content = document.getElementById('editRuleTextarea').value;
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(currentEditingRule)}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content })
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
showSuccessMessage('保存成功');
|
||||
hideEditRuleModal();
|
||||
loadRules();
|
||||
} else {
|
||||
showErrorMessage('保存失败:' + (data.error || ''));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('保存规则失败:', e);
|
||||
showErrorMessage('保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteRule(name) {
|
||||
if (!confirm(`确定删除规则文件 "${name}" 吗?`)) return;
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
showSuccessMessage('删除成功');
|
||||
loadRules();
|
||||
} else {
|
||||
showErrorMessage('删除失败:' + (data.error || ''));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('删除规则失败:', e);
|
||||
showErrorMessage('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadRule(name) {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(name)}`);
|
||||
const data = await resp.json();
|
||||
if (!data.success) throw new Error(data.error || '下载失败');
|
||||
const blob = new Blob([data.data.content || ''], { type: 'text/plain;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = name;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
a.remove();
|
||||
} catch (e) {
|
||||
console.error('下载规则失败:', e);
|
||||
showErrorMessage('下载失败');
|
||||
}
|
||||
}
|
||||
|
||||
function bindRulesEvents() {
|
||||
const uploadRuleBtn = document.getElementById('uploadRuleBtn');
|
||||
const refreshRuleBtn = document.getElementById('refreshRuleBtn');
|
||||
const closeEditRule = document.getElementById('closeEditRule');
|
||||
const cancelEditRule = document.getElementById('cancelEditRule');
|
||||
const saveRuleBtn = document.getElementById('saveRuleBtn');
|
||||
|
||||
if (uploadRuleBtn) uploadRuleBtn.addEventListener('click', showRuleUploadDialog);
|
||||
if (refreshRuleBtn) refreshRuleBtn.addEventListener('click', loadRules);
|
||||
if (closeEditRule) closeEditRule.addEventListener('click', hideEditRuleModal);
|
||||
if (cancelEditRule) cancelEditRule.addEventListener('click', hideEditRuleModal);
|
||||
if (saveRuleBtn) saveRuleBtn.addEventListener('click', saveEditingRule);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
bindRulesEvents();
|
||||
});
|
||||
|
||||
export { loadRules };
|
||||
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
# 代码质量检查报告
|
||||
|
||||
**文件:** test_sample.py
|
||||
**检查时间:** 2025/9/18 14:28:54
|
||||
|
||||
## flake8 检查结果
|
||||
### 输出:
|
||||
```
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758176934520.py:3:1: E302 expected 2 blank lines, found 1
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758176934520.py:5:37: E261 at least two spaces before inline comment
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758176934520.py:7:1: E305 expected 2 blank lines after class or function definition, found 1
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758176934520.py:7:12: E225 missing whitespace around operator
|
||||
```
|
||||
|
||||
### 解析结果:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758176934520.py",
|
||||
"line": 3,
|
||||
"column": 1,
|
||||
"code": "E302",
|
||||
"message": "expected 2 blank lines, found 1"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758176934520.py",
|
||||
"line": 5,
|
||||
"column": 37,
|
||||
"code": "E261",
|
||||
"message": "at least two spaces before inline comment"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758176934520.py",
|
||||
"line": 7,
|
||||
"column": 1,
|
||||
"code": "E305",
|
||||
"message": "expected 2 blank lines after class or function definition, found 1"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758176934520.py",
|
||||
"line": 7,
|
||||
"column": 12,
|
||||
"code": "E225",
|
||||
"message": "missing whitespace around operator"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
@ -0,0 +1,370 @@
|
||||
# 代码质量检查报告
|
||||
|
||||
**文件:** test_sample.py
|
||||
**检查时间:** 2025/9/18 14:33:21
|
||||
|
||||
## bandit 检查结果
|
||||
### 输出:
|
||||
```
|
||||
{
|
||||
"errors": [],
|
||||
"generated_at": "2025-09-18T06:33:19Z",
|
||||
"metrics": {
|
||||
"C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
},
|
||||
"_totals": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"code": "3 def bad_function():\n4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 34,
|
||||
"filename": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "MEDIUM",
|
||||
"issue_text": "Use of exec detected.",
|
||||
"line_number": 4,
|
||||
"line_range": [
|
||||
4
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b102_exec_used.html",
|
||||
"test_id": "B102",
|
||||
"test_name": "exec_used"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a shell: Seems safe, but may be changed in the future, consider rewriting without shell",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b605_start_process_with_a_shell.html",
|
||||
"test_id": "B605",
|
||||
"test_name": "start_process_with_a_shell"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a partial executable path",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b607_start_process_with_partial_path.html",
|
||||
"test_id": "B607",
|
||||
"test_name": "start_process_with_partial_path"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 错误:
|
||||
```
|
||||
[main] INFO profile include tests: None
|
||||
[main] INFO profile exclude tests: None
|
||||
[main] INFO cli include tests: None
|
||||
[main] INFO cli exclude tests: None
|
||||
```
|
||||
|
||||
### 解析结果:
|
||||
```json
|
||||
{
|
||||
"errors": [],
|
||||
"generated_at": "2025-09-18T06:33:19Z",
|
||||
"metrics": {
|
||||
"C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
},
|
||||
"_totals": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"code": "3 def bad_function():\n4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 34,
|
||||
"filename": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "MEDIUM",
|
||||
"issue_text": "Use of exec detected.",
|
||||
"line_number": 4,
|
||||
"line_range": [
|
||||
4
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b102_exec_used.html",
|
||||
"test_id": "B102",
|
||||
"test_name": "exec_used"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a shell: Seems safe, but may be changed in the future, consider rewriting without shell",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b605_start_process_with_a_shell.html",
|
||||
"test_id": "B605",
|
||||
"test_name": "start_process_with_a_shell"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a partial executable path",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b607_start_process_with_partial_path.html",
|
||||
"test_id": "B607",
|
||||
"test_name": "start_process_with_partial_path"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## flake8 检查结果
|
||||
### 输出:
|
||||
```
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177199898.py:3:1: E302 expected 2 blank lines, found 1
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177199898.py:5:37: E261 at least two spaces before inline comment
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177199898.py:7:1: E305 expected 2 blank lines after class or function definition, found 1
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177199898.py:7:12: E225 missing whitespace around operator
|
||||
```
|
||||
|
||||
### 解析结果:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177199898.py",
|
||||
"line": 3,
|
||||
"column": 1,
|
||||
"code": "E302",
|
||||
"message": "expected 2 blank lines, found 1"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177199898.py",
|
||||
"line": 5,
|
||||
"column": 37,
|
||||
"code": "E261",
|
||||
"message": "at least two spaces before inline comment"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177199898.py",
|
||||
"line": 7,
|
||||
"column": 1,
|
||||
"code": "E305",
|
||||
"message": "expected 2 blank lines after class or function definition, found 1"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177199898.py",
|
||||
"line": 7,
|
||||
"column": 12,
|
||||
"code": "E225",
|
||||
"message": "missing whitespace around operator"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## pylint 检查结果
|
||||
### 输出:
|
||||
```
|
||||
[
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "missing-module-docstring",
|
||||
"message": "Missing module docstring",
|
||||
"message-id": "C0114"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "invalid-name",
|
||||
"message": "Module name \"1758177199344-test_sample\" doesn't conform to snake_case naming style",
|
||||
"message-id": "C0103"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 3,
|
||||
"column": 0,
|
||||
"endLine": 3,
|
||||
"endColumn": 16,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "missing-function-docstring",
|
||||
"message": "Missing function or method docstring",
|
||||
"message-id": "C0116"
|
||||
},
|
||||
{
|
||||
"type": "warning",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 4,
|
||||
"column": 4,
|
||||
"endLine": 4,
|
||||
"endColumn": 34,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "exec-used",
|
||||
"message": "Use of exec",
|
||||
"message-id": "W0122"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 解析结果:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "missing-module-docstring",
|
||||
"message": "Missing module docstring",
|
||||
"message-id": "C0114"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "invalid-name",
|
||||
"message": "Module name \"1758177199344-test_sample\" doesn't conform to snake_case naming style",
|
||||
"message-id": "C0103"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 3,
|
||||
"column": 0,
|
||||
"endLine": 3,
|
||||
"endColumn": 16,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "missing-function-docstring",
|
||||
"message": "Missing function or method docstring",
|
||||
"message-id": "C0116"
|
||||
},
|
||||
{
|
||||
"type": "warning",
|
||||
"module": "1758177199344-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 4,
|
||||
"column": 4,
|
||||
"endLine": 4,
|
||||
"endColumn": 34,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177199344-test_sample.py",
|
||||
"symbol": "exec-used",
|
||||
"message": "Use of exec",
|
||||
"message-id": "W0122"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
@ -0,0 +1,370 @@
|
||||
# 代码质量检查报告
|
||||
|
||||
**文件:** test_sample.py
|
||||
**检查时间:** 2025/9/18 14:35:53
|
||||
|
||||
## bandit 检查结果
|
||||
### 输出:
|
||||
```
|
||||
{
|
||||
"errors": [],
|
||||
"generated_at": "2025-09-18T06:35:52Z",
|
||||
"metrics": {
|
||||
"C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
},
|
||||
"_totals": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"code": "3 def bad_function():\n4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 34,
|
||||
"filename": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "MEDIUM",
|
||||
"issue_text": "Use of exec detected.",
|
||||
"line_number": 4,
|
||||
"line_range": [
|
||||
4
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b102_exec_used.html",
|
||||
"test_id": "B102",
|
||||
"test_name": "exec_used"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a shell: Seems safe, but may be changed in the future, consider rewriting without shell",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b605_start_process_with_a_shell.html",
|
||||
"test_id": "B605",
|
||||
"test_name": "start_process_with_a_shell"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a partial executable path",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b607_start_process_with_partial_path.html",
|
||||
"test_id": "B607",
|
||||
"test_name": "start_process_with_partial_path"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 错误:
|
||||
```
|
||||
[main] INFO profile include tests: None
|
||||
[main] INFO profile exclude tests: None
|
||||
[main] INFO cli include tests: None
|
||||
[main] INFO cli exclude tests: None
|
||||
```
|
||||
|
||||
### 解析结果:
|
||||
```json
|
||||
{
|
||||
"errors": [],
|
||||
"generated_at": "2025-09-18T06:35:52Z",
|
||||
"metrics": {
|
||||
"C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
},
|
||||
"_totals": {
|
||||
"CONFIDENCE.HIGH": 3,
|
||||
"CONFIDENCE.LOW": 0,
|
||||
"CONFIDENCE.MEDIUM": 0,
|
||||
"CONFIDENCE.UNDEFINED": 0,
|
||||
"SEVERITY.HIGH": 0,
|
||||
"SEVERITY.LOW": 2,
|
||||
"SEVERITY.MEDIUM": 1,
|
||||
"SEVERITY.UNDEFINED": 0,
|
||||
"loc": 6,
|
||||
"nosec": 0,
|
||||
"skipped_tests": 0
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"code": "3 def bad_function():\n4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 34,
|
||||
"filename": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "MEDIUM",
|
||||
"issue_text": "Use of exec detected.",
|
||||
"line_number": 4,
|
||||
"line_range": [
|
||||
4
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b102_exec_used.html",
|
||||
"test_id": "B102",
|
||||
"test_name": "exec_used"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a shell: Seems safe, but may be changed in the future, consider rewriting without shell",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b605_start_process_with_a_shell.html",
|
||||
"test_id": "B605",
|
||||
"test_name": "start_process_with_a_shell"
|
||||
},
|
||||
{
|
||||
"code": "4 exec(\"print('Hello, world!')\") # Potential security risk\n5 os.system(\"echo This is a test\")# Missing space after comment\n6 \n",
|
||||
"col_offset": 4,
|
||||
"end_col_offset": 36,
|
||||
"filename": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"issue_confidence": "HIGH",
|
||||
"issue_cwe": {
|
||||
"id": 78,
|
||||
"link": "https://cwe.mitre.org/data/definitions/78.html"
|
||||
},
|
||||
"issue_severity": "LOW",
|
||||
"issue_text": "Starting a process with a partial executable path",
|
||||
"line_number": 5,
|
||||
"line_range": [
|
||||
5
|
||||
],
|
||||
"more_info": "https://bandit.readthedocs.io/en/1.8.6/plugins/b607_start_process_with_partial_path.html",
|
||||
"test_id": "B607",
|
||||
"test_name": "start_process_with_partial_path"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## flake8 检查结果
|
||||
### 输出:
|
||||
```
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177352260.py:3:1: E302 expected 2 blank lines, found 1
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177352260.py:5:37: E261 at least two spaces before inline comment
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177352260.py:7:1: E305 expected 2 blank lines after class or function definition, found 1
|
||||
D:\软件工程\代码质量检查\src\temp_check_1758177352260.py:7:12: E225 missing whitespace around operator
|
||||
```
|
||||
|
||||
### 解析结果:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177352260.py",
|
||||
"line": 3,
|
||||
"column": 1,
|
||||
"code": "E302",
|
||||
"message": "expected 2 blank lines, found 1"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177352260.py",
|
||||
"line": 5,
|
||||
"column": 37,
|
||||
"code": "E261",
|
||||
"message": "at least two spaces before inline comment"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177352260.py",
|
||||
"line": 7,
|
||||
"column": 1,
|
||||
"code": "E305",
|
||||
"message": "expected 2 blank lines after class or function definition, found 1"
|
||||
},
|
||||
{
|
||||
"file": "D:\\软件工程\\代码质量检查\\src\\temp_check_1758177352260.py",
|
||||
"line": 7,
|
||||
"column": 12,
|
||||
"code": "E225",
|
||||
"message": "missing whitespace around operator"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## pylint 检查结果
|
||||
### 输出:
|
||||
```
|
||||
[
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "missing-module-docstring",
|
||||
"message": "Missing module docstring",
|
||||
"message-id": "C0114"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "invalid-name",
|
||||
"message": "Module name \"1758177351895-test_sample\" doesn't conform to snake_case naming style",
|
||||
"message-id": "C0103"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 3,
|
||||
"column": 0,
|
||||
"endLine": 3,
|
||||
"endColumn": 16,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "missing-function-docstring",
|
||||
"message": "Missing function or method docstring",
|
||||
"message-id": "C0116"
|
||||
},
|
||||
{
|
||||
"type": "warning",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 4,
|
||||
"column": 4,
|
||||
"endLine": 4,
|
||||
"endColumn": 34,
|
||||
"path": "C:\\Users\\\u5f20\u6d0b\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "exec-used",
|
||||
"message": "Use of exec",
|
||||
"message-id": "W0122"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 解析结果:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "missing-module-docstring",
|
||||
"message": "Missing module docstring",
|
||||
"message-id": "C0114"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "",
|
||||
"line": 1,
|
||||
"column": 0,
|
||||
"endLine": null,
|
||||
"endColumn": null,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "invalid-name",
|
||||
"message": "Module name \"1758177351895-test_sample\" doesn't conform to snake_case naming style",
|
||||
"message-id": "C0103"
|
||||
},
|
||||
{
|
||||
"type": "convention",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 3,
|
||||
"column": 0,
|
||||
"endLine": 3,
|
||||
"endColumn": 16,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "missing-function-docstring",
|
||||
"message": "Missing function or method docstring",
|
||||
"message-id": "C0116"
|
||||
},
|
||||
{
|
||||
"type": "warning",
|
||||
"module": "1758177351895-test_sample",
|
||||
"obj": "bad_function",
|
||||
"line": 4,
|
||||
"column": 4,
|
||||
"endLine": 4,
|
||||
"endColumn": 34,
|
||||
"path": "C:\\Users\\张洋\\AppData\\Local\\Temp\\1758177351895-test_sample.py",
|
||||
"symbol": "exec-used",
|
||||
"message": "Use of exec",
|
||||
"message-id": "W0122"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
# FortifyCode Python 依赖
|
||||
|
||||
# 代码质量检查工具
|
||||
pylint>=2.17.0
|
||||
flake8>=6.0.0
|
||||
bandit>=1.7.5
|
||||
|
||||
# 可选:增强功能
|
||||
pycodestyle>=2.10.0
|
||||
mccabe>=0.7.0
|
||||
|
||||
@ -1,125 +0,0 @@
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const multer = require('multer');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const RULE_DIR = path.join(__dirname, '..', '..', 'rule');
|
||||
if (!fs.existsSync(RULE_DIR)) {
|
||||
fs.mkdirSync(RULE_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
const ruleStorage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, RULE_DIR);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
cb(null, file.originalname);
|
||||
}
|
||||
});
|
||||
|
||||
const ruleUpload = multer({
|
||||
storage: ruleStorage,
|
||||
limits: { fileSize: 5 * 1024 * 1024 },
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowed = [
|
||||
'setup.cfg', '.pylintrc', '.flake8', 'pyproject.toml',
|
||||
'pylintrc', 'flake8.cfg', 'tox.ini'
|
||||
];
|
||||
const extAllowed = /\.(cfg|ini|toml|yaml|yml|json)$/i;
|
||||
if (allowed.includes(file.originalname) || extAllowed.test(file.originalname)) {
|
||||
return cb(null, true);
|
||||
}
|
||||
return cb(new Error('不支持的规则文件类型'));
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/rules/upload', ruleUpload.array('files'), (req, res) => {
|
||||
try {
|
||||
if (!req.files || req.files.length === 0) {
|
||||
return res.status(400).json({ success: false, error: '没有上传文件' });
|
||||
}
|
||||
const saved = req.files.map(f => f.originalname);
|
||||
res.json({ success: true, data: { saved_files: saved, directory: RULE_DIR } });
|
||||
} catch (error) {
|
||||
console.error('规则文件上传失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/rules', (req, res) => {
|
||||
try {
|
||||
if (!fs.existsSync(RULE_DIR)) {
|
||||
return res.json({ success: true, data: [] });
|
||||
}
|
||||
const files = fs.readdirSync(RULE_DIR).map(name => {
|
||||
const p = path.join(RULE_DIR, name);
|
||||
const stat = fs.statSync(p);
|
||||
return {
|
||||
name,
|
||||
size: stat.size,
|
||||
modified_at: stat.mtime.toISOString(),
|
||||
is_directory: stat.isDirectory()
|
||||
};
|
||||
}).filter(item => !item.is_directory);
|
||||
res.json({ success: true, data: files });
|
||||
} catch (error) {
|
||||
console.error('读取规则列表失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/rules/:name', (req, res) => {
|
||||
try {
|
||||
const name = req.params.name;
|
||||
const target = path.join(RULE_DIR, name);
|
||||
if (!fs.existsSync(target)) {
|
||||
return res.status(404).json({ success: false, error: '规则文件不存在' });
|
||||
}
|
||||
const content = fs.readFileSync(target, 'utf8');
|
||||
res.json({ success: true, data: { name, content } });
|
||||
} catch (error) {
|
||||
console.error('读取规则文件失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/rules/:name', (req, res) => {
|
||||
try {
|
||||
const name = req.params.name;
|
||||
const { content } = req.body || {};
|
||||
if (!name) {
|
||||
return res.status(400).json({ success: false, error: '规则文件名不能为空' });
|
||||
}
|
||||
const target = path.join(RULE_DIR, name);
|
||||
const dir = path.dirname(target);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(target, content ?? '', 'utf8');
|
||||
res.json({ success: true, message: '保存成功' });
|
||||
} catch (error) {
|
||||
console.error('保存规则文件失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/rules/:name', (req, res) => {
|
||||
try {
|
||||
const name = req.params.name;
|
||||
const target = path.join(RULE_DIR, name);
|
||||
if (!fs.existsSync(target)) {
|
||||
return res.status(404).json({ success: false, error: '规则文件不存在' });
|
||||
}
|
||||
fs.unlinkSync(target);
|
||||
res.json({ success: true, message: '已删除' });
|
||||
} catch (error) {
|
||||
console.error('删除规则文件失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// 模拟backend.js中的路径计算
|
||||
function testPathCalculation() {
|
||||
// 模拟__dirname的值 (view目录)
|
||||
const mockDirname = path.join(__dirname, 'view');
|
||||
|
||||
console.log('当前目录:', __dirname);
|
||||
console.log('模拟的__dirname:', mockDirname);
|
||||
|
||||
// 计算out目录路径
|
||||
const outDir = path.join(mockDirname, '..', 'out');
|
||||
console.log('计算出的out目录路径:', outDir);
|
||||
|
||||
// 规范化路径
|
||||
const normalizedOutDir = path.resolve(outDir);
|
||||
console.log('规范化后的out目录路径:', normalizedOutDir);
|
||||
|
||||
// 检查目录是否存在
|
||||
if (fs.existsSync(normalizedOutDir)) {
|
||||
console.log('目录存在');
|
||||
// 列出目录内容
|
||||
const files = fs.readdirSync(normalizedOutDir);
|
||||
console.log('目录内容:', files);
|
||||
} else {
|
||||
console.log('目录不存在');
|
||||
}
|
||||
}
|
||||
|
||||
testPathCalculation();
|
||||
@ -0,0 +1,8 @@
|
||||
import os
|
||||
|
||||
def bad_function():
|
||||
exec("print('Hello, world!')") # Potential security risk
|
||||
os.system("echo This is a test")# Missing space after comment
|
||||
|
||||
if __name__=="__main__": # Missing spaces around ==
|
||||
bad_function()
|
||||
@ -0,0 +1,347 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>简化的代码质量检查工具</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.tool-checkbox {
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background-color: #f1f8ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.file-upload {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.download-btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
#report-area {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
display: none;
|
||||
background-color: #e8f5e9;
|
||||
}
|
||||
|
||||
#result {
|
||||
background-color: #f5f5f5;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3498db;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 20px auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
background-color: #95a5a6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.debug-panel {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>简化的代码质量检查工具</h1>
|
||||
|
||||
<div id="toolContainer">
|
||||
<fieldset>
|
||||
<legend>选择工具:</legend>
|
||||
<div class="tool-checkbox">
|
||||
<input type="checkbox" id="bandit" name="tools" value="bandit" checked>
|
||||
<label for="bandit">Bandit</label>
|
||||
</div>
|
||||
<div class="tool-checkbox">
|
||||
<input type="checkbox" id="flake8" name="tools" value="flake8" checked>
|
||||
<label for="flake8">Flake8</label>
|
||||
</div>
|
||||
<div class="tool-checkbox">
|
||||
<input type="checkbox" id="pylint" name="tools" value="pylint" checked>
|
||||
<label for="pylint">Pylint</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="file-upload">
|
||||
<label for="file">上传代码文件:</label>
|
||||
<input type="file" id="file" name="file" accept=".py" aria-label="选择Python文件">
|
||||
</div>
|
||||
|
||||
<button type="button" id="submitBtn" class="btn">提交检查</button>
|
||||
</div>
|
||||
|
||||
<div class="loader" id="loader"></div>
|
||||
|
||||
<div id="report-area" aria-live="polite">
|
||||
<h2>检查报告</h2>
|
||||
<p>报告已生成,点击下载:</p>
|
||||
<a id="report-download" href="#" class="download-btn">下载报告</a>
|
||||
<button id="close-report" class="btn" style="margin-left: 10px;">关闭</button>
|
||||
</div>
|
||||
|
||||
<h2>结果:</h2>
|
||||
<pre id="result" role="log" aria-live="polite"></pre>
|
||||
|
||||
<div class="debug-panel">
|
||||
<strong>调试信息:</strong><br>
|
||||
<div id="debug-log"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 简单的调试日志函数
|
||||
function debugLog(message, data = null) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const logEntry = `[${timestamp}] ${message}`;
|
||||
if (data) {
|
||||
console.log(logEntry, data);
|
||||
} else {
|
||||
console.log(logEntry);
|
||||
}
|
||||
|
||||
const debugDiv = document.getElementById('debug-log');
|
||||
debugDiv.innerHTML += logEntry + '<br>';
|
||||
debugDiv.scrollTop = debugDiv.scrollHeight;
|
||||
}
|
||||
|
||||
// 页面加载
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
debugLog('页面DOM加载完成');
|
||||
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const resultPre = document.getElementById('result');
|
||||
const reportArea = document.getElementById('report-area');
|
||||
const reportLink = document.getElementById('report-download');
|
||||
const closeReportBtn = document.getElementById('close-report');
|
||||
const loader = document.getElementById('loader');
|
||||
const fileInput = document.getElementById('file');
|
||||
|
||||
let isProcessing = false;
|
||||
|
||||
// 初始隐藏报告区域
|
||||
reportArea.style.display = 'none';
|
||||
|
||||
// 关闭报告区域
|
||||
closeReportBtn.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
reportArea.style.display = 'none';
|
||||
debugLog('报告区域已关闭');
|
||||
});
|
||||
|
||||
// 提交按钮处理
|
||||
submitBtn.addEventListener('click', async function (event) {
|
||||
debugLog('提交按钮被点击');
|
||||
|
||||
// 阻止所有默认行为
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
debugLog('阻止了默认行为和冒泡');
|
||||
|
||||
// 防止重复提交
|
||||
if (isProcessing) {
|
||||
debugLog('正在处理中,忽略重复点击');
|
||||
return;
|
||||
}
|
||||
isProcessing = true;
|
||||
debugLog('开始处理请求');
|
||||
|
||||
// 更新UI状态
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = '检查中...';
|
||||
loader.style.display = 'block';
|
||||
resultPre.textContent = '正在检查...';
|
||||
reportArea.style.display = 'none';
|
||||
|
||||
try {
|
||||
// 验证文件
|
||||
if (fileInput.files.length === 0) {
|
||||
throw new Error('请上传一个文件');
|
||||
}
|
||||
debugLog('文件验证通过');
|
||||
|
||||
// 获取选中的工具
|
||||
const tools = [];
|
||||
if (document.getElementById('bandit').checked) tools.push('bandit');
|
||||
if (document.getElementById('flake8').checked) tools.push('flake8');
|
||||
if (document.getElementById('pylint').checked) tools.push('pylint');
|
||||
|
||||
if (tools.length === 0) {
|
||||
throw new Error('请至少选择一个工具');
|
||||
}
|
||||
debugLog('工具选择:', tools);
|
||||
|
||||
// 准备表单数据
|
||||
const formData = new FormData();
|
||||
formData.append('file', fileInput.files[0]);
|
||||
formData.append('tools', tools.join(','));
|
||||
debugLog('表单数据准备完成');
|
||||
|
||||
// 发送请求
|
||||
debugLog('开始发送网络请求...');
|
||||
const response = await fetch('http://localhost:3000/check', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
debugLog('收到响应,状态码:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`HTTP错误! 状态码: ${response.status}\n${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
debugLog('成功解析响应数据');
|
||||
debugLog('响应数据结构:', data);
|
||||
|
||||
// 显示结果
|
||||
resultPre.textContent = JSON.stringify(data, null, 2);
|
||||
debugLog('结果已显示在页面上');
|
||||
|
||||
// 显示报告下载区域
|
||||
if (data.reportUrl) {
|
||||
const downloadUrl = 'http://localhost:3000' + data.reportUrl;
|
||||
reportLink.href = downloadUrl;
|
||||
reportLink.download = `code_report_${Date.now()}.md`;
|
||||
reportArea.style.display = 'block';
|
||||
debugLog('报告下载链接已设置');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
debugLog('处理过程中发生错误:', error.message);
|
||||
resultPre.textContent = `错误: ${error.message}`;
|
||||
console.error('详细错误信息:', error);
|
||||
} finally {
|
||||
// 恢复UI状态
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '提交检查';
|
||||
loader.style.display = 'none';
|
||||
isProcessing = false;
|
||||
debugLog('UI状态已恢复');
|
||||
}
|
||||
});
|
||||
|
||||
// 下载链接处理
|
||||
reportLink.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
debugLog('下载链接被点击');
|
||||
|
||||
// 使用简单的下载方式
|
||||
const link = document.createElement('a');
|
||||
link.href = reportLink.href;
|
||||
link.download = reportLink.download;
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
debugLog('下载已启动');
|
||||
});
|
||||
|
||||
debugLog('事件监听器设置完成');
|
||||
});
|
||||
|
||||
// 全局错误处理
|
||||
window.addEventListener('error', function (event) {
|
||||
console.error('全局JavaScript错误:', event.error);
|
||||
debugLog('捕获到全局错误: ' + event.message);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', function (event) {
|
||||
console.error('未处理的Promise错误:', event.reason);
|
||||
debugLog('捕获到未处理Promise错误: ' + event.reason);
|
||||
});
|
||||
|
||||
debugLog('脚本初始化完成');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -0,0 +1,193 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>防刷新测试页面</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>防刷新测试页面</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>防刷新状态控制</h2>
|
||||
<button id="toggleRefresh" class="btn">启用防刷新</button>
|
||||
<span id="refreshStatus">防刷新: 禁用</span>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>测试按钮</h2>
|
||||
<button id="testBtn1" class="btn">测试按钮1</button>
|
||||
<button id="testBtn2" class="btn">测试按钮2</button>
|
||||
<button id="testBtn3" class="btn">测试按钮3</button>
|
||||
<a id="testLink" href="#" class="btn" style="text-decoration: none;">测试链接</a>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>结果显示</h2>
|
||||
<div id="results" class="status">
|
||||
点击按钮查看结果...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>键盘测试</h2>
|
||||
<p>尝试按 F5 或 Ctrl+R 键测试防刷新功能</p>
|
||||
<div id="keyboardResults" class="status">
|
||||
键盘事件将在这里显示...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 防刷新状态
|
||||
let preventRefresh = false;
|
||||
let eventCount = 0;
|
||||
|
||||
// DOM元素
|
||||
const elements = {
|
||||
toggleRefresh: document.getElementById('toggleRefresh'),
|
||||
refreshStatus: document.getElementById('refreshStatus'),
|
||||
testBtn1: document.getElementById('testBtn1'),
|
||||
testBtn2: document.getElementById('testBtn2'),
|
||||
testBtn3: document.getElementById('testBtn3'),
|
||||
testLink: document.getElementById('testLink'),
|
||||
results: document.getElementById('results'),
|
||||
keyboardResults: document.getElementById('keyboardResults')
|
||||
};
|
||||
|
||||
// 全局防刷新拦截器
|
||||
function setupRefreshPrevention() {
|
||||
// 拦截所有点击事件
|
||||
document.addEventListener('click', function (event) {
|
||||
if (preventRefresh) {
|
||||
eventCount++;
|
||||
console.log(`拦截到点击事件 #${eventCount}:`, event.target.tagName, event.target.id || event.target.textContent);
|
||||
elements.results.innerHTML += `<br>点击事件 #${eventCount}: ${event.target.tagName} - ${event.target.id || event.target.textContent}`;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
// 拦截键盘事件
|
||||
document.addEventListener('keydown', function (event) {
|
||||
if (preventRefresh) {
|
||||
console.log('拦截到键盘事件:', event.key, event.ctrlKey ? 'Ctrl+' : '');
|
||||
elements.keyboardResults.innerHTML += `<br>键盘事件: ${event.key}${event.ctrlKey ? ' (Ctrl)' : ''}`;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
// 拦截链接点击
|
||||
document.addEventListener('click', function (event) {
|
||||
if (preventRefresh && event.target.tagName === 'A') {
|
||||
console.log('拦截到链接点击:', event.target.href);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
// 切换防刷新状态
|
||||
elements.toggleRefresh.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
preventRefresh = !preventRefresh;
|
||||
|
||||
if (preventRefresh) {
|
||||
elements.toggleRefresh.textContent = '禁用防刷新';
|
||||
elements.toggleRefresh.style.backgroundColor = '#27ae60';
|
||||
elements.refreshStatus.textContent = '防刷新: 启用';
|
||||
console.log('防刷新已启用');
|
||||
} else {
|
||||
elements.toggleRefresh.textContent = '启用防刷新';
|
||||
elements.toggleRefresh.style.backgroundColor = '#3498db';
|
||||
elements.refreshStatus.textContent = '防刷新: 禁用';
|
||||
eventCount = 0;
|
||||
elements.results.innerHTML = '点击按钮查看结果...';
|
||||
elements.keyboardResults.innerHTML = '键盘事件将在这里显示...';
|
||||
console.log('防刷新已禁用');
|
||||
}
|
||||
});
|
||||
|
||||
// 添加测试按钮事件
|
||||
[elements.testBtn1, elements.testBtn2, elements.testBtn3].forEach((btn, index) => {
|
||||
btn.addEventListener('click', function (event) {
|
||||
if (!preventRefresh) {
|
||||
elements.results.innerHTML += `<br>按钮 ${index + 1} 被点击`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 添加测试链接事件
|
||||
elements.testLink.addEventListener('click', function (event) {
|
||||
if (!preventRefresh) {
|
||||
elements.results.innerHTML += '<br>链接被点击';
|
||||
}
|
||||
});
|
||||
|
||||
// 页面事件监听器
|
||||
window.addEventListener('beforeunload', function (event) {
|
||||
console.log('页面即将被刷新或关闭');
|
||||
if (preventRefresh) {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化
|
||||
setupRefreshPrevention();
|
||||
console.log('防刷新测试页面初始化完成');
|
||||
|
||||
// 添加页面加载时间戳
|
||||
const loadTime = new Date().toLocaleString();
|
||||
console.log('页面加载时间:', loadTime);
|
||||
document.body.insertAdjacentHTML('afterbegin',
|
||||
`<div style="background: #e8f5e9; padding: 10px; margin-bottom: 20px; border-radius: 4px;">
|
||||
页面加载时间: ${loadTime}
|
||||
</div>`
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -1,217 +0,0 @@
|
||||
# FortifyCode 快速开始指南
|
||||
|
||||
## 一键启动
|
||||
|
||||
### Windows 用户
|
||||
双击运行 `start_server.bat` 文件
|
||||
|
||||
### Linux/Mac 用户
|
||||
```bash
|
||||
chmod +x start_server.sh
|
||||
./start_server.sh
|
||||
```
|
||||
|
||||
### 使用 npm
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
## 首次使用前的准备
|
||||
|
||||
### 1. 安装 Node.js
|
||||
访问 https://nodejs.org/ 下载并安装 Node.js (推荐 LTS 版本)
|
||||
|
||||
验证安装:
|
||||
```bash
|
||||
node --version
|
||||
npm --version
|
||||
```
|
||||
|
||||
### 2. 安装 Python
|
||||
访问 https://www.python.org/ 下载并安装 Python 3.7+
|
||||
|
||||
**重要**: 安装时勾选 "Add Python to PATH"
|
||||
|
||||
验证安装:
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
|
||||
### 3. 安装项目依赖
|
||||
|
||||
#### Node.js 依赖
|
||||
```bash
|
||||
cd src
|
||||
npm install
|
||||
```
|
||||
|
||||
#### Python 代码检查工具
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
或手动安装:
|
||||
```bash
|
||||
pip install pylint flake8 bandit
|
||||
```
|
||||
|
||||
## 启动服务器
|
||||
|
||||
选择以下任一方式:
|
||||
|
||||
### 方式 1: 使用启动脚本(推荐)
|
||||
- Windows: 双击 `start_server.bat`
|
||||
- Linux/Mac: 运行 `./start_server.sh`
|
||||
|
||||
### 方式 2: 使用 npm
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
### 方式 3: 直接运行
|
||||
```bash
|
||||
node backend.js
|
||||
```
|
||||
|
||||
## 访问系统
|
||||
|
||||
启动成功后,浏览器访问:
|
||||
|
||||
```
|
||||
http://localhost:5000
|
||||
```
|
||||
|
||||
你会看到 FortifyCode 的主界面。
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例 1: 快速检查单个文件
|
||||
|
||||
1. 准备一个 Python 文件,例如 `test.py`:
|
||||
```python
|
||||
import os
|
||||
def test():
|
||||
x=1+2
|
||||
print(x)
|
||||
test()
|
||||
```
|
||||
|
||||
2. 在主页点击或拖拽文件到上传区域
|
||||
3. 点击"开始检查"按钮
|
||||
4. 查看检查结果
|
||||
|
||||
### 示例 2: 创建项目
|
||||
|
||||
1. 点击顶部导航栏的"项目管理"
|
||||
2. 点击"新建项目"按钮
|
||||
3. 填写项目信息:
|
||||
- 项目名称: 我的第一个项目
|
||||
- 项目描述: 测试项目
|
||||
- 来源类型: 选择"文件上传"
|
||||
4. 选择要上传的文件或文件夹
|
||||
5. 点击"创建项目"
|
||||
|
||||
### 示例 3: 查看项目详情
|
||||
|
||||
1. 在项目列表中点击项目卡片
|
||||
2. 查看项目信息和文件浏览器
|
||||
3. 点击"运行检查"进行代码检查
|
||||
4. 在"检查历史"中查看历史记录
|
||||
|
||||
## 常见问题快速解决
|
||||
|
||||
### ❌ 提示 "未找到 Node.js"
|
||||
**解决**: 安装 Node.js 并确保添加到系统 PATH
|
||||
|
||||
### ❌ 提示 "未找到 Python"
|
||||
**解决**: 安装 Python 并确保添加到系统 PATH
|
||||
|
||||
### ❌ 提示 "pylint 未安装"
|
||||
**解决**:
|
||||
```bash
|
||||
pip install pylint
|
||||
```
|
||||
|
||||
### ❌ 提示 "端口 5000 被占用"
|
||||
**解决**:
|
||||
- 方案 1: 关闭占用端口的程序
|
||||
- 方案 2: 修改端口号
|
||||
编辑 `backend.js` 第 10 行:
|
||||
```javascript
|
||||
const PORT = process.env.PORT || 8080; // 改为其他端口
|
||||
```
|
||||
|
||||
### ❌ 上传文件失败
|
||||
**解决**:
|
||||
- 确保文件是 .py, .pyx, .pyi 格式
|
||||
- 确保文件大小不超过 100MB
|
||||
- 检查磁盘空间是否充足
|
||||
|
||||
## 系统架构
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ 浏览器 │ http://localhost:5000
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ Express 服务器 (Node.js) │
|
||||
│ - 提供前端页面 │
|
||||
│ - 处理API请求 │
|
||||
│ - 文件管理 │
|
||||
└──────┬──────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────┐
|
||||
│ 代码检查工具 (Python) │
|
||||
│ - Pylint (代码质量) │
|
||||
│ - Flake8 (代码规范) │
|
||||
│ - Bandit (安全检查) │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## 功能概览
|
||||
|
||||
### 🏠 仪表板
|
||||
- 查看系统统计信息
|
||||
- 快捷文件上传和检查
|
||||
- Agent 状态监控
|
||||
|
||||
### 📁 项目管理
|
||||
- 创建和管理多个项目
|
||||
- 支持 GitHub/Gitee 克隆
|
||||
- 支持本地文件上传
|
||||
|
||||
### 📝 文件浏览器
|
||||
- 浏览项目文件
|
||||
- 在线编辑代码
|
||||
- 文件上传和管理
|
||||
|
||||
### 🔍 代码检查
|
||||
- 多工具协同检查
|
||||
- 问题分类显示
|
||||
- 修复建议提供
|
||||
|
||||
### 📊 检查报告
|
||||
- 详细的问题列表
|
||||
- 问题统计分析
|
||||
- 历史记录查看
|
||||
|
||||
## 下一步
|
||||
|
||||
- 📖 阅读完整的 [README.md](README.md)
|
||||
- 📚 查看 [API 文档](view/API.md)
|
||||
- 🔧 了解 [军工软件Python编码指南](../doc/军工软件python编码指南.docx)
|
||||
|
||||
## 获取帮助
|
||||
|
||||
如果遇到问题:
|
||||
1. 检查控制台输出的错误信息
|
||||
2. 查看浏览器开发者工具的 Console 选项卡
|
||||
3. 参考 README.md 中的"常见问题"部分
|
||||
|
||||
---
|
||||
|
||||
祝使用愉快! 🚀
|
||||
|
||||
Loading…
Reference in new issue