From 95461a09ab882e9ba64fd25b4a686e3cd3631ccb Mon Sep 17 00:00:00 2001 From: taiyi Date: Mon, 17 Nov 2025 12:56:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A1=B5=E9=9D=A2=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=8A=9F=E8=83=BD=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DOCUMENTATION_GUIDE.md | 52 ++++ app/web/templates/license/export.html | 203 +++++++++++++ deploy.py | 292 ++++++++++++++++++ docs/DEPLOYMENT.md | 134 ++++++++ init_db_mysql.py | 420 ++++++++++++++++++++++++++ 5 files changed, 1101 insertions(+) create mode 100644 DOCUMENTATION_GUIDE.md create mode 100644 app/web/templates/license/export.html create mode 100644 deploy.py create mode 100644 docs/DEPLOYMENT.md create mode 100644 init_db_mysql.py diff --git a/DOCUMENTATION_GUIDE.md b/DOCUMENTATION_GUIDE.md new file mode 100644 index 0000000..ecfa779 --- /dev/null +++ b/DOCUMENTATION_GUIDE.md @@ -0,0 +1,52 @@ +# 项目文档整理指南 + +本文档介绍了KaMiXiTong软件授权管理系统中各个文档的作用和使用方法,帮助用户快速找到所需的文档。 + +## 文档分类 + +### 1. 入门指南 +- [README.md](README.md) - 项目简介和快速开始指南 +- [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) - 项目详细总结和架构说明 + +### 2. 部署文档 +- [DEPLOYMENT.md](DEPLOYMENT.md) - MySQL数据库部署详细指南 +- [MYSQL_CONFIG_GUIDE.md](MYSQL_CONFIG_GUIDE.md) - MySQL配置与初始化指南 + +### 3. 集成文档 +- [docs/INTEGRATION.md](docs/INTEGRATION.md) - 软件授权系统集成指南 +- [docs/EXAMPLES.md](docs/EXAMPLES.md) - 使用示例和代码模板 + +### 4. API文档 +- [docs/FASTAPI.md](docs/FASTAPI.md) - FastAPI接口配置文档 +- [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) - API部署说明 + +## 文档使用建议 + +### 新用户入门 +1. 首先阅读 [README.md](README.md) 了解项目基本功能 +2. 查看 [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) 了解系统架构和技术栈 +3. 根据需要选择部署方式,参考相应的部署文档 + +### 开发者集成 +1. 阅读 [docs/INTEGRATION.md](docs/INTEGRATION.md) 了解如何将授权验证器集成到您的软件中 +2. 参考 [docs/EXAMPLES.md](docs/EXAMPLES.md) 中的代码示例进行实际开发 +3. 如需使用FastAPI接口,查看 [docs/FASTAPI.md](docs/FASTAPI.md) + +### 系统管理员 +1. 根据数据库类型选择相应的部署文档: + - MySQL数据库:[DEPLOYMENT.md](DEPLOYMENT.md) 和 [MYSQL_CONFIG_GUIDE.md](MYSQL_CONFIG_GUIDE.md) +2. 参考 [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) 进行生产环境部署 + +## 已删除文档说明 + +以下文档已被删除,因为它们是过时的或与当前系统不相关: + +- `DEPLOYMENT_GUIDE.md` - 账号管理系统重构部署指南(已过时) +- `SERVER_DEPLOYMENT_GUIDE.md` - 服务器部署指南(内容已整合到其他文档中) +- `REFACTOR_NOTES.md` - 账号管理系统重构说明(已过时) + +## 注意事项 + +1. 所有文档均保持最新状态,与当前代码版本兼容 +2. 如在使用过程中发现问题,请及时反馈 +3. 建议收藏本文档,方便日后查阅 \ No newline at end of file diff --git a/app/web/templates/license/export.html b/app/web/templates/license/export.html new file mode 100644 index 0000000..3d4fb27 --- /dev/null +++ b/app/web/templates/license/export.html @@ -0,0 +1,203 @@ +{% extends "base.html" %} + +{% block title %}导出卡密 - 软件授权管理系统{% endblock %} + +{% block page_title %}导出卡密{% endblock %} + +{% block page_actions %} + + + 返回列表 + +{% endblock %} + +{% block content %} +
+
+
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + 取消 +
+
+
+
+ +
+
+
+
导出说明
+
+
+
    +
  • + + 可以选择特定产品、状态和类型的卡密进行导出 +
  • +
  • + + 导出格式支持Excel和CSV两种 +
  • +
  • + + 导出的文件包含卡密、产品、状态、有效期等详细信息 +
  • +
  • + + 导出操作可能需要一些时间,请耐心等待 +
  • +
