420 lines
13 KiB
Python
420 lines
13 KiB
Python
#!/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() |