- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
431 lines
20 KiB
Python
431 lines
20 KiB
Python
#!/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())
|