+
+
+
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/deploy.py b/deploy.py new file mode 100644 index 0000000..af1ac4d --- /dev/null +++ b/deploy.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +KaMiXiTong 部署脚本 +用于生产环境的一键部署 +""" + +import os +import sys +import subprocess +import argparse +from pathlib import Path + +def check_python_version(): + """检查Python版本""" + if sys.version_info < (3, 6): + print("❌ 错误: 需要Python 3.6或更高版本") + print(f"当前版本: {sys.version}") + sys.exit(1) + print(f"✅ Python版本检查通过: {sys.version}") + +def create_directories(): + """创建必要的目录""" + directories = [ + 'logs', + 'static/uploads', + 'static/css', + 'static/js' + ] + + for directory in directories: + Path(directory).mkdir(parents=True, exist_ok=True) + print("✅ 目录结构创建完成") + +def check_env_file(): + """检查环境配置文件""" + env_file = Path('.env') + if not env_file.exists(): + print("⚠️ 未找到 .env 文件,正在创建默认配置...") + try: + with open('.env.example', 'r', encoding='utf-8') as src: + with open('.env', 'w', encoding='utf-8') as dst: + dst.write(src.read()) + print("✅ 已创建默认 .env 文件") + except FileNotFoundError: + print("❌ 未找到 .env.example 文件") + return False + else: + print("✅ 环境配置文件已存在") + return True + +def install_dependencies(): + """安装依赖""" + print("📦 正在安装依赖包...") + try: + subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt']) + + # 检查并安装生产环境依赖 + try: + subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'gunicorn']) + print("✅ Gunicorn安装完成") + except subprocess.CalledProcessError: + print("⚠️ Gunicorn安装失败") + + print("✅ 依赖安装完成") + return True + except subprocess.CalledProcessError: + print("❌ 依赖安装失败") + return False + +def init_database(): + """初始化数据库""" + print("🗄️ 正在初始化数据库...") + + # 检查使用哪种数据库 + env_file = Path('.env') + if env_file.exists(): + with open(env_file, 'r', encoding='utf-8') as f: + content = f.read() + if 'DATABASE_URL=sqlite' in content: + init_script = 'init_db_sqlite.py' + else: + init_script = 'setup_mysql.py' + else: + # 默认使用SQLite + init_script = 'init_db_sqlite.py' + + try: + if os.path.exists(init_script): + subprocess.check_call([sys.executable, init_script]) + print("✅ 数据库初始化完成") + return True + else: + print(f"⚠️ 数据库初始化脚本 {init_script} 不存在") + return False + except subprocess.CalledProcessError as e: + print(f"❌ 数据库初始化失败: {e}") + return False + +def setup_systemd_service(): + """配置Systemd服务""" + print("⚙️ 正在配置Systemd服务...") + + # 获取当前路径 + app_path = os.path.abspath('.') + venv_path = os.path.join(app_path, 'venv', 'bin', 'gunicorn') + + # 如果没有虚拟环境,使用系统gunicorn + if not os.path.exists(venv_path): + venv_path = 'gunicorn' + + service_content = f"""[Unit] +Description=KaMiXiTong Service +After=network.target + +[Service] +User=www-data +Group=www-data +WorkingDirectory={app_path} +Environment="FLASK_ENV=production" +ExecStart={venv_path} -w 4 -b 127.0.0.1:5000 run:app +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +""" + + try: + # 写入服务文件 + with open('/tmp/kamaxitong.service', 'w') as f: + f.write(service_content) + + print("✅ Systemd服务配置文件已生成") + print("💡 请使用以下命令安装服务:") + print(" sudo cp /tmp/kamaxitong.service /etc/systemd/system/") + print(" sudo systemctl daemon-reload") + print(" sudo systemctl enable kamaxitong.service") + print(" sudo systemctl start kamaxitong.service") + return True + except Exception as e: + print(f"❌ Systemd服务配置失败: {e}") + return False + +def setup_nginx_config(): + """配置Nginx""" + print("⚙️ 正在生成Nginx配置...") + + # 从环境变量获取域名 + frontend_domain = os.environ.get('FRONTEND_DOMAIN', '') + if frontend_domain: + # 移除协议部分 + if '://' in frontend_domain: + server_name = frontend_domain.split('://', 1)[1] + else: + server_name = frontend_domain + else: + server_name = 'your-domain.com' + + nginx_config = f"""server {{ + listen 80; + server_name {server_name}; + + # 重定向到HTTPS(可选) + return 301 https://$server_name$request_uri; +}} + +server {{ + listen 443 ssl http2; + server_name {server_name}; + + # SSL证书配置(请替换为实际路径) + # ssl_certificate /path/to/your/certificate.crt; + # ssl_certificate_key /path/to/your/private.key; + + # 安全头配置 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; + + # Gzip压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied expired no-cache no-store private must-revalidate auth; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss; + + # 代理到Flask应用 + location / {{ + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $server_name; + }} + + # 静态文件直接由Nginx处理 + location /static/ {{ + alias {os.path.abspath('static')}/; + expires 1y; + add_header Cache-Control "public, immutable"; + }} + + # 日志配置 + access_log /var/log/nginx/kamaxitong.access.log; + error_log /var/log/nginx/kamaxitong.error.log; +}} +""" + + try: + # 写入配置文件 + with open('/tmp/kamaxitong_nginx.conf', 'w') as f: + f.write(nginx_config) + + print("✅ Nginx配置文件已生成") + print("💡 请使用以下命令安装配置:") + print(" sudo cp /tmp/kamaxitong_nginx.conf /etc/nginx/sites-available/kamaxitong") + print(" sudo ln -s /etc/nginx/sites-available/kamaxitong /etc/nginx/sites-enabled/") + print(" sudo nginx -t") + print(" sudo systemctl restart nginx") + return True + except Exception as e: + print(f"❌ Nginx配置生成失败: {e}") + return False + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description='KaMiXiTong 部署脚本') + parser.add_argument('--skip-install', action='store_true', help='跳过依赖安装') + parser.add_argument('--skip-db', action='store_true', help='跳过数据库初始化') + parser.add_argument('--setup-service', action='store_true', help='配置Systemd服务') + parser.add_argument('--setup-nginx', action='store_true', help='生成Nginx配置') + + args = parser.parse_args() + + print("=" * 50) + print(" KaMiXiTong 生产环境部署脚本") + print("=" * 50) + + # 检查Python版本 + check_python_version() + + # 创建目录 + create_directories() + + # 检查环境配置 + if not check_env_file(): + sys.exit(1) + + # 安装依赖 + if not args.skip_install: + if not install_dependencies(): + sys.exit(1) + else: + print("⏭️ 跳过依赖安装") + + # 初始化数据库 + if not args.skip_db: + if not init_database(): + sys.exit(1) + else: + print("⏭️ 跳过数据库初始化") + + # 配置Systemd服务 + if args.setup_service: + setup_systemd_service() + + # 配置Nginx + if args.setup_nginx: + setup_nginx_config() + + print("\n" + "=" * 50) + print("✅ 部署准备完成!") + print("=" * 50) + + if args.setup_service: + print("💡 已生成Systemd服务配置文件,请按提示安装") + + if args.setup_nginx: + print("💡 已生成Nginx配置文件,请按提示安装") + + print("\n📌 下一步操作:") + print("1. 编辑 .env 文件,配置数据库和其他参数") + print("2. 配置SSL证书(推荐)") + print("3. 启动服务") + print("4. 访问你的域名") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..68f4d70 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,134 @@ +# 部署说明 + +本文档介绍如何将KaMiXiTong系统部署到生产环境并通过域名访问。 + +## 域名访问配置 + +### 问题描述 + +在本地开发环境中,系统默认通过 `http://localhost:5000` 访问。当部署到服务器并通过域名访问时,前端页面中的接口调用可能会仍然指向 `localhost:5000`,导致接口调用失败。 + +### 解决方案 + +系统已通过修改前端JavaScript中的 `apiRequest` 函数来自动检测当前访问的主机并构建正确的API调用地址。 + +前端JavaScript现在会使用 `window.location.origin` 来获取当前访问的完整域名,并自动构建完整的API URL。 + +### 配置Nginx反向代理 + +推荐使用Nginx作为反向代理服务器。以下是一个Nginx配置示例: + +```nginx +server { + listen 80; + server_name your-domain.com; + + # 重定向到HTTPS(可选) + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl; + server_name your-domain.com; + + # SSL证书配置 + ssl_certificate /path/to/your/certificate.crt; + ssl_certificate_key /path/to/your/private.key; + + # 代理到Flask应用 + location / { + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # 静态文件直接由Nginx处理 + location /static/ { + alias /path/to/your/app/static/; + expires 1y; + add_header Cache-Control "public, immutable"; + } +} +``` + +### 环境变量配置 + +在生产环境中,建议通过环境变量来配置应用: + +```bash +# 复制示例配置文件 +cp .env.example .env + +# 编辑配置文件 +nano .env +``` + +重要的环境变量包括: + +- `FLASK_ENV`: 设置为 `production` +- `SECRET_KEY`: 设置为强随机字符串 +- `DATABASE_URL`: 数据库连接URL +- `HOST`: 监听地址,生产环境建议设置为 `0.0.0.0` + +### 使用Gunicorn启动应用 + +在生产环境中,推荐使用Gunicorn作为WSGI服务器: + +```bash +# 安装Gunicorn +pip install gunicorn + +# 启动应用 +gunicorn -w 4 -b 127.0.0.1:5000 run:app +``` + +### 使用Systemd管理服务(Linux) + +创建Systemd服务文件: + +```ini +# /etc/systemd/system/kamaxitong.service +[Unit] +Description=KaMiXiTong Service +After=network.target + +[Service] +User=www-data +Group=www-data +WorkingDirectory=/path/to/your/app +Environment="FLASK_ENV=production" +ExecStart=/path/to/your/venv/bin/gunicorn -w 4 -b 127.0.0.1:5000 run:app +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +启用并启动服务: + +```bash +sudo systemctl enable kamaxitong.service +sudo systemctl start kamaxitong.service +``` + +## 常见问题 + +### Q: 前端页面能正常访问,但API调用失败怎么办? + +A: 检查以下几点: +1. 确认Nginx配置正确,特别是 `proxy_set_header` 指令 +2. 确认Flask应用监听地址设置为 `0.0.0.0` +3. 检查浏览器开发者工具中的网络请求,确认API请求的URL是否正确 + +### Q: 如何确认域名配置是否正确? + +A: 可以通过以下方式检查: +1. 在浏览器中打开应用,按F12打开开发者工具 +2. 切换到Network标签页 +3. 刷新页面,观察API请求的URL是否为域名地址而非localhost + +### Q: 静态文件无法加载怎么办? + +A: 确保Nginx配置中正确设置了静态文件的location块,或者让Flask应用处理静态文件。 \ No newline at end of file diff --git a/init_db_mysql.py b/init_db_mysql.py new file mode 100644 index 0000000..5da8cfc --- /dev/null +++ b/init_db_mysql.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +MySQL 数据库初始化脚本 +KaMiXiTong 软件授权管理系统 + +使用 MySQL 数据库的完整版本,支持多用户和高性能 +""" + +import os +import sys +from datetime import datetime, timedelta, timezone +from werkzeug.security import generate_password_hash + +# 添加项目根目录到Python路径 +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +def load_env_config(): + """从.env文件加载配置""" + env_file = '.env' + if os.path.exists(env_file): + print(f"正在从 {env_file} 加载配置...") + with open(env_file, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + # 跳过注释和空行 + if line and not line.startswith('#'): + # 解析键值对 + if '=' in line: + key, value = line.split('=', 1) + key = key.strip() + value = value.strip().strip('"').strip("'") + os.environ[key] = value + print("配置加载完成") + else: + print(f"警告: 未找到 {env_file} 文件") + +def init_mysql_database(): + """初始化 MySQL 数据库""" + print("=" * 50) + print("KaMiXiTong MySQL 数据库初始化开始...") + print("=" * 50) + + # 从.env文件加载配置 + load_env_config() + + # 获取数据库URL + database_url = os.environ.get('DATABASE_URL') + if not database_url: + print("❌ 未设置DATABASE_URL环境变量") + print("💡 请在.env文件中配置DATABASE_URL") + print("示例: DATABASE_URL=mysql+pymysql://用户名:密码@主机:端口/数据库名") + return False + + try: + # 设置数据库URL到环境变量 + os.environ['DATABASE_URL'] = database_url + + # 导入应用 + from app import create_app, db + from app.models import Admin, Product, Version, License, Device, Ticket + from app.models.audit_log import AuditLog + + # 创建应用实例 + app = create_app() + + print(f"数据库类型: MySQL") + print(f"数据库URL: {database_url}") + + with app.app_context(): + # 1. 删除所有表(如果存在) + print("\n1. 清理现有数据库表...") + db.drop_all() + print(" ✓ 已删除所有现有表") + + # 2. 创建所有表 + print("\n2. 创建数据库表结构...") + db.create_all() + print(" ✓ 已创建所有数据表") + + # 显式提交事务以确保表被创建 + db.session.commit() + + # 2.1 验证表创建 + print("\n2.1 验证表创建...") + from sqlalchemy import inspect + inspector = inspect(db.engine) + tables = inspector.get_table_names() + print(f" ✓ 成功创建 {len(tables)} 个表: {', '.join(tables)}") + + if not tables: + raise Exception("表创建失败,没有找到任何表") + + # 3. 插入初始数据 + print("\n3. 插入初始数据...") + insert_mysql_data() + + # 显式提交并关闭会话 + db.session.commit() + db.session.close() + + # 4. 显示数据库信息 + print("\n4. MySQL 数据库初始化完成!") + show_mysql_database_info() + + return True + + except Exception as e: + print(f"\n❌ MySQL 数据库初始化失败: {e}") + import traceback + traceback.print_exc() + return False + + +def insert_mysql_data(): + """插入 MySQL 初始数据""" + from app import db + from app.models import Admin, Product, Version, License, Device, Ticket + from app.models.audit_log import AuditLog + + # 1. 创建默认管理员账号 + print(" 创建默认管理员账号...") + admin = Admin( + username='admin', + email='admin@kamaxitong.com', + role=1, # 超级管理员 + status=1 # 正常 + ) + admin.set_password('admin123') + db.session.add(admin) + + # 创建测试管理员账号 + test_admin = Admin( + username='test_admin', + email='test@kamaxitong.com', + role=0, # 普通管理员 + status=1 # 正常 + ) + test_admin.set_password('test123') + db.session.add(test_admin) + print(" ✓ 已创建管理员账号 (admin/admin123, test_admin/test123)") + + # 2. 创建示例产品 + print(" 创建示例产品...") + products = [ + Product( + product_id='KMX001', + product_name='KaMiXiTong 专业版', + description='专业的软件授权管理系统,支持多种授权模式和完整的设备管理', + status=1 + ), + Product( + product_id='KMX002', + product_name='KaMiXiTong 企业版', + description='企业级软件授权管理解决方案,支持分布式部署和高并发访问', + status=1 + ) + ] + + for product in products: + db.session.add(product) + print(" ✓ 已创建2个示例产品") + + # 3. 创建版本信息 + print(" 创建版本信息...") + versions = [ + Version( + product_id='KMX001', + version_num='1.0.0', + update_log='初始版本发布', + download_url='https://download.kamaxitong.com/v1.0.0/professional.exe', + min_license_version='1.0.0', + force_update=0, + download_status=1, + publish_status=1 + ), + Version( + product_id='KMX001', + version_num='1.1.0', + update_log='功能增强版本', + download_url='https://download.kamaxitong.com/v1.1.0/professional.exe', + min_license_version='1.0.0', + force_update=0, + download_status=1, + publish_status=1 + ), + Version( + product_id='KMX002', + version_num='2.0.0', + update_log='企业版初始版本', + download_url='https://download.kamaxitong.com/v2.0.0/enterprise.exe', + min_license_version='2.0.0', + force_update=0, + download_status=1, + publish_status=1 + ) + ] + + for version in versions: + db.session.add(version) + print(" ✓ 已创建3个版本信息") + + # 4. 创建示例许可证 + print(" 创建示例许可证...") + # 使用时区感知的 datetime 对象替换 utcnow() + current_time = datetime.now(timezone.utc) + + licenses = [ + # 永久许可证 + License( + license_key=License.generate_license_key(), + product_id='KMX001', + type=1, # 正式版 + status=1, # 已激活 + valid_days=-1, # 永久 + bind_machine_code='DEMO-MACHINE-CODE-001', + activate_time=current_time - timedelta(days=30), + expire_time=None, # 永久不过期 + last_verify_time=current_time - timedelta(days=1), + unbind_count=0 + ), + # 1年试用期许可证 + License( + license_key=License.generate_license_key(), + product_id='KMX001', + type=0, # 试用版 + status=1, # 已激活 + valid_days=365, # 1年 + bind_machine_code='DEMO-MACHINE-CODE-002', + activate_time=current_time - timedelta(days=15), + expire_time=current_time + timedelta(days=350), # 还有350天过期 + last_verify_time=current_time - timedelta(hours=6), + unbind_count=1 + ), + # 未激活许可证 + License( + license_key=License.generate_license_key(), + product_id='KMX002', + type=1, # 正式版 + status=0, # 未激活 + valid_days=365, # 1年 + bind_machine_code=None, + activate_time=None, + expire_time=None, + last_verify_time=None, + unbind_count=0 + ) + ] + + for license_obj in licenses: + db.session.add(license_obj) + print(" ✓ 已创建3个示例许可证") + + # 5. 创建示例设备 + print(" 创建示例设备...") + devices = [ + Device( + machine_code='DEMO-MACHINE-CODE-001', + license_id=1, # 对应第一个许可证 + product_id='KMX001', + software_version='1.1.0', + status=1, # 正常 + activate_time=current_time - timedelta(days=30), + last_verify_time=current_time - timedelta(days=1) + ), + Device( + machine_code='DEMO-MACHINE-CODE-002', + license_id=2, # 对应第二个许可证 + product_id='KMX001', + software_version='1.0.0', + status=1, # 正常 + activate_time=current_time - timedelta(days=15), + last_verify_time=current_time - timedelta(hours=6) + ) + ] + + for device in devices: + db.session.add(device) + print(" ✓ 已创建2个示例设备") + + # 6. 创建示例工单 + print(" 创建示例工单...") + tickets = [ + Ticket( + title='许可证激活失败', + product_id='KMX001', + software_version='1.0.0', + machine_code='CUSTOMER-MACHINE-001', + license_key='DEMO-LICENSE-KEY-001', + description='客户反馈无法激活许可证', + priority=2, # 高优先级 + status=2, # 已解决 + operator='admin', + remark='问题已解决', + create_time=current_time - timedelta(days=5), + update_time=current_time - timedelta(days=4), + resolve_time=current_time - timedelta(days=4) + ), + Ticket( + title='功能咨询', + product_id='KMX002', + software_version='2.0.0', + machine_code=None, + license_key=None, + description='客户咨询批量购买事宜', + priority=1, # 中优先级 + status=0, # 待处理 + operator=None, + remark=None, + create_time=current_time - timedelta(days=2), + update_time=current_time - timedelta(days=2) + ) + ] + + for ticket in tickets: + db.session.add(ticket) + print(" ✓ 已创建2个示例工单") + + # 7. 创建审计日志示例数据 + print(" 创建示例审计日志...") + audit_logs = [ + AuditLog( + admin_id=1, + action='login', + target_type='system', + target_id=None, + details='管理员登录系统', + ip_address='127.0.0.1', + user_agent='Mozilla/5.0...', + create_time=current_time - timedelta(days=30) + ), + AuditLog( + admin_id=1, + action='create', + target_type='product', + target_id=1, + details='创建产品 KaMiXiTong 专业版', + ip_address='127.0.0.1', + user_agent='Mozilla/5.0...', + create_time=current_time - timedelta(days=29) + ) + ] + + for log in audit_logs: + db.session.add(log) + print(" ✓ 已创建2条示例审计日志") + + # 提交所有更改 + db.session.commit() + print(" ✓ 所有初始数据插入完成") + + +def show_mysql_database_info(): + """显示 MySQL 数据库信息""" + from app import db + from app.models import Admin, Product, Version, License, Device, Ticket + from app.models.audit_log import AuditLog + + print("\n" + "=" * 50) + print("MySQL 数据库初始化信息统计") + print("=" * 50) + + # 统计各表的记录数 + tables_info = [ + ('管理员账号', Admin.query.count()), + ('产品', Product.query.count()), + ('版本', Version.query.count()), + ('许可证', License.query.count()), + ('设备', Device.query.count()), + ('工单', Ticket.query.count()), + ('审计日志', AuditLog.query.count()) + ] + + for table_name, count in tables_info: + print(f" {table_name:8} : {count:3} 条记录") + + print("\n" + "=" * 50) + print("默认登录信息") + print("=" * 50) + print(" 超级管理员: admin / admin123") + print(" 普通管理员: test_admin / test123") + print("\n注意事项:") + print(" 1. 请在生产环境中修改默认密码") + print(" 2. 确保MySQL服务正常运行") + print(" 3. 修改 .env 文件中的 DATABASE_URL 为正确的MySQL配置") + print(" 4. 定期备份数据库") + print("=" * 50) + + +def main(): + """主函数""" + try: + # 检查是否安装了PyMySQL + try: + import pymysql + except ImportError: + print("❌ 未安装PyMySQL,请运行: pip install PyMySQL") + sys.exit(1) + + # 初始化MySQL数据库 + success = init_mysql_database() + + if success: + print("\n🎉 MySQL 数据库初始化成功!") + print("\n启动服务器:") + print(" python run.py") + print(" 或") + print(" python start.py") + print("\n访问: http://localhost:5000") + else: + print("\n❌ MySQL 数据库初始化失败!") + sys.exit(1) + + except Exception as e: + print(f"\n❌ 初始化失败: {e}") + sys.exit(1) + + +if __name__ == '__main__': + main() \ No newline at end of file