feat: 初始化考培练系统项目
- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
430
backend/scripts/seed_beauty_data.py
Normal file
430
backend/scripts/seed_beauty_data.py
Normal file
@@ -0,0 +1,430 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
轻医美连锁岗位与课程种子数据
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import aiomysql
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.append(str(project_root))
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
async def execute_seed():
|
||||
"""执行种子数据插入"""
|
||||
try:
|
||||
# 从环境变量或配置中获取数据库连接信息
|
||||
database_url = os.getenv("DATABASE_URL", settings.DATABASE_URL)
|
||||
|
||||
# 解析数据库连接字符串
|
||||
if database_url.startswith("mysql+aiomysql://"):
|
||||
url = database_url.replace("mysql+aiomysql://", "")
|
||||
else:
|
||||
url = database_url
|
||||
|
||||
# 解析连接参数
|
||||
auth_host = url.split("@")[1]
|
||||
user_pass = url.split("@")[0]
|
||||
host_port_db = auth_host.split("/")
|
||||
host_port = host_port_db[0].split(":")
|
||||
|
||||
user = user_pass.split(":")[0]
|
||||
password = user_pass.split(":")[1]
|
||||
host = host_port[0]
|
||||
port = int(host_port[1]) if len(host_port) > 1 else 3306
|
||||
database = host_port_db[1].split("?")[0] if len(host_port_db) > 1 else "kaopeilian"
|
||||
|
||||
print(f"连接数据库: {host}:{port}/{database}")
|
||||
|
||||
# 创建数据库连接
|
||||
conn = await aiomysql.connect(
|
||||
host=host,
|
||||
port=port,
|
||||
user=user,
|
||||
password=password,
|
||||
db=database,
|
||||
charset='utf8mb4'
|
||||
)
|
||||
|
||||
async with conn.cursor() as cursor:
|
||||
print("\n🎯 开始插入轻医美连锁业务数据...")
|
||||
|
||||
# 1. 插入轻医美相关岗位
|
||||
print("\n📌 插入岗位数据...")
|
||||
positions_data = [
|
||||
{
|
||||
'name': '区域经理',
|
||||
'code': 'region_manager',
|
||||
'description': '负责多家门店的运营管理和业绩达成',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['团队管理', '业绩分析', '战略规划', '客户关系'], ensure_ascii=False),
|
||||
'level': 'expert',
|
||||
'sort_order': 10
|
||||
},
|
||||
{
|
||||
'name': '店长',
|
||||
'code': 'store_manager',
|
||||
'description': '负责门店日常运营管理,团队建设和业绩达成',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['门店管理', '团队建设', '销售管理', '客户维护'], ensure_ascii=False),
|
||||
'level': 'senior',
|
||||
'sort_order': 20,
|
||||
'parent_code': 'region_manager'
|
||||
},
|
||||
{
|
||||
'name': '美容顾问',
|
||||
'code': 'beauty_consultant',
|
||||
'description': '为客户提供专业的美容咨询和方案设计',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['产品知识', '销售技巧', '方案设计', '客户沟通'], ensure_ascii=False),
|
||||
'level': 'intermediate',
|
||||
'sort_order': 30,
|
||||
'parent_code': 'store_manager'
|
||||
},
|
||||
{
|
||||
'name': '美容技师',
|
||||
'code': 'beauty_therapist',
|
||||
'description': '为客户提供专业的美容护理服务',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['护肤技术', '仪器操作', '手法技巧', '服务意识'], ensure_ascii=False),
|
||||
'level': 'intermediate',
|
||||
'sort_order': 40,
|
||||
'parent_code': 'store_manager'
|
||||
},
|
||||
{
|
||||
'name': '医美咨询师',
|
||||
'code': 'medical_beauty_consultant',
|
||||
'description': '提供医疗美容项目咨询和方案制定',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['医美知识', '风险评估', '方案设计', '合规意识'], ensure_ascii=False),
|
||||
'level': 'senior',
|
||||
'sort_order': 35,
|
||||
'parent_code': 'store_manager'
|
||||
},
|
||||
{
|
||||
'name': '护士',
|
||||
'code': 'nurse',
|
||||
'description': '协助医生进行医美项目操作,负责术后护理',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['护理技术', '无菌操作', '应急处理', '医疗知识'], ensure_ascii=False),
|
||||
'level': 'intermediate',
|
||||
'sort_order': 45,
|
||||
'parent_code': 'store_manager'
|
||||
},
|
||||
{
|
||||
'name': '前台接待',
|
||||
'code': 'receptionist',
|
||||
'description': '负责客户接待、预约管理和前台事务',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['接待礼仪', '沟通能力', '信息管理', '服务意识'], ensure_ascii=False),
|
||||
'level': 'junior',
|
||||
'sort_order': 50,
|
||||
'parent_code': 'store_manager'
|
||||
},
|
||||
{
|
||||
'name': '市场专员',
|
||||
'code': 'marketing_specialist',
|
||||
'description': '负责门店营销活动策划和执行',
|
||||
'status': 'active',
|
||||
'skills': json.dumps(['活动策划', '社媒运营', '数据分析', '创意设计'], ensure_ascii=False),
|
||||
'level': 'intermediate',
|
||||
'sort_order': 60,
|
||||
'parent_code': 'store_manager'
|
||||
}
|
||||
]
|
||||
|
||||
# 先获取已存在的岗位ID映射
|
||||
position_id_map = {}
|
||||
|
||||
# 插入岗位(处理层级关系)
|
||||
for position in positions_data:
|
||||
# 检查是否已存在
|
||||
check_sql = "SELECT id FROM positions WHERE code = %s AND is_deleted = FALSE"
|
||||
await cursor.execute(check_sql, (position['code'],))
|
||||
existing = await cursor.fetchone()
|
||||
|
||||
if existing:
|
||||
position_id_map[position['code']] = existing[0]
|
||||
print(f" ⚠️ 岗位 '{position['name']}' 已存在,ID: {existing[0]}")
|
||||
else:
|
||||
# 获取parent_id
|
||||
parent_id = None
|
||||
if 'parent_code' in position:
|
||||
parent_code = position.pop('parent_code')
|
||||
if parent_code in position_id_map:
|
||||
parent_id = position_id_map[parent_code]
|
||||
|
||||
# 插入岗位
|
||||
insert_sql = """
|
||||
INSERT INTO positions (name, code, description, parent_id, status, skills, level, sort_order, created_at, updated_at)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
||||
"""
|
||||
await cursor.execute(insert_sql, (
|
||||
position['name'],
|
||||
position['code'],
|
||||
position['description'],
|
||||
parent_id,
|
||||
position['status'],
|
||||
position['skills'],
|
||||
position['level'],
|
||||
position['sort_order']
|
||||
))
|
||||
position_id = cursor.lastrowid
|
||||
position_id_map[position['code']] = position_id
|
||||
print(f" ✅ 插入岗位: {position['name']} (ID: {position_id})")
|
||||
|
||||
await conn.commit()
|
||||
|
||||
# 2. 插入轻医美相关课程
|
||||
print("\n📚 插入课程数据...")
|
||||
courses_data = [
|
||||
{
|
||||
'name': '皮肤生理学基础',
|
||||
'description': '学习皮肤结构、功能和常见问题,为专业护理打下基础',
|
||||
'category': 'technology',
|
||||
'status': 'published',
|
||||
'duration_hours': 16,
|
||||
'difficulty_level': 2,
|
||||
'tags': json.dumps(['皮肤学', '基础理论', '必修课'], ensure_ascii=False),
|
||||
'is_featured': True,
|
||||
'sort_order': 100
|
||||
},
|
||||
{
|
||||
'name': '医美产品知识与应用',
|
||||
'description': '全面了解各类医美产品的成分、功效和适用人群',
|
||||
'category': 'technology',
|
||||
'status': 'published',
|
||||
'duration_hours': 20,
|
||||
'difficulty_level': 3,
|
||||
'tags': json.dumps(['产品知识', '医美', '专业技能'], ensure_ascii=False),
|
||||
'is_featured': True,
|
||||
'sort_order': 110
|
||||
},
|
||||
{
|
||||
'name': '美容仪器操作与维护',
|
||||
'description': '掌握各类美容仪器的操作方法、注意事项和日常维护',
|
||||
'category': 'technology',
|
||||
'status': 'published',
|
||||
'duration_hours': 24,
|
||||
'difficulty_level': 3,
|
||||
'tags': json.dumps(['仪器操作', '实操技能', '设备维护'], ensure_ascii=False),
|
||||
'is_featured': False,
|
||||
'sort_order': 120
|
||||
},
|
||||
{
|
||||
'name': '轻医美销售技巧',
|
||||
'description': '学习专业的销售话术、客户需求分析和成交技巧',
|
||||
'category': 'business',
|
||||
'status': 'published',
|
||||
'duration_hours': 16,
|
||||
'difficulty_level': 2,
|
||||
'tags': json.dumps(['销售技巧', '客户沟通', '业绩提升'], ensure_ascii=False),
|
||||
'is_featured': True,
|
||||
'sort_order': 130
|
||||
},
|
||||
{
|
||||
'name': '客户服务与投诉处理',
|
||||
'description': '提升服务意识,掌握客户投诉处理的方法和技巧',
|
||||
'category': 'business',
|
||||
'status': 'published',
|
||||
'duration_hours': 12,
|
||||
'difficulty_level': 2,
|
||||
'tags': json.dumps(['客户服务', '危机处理', '沟通技巧'], ensure_ascii=False),
|
||||
'is_featured': False,
|
||||
'sort_order': 140
|
||||
},
|
||||
{
|
||||
'name': '卫生消毒与感染控制',
|
||||
'description': '学习医美机构的卫生标准和消毒流程,确保服务安全',
|
||||
'category': 'general',
|
||||
'status': 'published',
|
||||
'duration_hours': 8,
|
||||
'difficulty_level': 1,
|
||||
'tags': json.dumps(['卫生安全', '消毒规范', '合规管理'], ensure_ascii=False),
|
||||
'is_featured': True,
|
||||
'sort_order': 150
|
||||
},
|
||||
{
|
||||
'name': '门店运营管理',
|
||||
'description': '学习门店日常管理、团队建设和业绩管理',
|
||||
'category': 'management',
|
||||
'status': 'published',
|
||||
'duration_hours': 20,
|
||||
'difficulty_level': 3,
|
||||
'tags': json.dumps(['门店管理', '团队管理', '运营策略'], ensure_ascii=False),
|
||||
'is_featured': False,
|
||||
'sort_order': 160
|
||||
},
|
||||
{
|
||||
'name': '医美项目介绍与咨询',
|
||||
'description': '详细了解各类医美项目的原理、效果和适应症',
|
||||
'category': 'technology',
|
||||
'status': 'published',
|
||||
'duration_hours': 30,
|
||||
'difficulty_level': 4,
|
||||
'tags': json.dumps(['医美项目', '专业咨询', '风险告知'], ensure_ascii=False),
|
||||
'is_featured': True,
|
||||
'sort_order': 170
|
||||
},
|
||||
{
|
||||
'name': '社媒营销与私域运营',
|
||||
'description': '学习如何通过社交媒体进行品牌推广和客户维护',
|
||||
'category': 'business',
|
||||
'status': 'published',
|
||||
'duration_hours': 16,
|
||||
'difficulty_level': 2,
|
||||
'tags': json.dumps(['社媒营销', '私域流量', '客户维护'], ensure_ascii=False),
|
||||
'is_featured': False,
|
||||
'sort_order': 180
|
||||
}
|
||||
]
|
||||
|
||||
course_id_map = {}
|
||||
|
||||
for course in courses_data:
|
||||
# 检查是否已存在
|
||||
check_sql = "SELECT id FROM courses WHERE name = %s AND is_deleted = FALSE"
|
||||
await cursor.execute(check_sql, (course['name'],))
|
||||
existing = await cursor.fetchone()
|
||||
|
||||
if existing:
|
||||
course_id_map[course['name']] = existing[0]
|
||||
print(f" ⚠️ 课程 '{course['name']}' 已存在,ID: {existing[0]}")
|
||||
else:
|
||||
# 插入课程
|
||||
insert_sql = """
|
||||
INSERT INTO courses (
|
||||
name, description, category, status, duration_hours,
|
||||
difficulty_level, tags, is_featured, sort_order,
|
||||
published_at, created_at, updated_at
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW(), NOW())
|
||||
"""
|
||||
await cursor.execute(insert_sql, (
|
||||
course['name'],
|
||||
course['description'],
|
||||
course['category'],
|
||||
course['status'],
|
||||
course['duration_hours'],
|
||||
course['difficulty_level'],
|
||||
course['tags'],
|
||||
course['is_featured'],
|
||||
course['sort_order']
|
||||
))
|
||||
course_id = cursor.lastrowid
|
||||
course_id_map[course['name']] = course_id
|
||||
print(f" ✅ 插入课程: {course['name']} (ID: {course_id})")
|
||||
|
||||
await conn.commit()
|
||||
|
||||
# 3. 设置岗位与课程的关联
|
||||
print("\n🔗 设置岗位课程关联...")
|
||||
position_courses = [
|
||||
# 店长必修课程
|
||||
('store_manager', '门店运营管理', 'required', 1),
|
||||
('store_manager', '轻医美销售技巧', 'required', 2),
|
||||
('store_manager', '客户服务与投诉处理', 'required', 3),
|
||||
('store_manager', '卫生消毒与感染控制', 'required', 4),
|
||||
|
||||
# 美容顾问必修课程
|
||||
('beauty_consultant', '皮肤生理学基础', 'required', 1),
|
||||
('beauty_consultant', '医美产品知识与应用', 'required', 2),
|
||||
('beauty_consultant', '轻医美销售技巧', 'required', 3),
|
||||
('beauty_consultant', '客户服务与投诉处理', 'required', 4),
|
||||
('beauty_consultant', '社媒营销与私域运营', 'optional', 5),
|
||||
|
||||
# 美容技师必修课程
|
||||
('beauty_therapist', '皮肤生理学基础', 'required', 1),
|
||||
('beauty_therapist', '美容仪器操作与维护', 'required', 2),
|
||||
('beauty_therapist', '卫生消毒与感染控制', 'required', 3),
|
||||
('beauty_therapist', '医美产品知识与应用', 'optional', 4),
|
||||
|
||||
# 医美咨询师必修课程
|
||||
('medical_beauty_consultant', '医美项目介绍与咨询', 'required', 1),
|
||||
('medical_beauty_consultant', '皮肤生理学基础', 'required', 2),
|
||||
('medical_beauty_consultant', '医美产品知识与应用', 'required', 3),
|
||||
('medical_beauty_consultant', '轻医美销售技巧', 'required', 4),
|
||||
('medical_beauty_consultant', '客户服务与投诉处理', 'required', 5),
|
||||
|
||||
# 护士必修课程
|
||||
('nurse', '卫生消毒与感染控制', 'required', 1),
|
||||
('nurse', '医美项目介绍与咨询', 'required', 2),
|
||||
('nurse', '皮肤生理学基础', 'required', 3),
|
||||
|
||||
# 前台接待必修课程
|
||||
('receptionist', '客户服务与投诉处理', 'required', 1),
|
||||
('receptionist', '医美产品知识与应用', 'optional', 2),
|
||||
|
||||
# 市场专员必修课程
|
||||
('marketing_specialist', '社媒营销与私域运营', 'required', 1),
|
||||
('marketing_specialist', '医美产品知识与应用', 'optional', 2),
|
||||
('marketing_specialist', '轻医美销售技巧', 'optional', 3),
|
||||
]
|
||||
|
||||
for pos_code, course_name, course_type, priority in position_courses:
|
||||
if pos_code in position_id_map and course_name in course_id_map:
|
||||
position_id = position_id_map[pos_code]
|
||||
course_id = course_id_map[course_name]
|
||||
|
||||
# 检查是否已存在
|
||||
check_sql = """
|
||||
SELECT id FROM position_courses
|
||||
WHERE position_id = %s AND course_id = %s AND is_deleted = FALSE
|
||||
"""
|
||||
await cursor.execute(check_sql, (position_id, course_id))
|
||||
existing = await cursor.fetchone()
|
||||
|
||||
if not existing:
|
||||
insert_sql = """
|
||||
INSERT INTO position_courses (position_id, course_id, course_type, priority, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, %s, %s, %s, FALSE, NOW(), NOW())
|
||||
"""
|
||||
await cursor.execute(insert_sql, (position_id, course_id, course_type, priority))
|
||||
print(f" ✅ 关联: {pos_code} - {course_name} ({course_type})")
|
||||
|
||||
await conn.commit()
|
||||
|
||||
# 4. 显示统计信息
|
||||
print("\n📊 数据统计:")
|
||||
|
||||
# 统计岗位
|
||||
await cursor.execute("SELECT COUNT(*) FROM positions WHERE is_deleted = FALSE")
|
||||
total_positions = (await cursor.fetchone())[0]
|
||||
print(f" 岗位总数: {total_positions}")
|
||||
|
||||
# 统计课程
|
||||
await cursor.execute("SELECT COUNT(*) FROM courses WHERE is_deleted = FALSE")
|
||||
total_courses = (await cursor.fetchone())[0]
|
||||
print(f" 课程总数: {total_courses}")
|
||||
|
||||
# 统计岗位课程关联
|
||||
await cursor.execute("SELECT COUNT(*) FROM position_courses WHERE is_deleted = FALSE")
|
||||
total_pc = (await cursor.fetchone())[0]
|
||||
print(f" 岗位课程关联数: {total_pc}")
|
||||
|
||||
print("\n🎉 轻医美连锁业务数据插入完成!")
|
||||
print("\n💡 提示:")
|
||||
print(" 1. 可以登录系统查看岗位管理页面")
|
||||
print(" 2. 每个岗位都配置了相应的必修和选修课程")
|
||||
print(" 3. 课程涵盖了技术、管理、业务和通用等各个分类")
|
||||
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 执行失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🌟 开始插入轻医美连锁岗位与课程数据...")
|
||||
asyncio.run(execute_seed())
|
||||
Reference in New Issue
Block a user