Files
012-kaopeilian/backend/scripts/seed_beauty_data.py
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

431 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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())