Files
beige/backend/scripts/seed_data.py
yangqianqian df1856d793 上线版: 产品表单统一+form嵌套修复+用户管理+部署+三套叙事
- 产品编辑入口统一走 ProductFormFull(卖点/风格/人群/品牌词全字段);
  修复开任务页 <form> 套 <form> 致"编辑产品"报错、改不了、跳回首个产品
- dashboard 入口卡片对齐实际路由: 系统管理(/config) 与 工作配置(/settings) 分开;
  settings ?tab=products 直达改用挂载后读 URL, 消除 hydration mismatch
- 新增用户管理(users API/admin service/改密页) + alembic 022/023/024
- 上线部署: Dockerfile / docker-compose.prod+https / nginx https / .env.example
- A8 三套正交叙事(痛点/场景/成分背书) + beige 调色去AI化 + 飞轮 text_import 高权重信号

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 18:08:13 +08:00

137 lines
6.0 KiB
Python
Raw Permalink 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.
"""
scripts/seed_data.py — 初始化北哥 workspace 种子数据
建3账号(admin/supervisor/operator) + workspace_members + 预置素颜霜 + 5条违禁词
幂等:已存在则跳过,可重复跑。
用法cd backend && python3 scripts/seed_data.py
"""
import json
import os
import sys
# 把 backend/ 加入 path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.core.config import get_settings # noqa: E402 — path must be set first
from app.core.database import SessionLocal # noqa: E402
from app.models.user import User # noqa: E402
from app.models.workspace import Workspace, WorkspaceMember # noqa: E402
from app.models.product import Product, BannedWord # noqa: E402
from app.services.auth_service import DEFAULT_SEED_PASSWORD, hash_password # noqa: E402
get_settings() # 触发 .env 校验,缺变量早报错
WORKSPACE_NAME = "北哥小红书车间"
WORKSPACE_SLUG = "beige-xhs"
SEED_PASSWORDS = {
"admin": os.environ.get("CLOVER_SEED_PASSWORD_ADMIN", os.environ.get("CLOVER_SEED_PASSWORD", DEFAULT_SEED_PASSWORD)),
"supervisor": os.environ.get("CLOVER_SEED_PASSWORD_SUPERVISOR", os.environ.get("CLOVER_SEED_PASSWORD", DEFAULT_SEED_PASSWORD)),
"operator": os.environ.get("CLOVER_SEED_PASSWORD_OPERATOR", os.environ.get("CLOVER_SEED_PASSWORD", DEFAULT_SEED_PASSWORD)),
}
if get_settings().APP_ENV == "production" and any(p == DEFAULT_SEED_PASSWORD for p in SEED_PASSWORDS.values()):
raise RuntimeError("Production seed requires CLOVER_SEED_PASSWORD; default password is forbidden")
USERS = [
{"username": "admin", "email": "admin@clover.local", "password": SEED_PASSWORDS["admin"], "role": "admin"},
{"username": "supervisor", "email": "supervisor@clover.local", "password": SEED_PASSWORDS["supervisor"], "role": "supervisor"},
{"username": "operator", "email": "operator@clover.local", "password": SEED_PASSWORDS["operator"], "role": "operator"},
]
PRODUCT = {
"name": "素颜霜(预置样本)",
"category": "素颜霜",
"source": "preset",
"selling_points": json.dumps(["遮瑕提亮", "持妆", "养肤成分"], ensure_ascii=False),
"style_tone": "素人分享、真实不浮夸",
"text_angles": json.dumps(["痛点切入", "成分党", "使用场景", "质地肤感", "平价替代"], ensure_ascii=False),
"custom_prompt": None,
"is_active": True,
}
BANNED_WORDS = [
{"word": "美白", "level": "auto_fix", "replacement": "提亮肤色"},
{"word": "祛斑", "level": "auto_fix", "replacement": "改善暗沉"},
{"word": "速效", "level": "soft_warn", "replacement": "温和渐进"},
{"word": "医用", "level": "hard_block", "replacement": None},
{"word": "药妆", "level": "hard_block", "replacement": None},
]
def run():
db = SessionLocal()
try:
# ── workspace ──────────────────────────────────────
ws = db.query(Workspace).filter(Workspace.slug == WORKSPACE_SLUG).first()
if not ws:
ws = Workspace(name=WORKSPACE_NAME, slug=WORKSPACE_SLUG, is_active=True)
db.add(ws)
db.flush()
print(f"[+] workspace 已建: {ws.name} (id={ws.id})")
else:
print(f"[=] workspace 已存在: id={ws.id}")
# ── users + members ────────────────────────────────
for u_spec in USERS:
user = db.query(User).filter(User.username == u_spec["username"]).first()
if not user:
user = User(
username=u_spec["username"],
email=u_spec["email"],
hashed_password=hash_password(u_spec["password"]),
is_active=True,
must_change_password=True,
)
db.add(user)
db.flush()
print(f"[+] user: {user.username}")
else:
print(f"[=] user 已存在: {user.username}")
mem = db.query(WorkspaceMember).filter(
WorkspaceMember.workspace_id == ws.id,
WorkspaceMember.user_id == user.id,
).first()
if not mem:
db.add(WorkspaceMember(workspace_id=ws.id, user_id=user.id, role=u_spec["role"]))
print(f" -> 绑定 role={u_spec['role']}")
# ── 预置素颜霜 ─────────────────────────────────────
prod = db.query(Product).filter(
Product.workspace_id == ws.id, Product.name == PRODUCT["name"]
).first()
if not prod:
prod = Product(workspace_id=ws.id, **PRODUCT)
db.add(prod)
print(f"[+] product: {prod.name}")
else:
print(f"[=] product 已存在: {prod.name}")
# ── 违禁词 ─────────────────────────────────────────
for bw_spec in BANNED_WORDS:
existing = db.query(BannedWord).filter(
BannedWord.workspace_id == ws.id, BannedWord.word == bw_spec["word"]
).first()
if not existing:
db.add(BannedWord(workspace_id=ws.id, **bw_spec))
print(f"[+] banned_word: {bw_spec['word']} ({bw_spec['level']})")
else:
print(f"[=] banned_word 已存在: {bw_spec['word']}")
# ── user_api_keys不再预置倩倩姐2026-06-12拍板系统埋的key彻底拔干净──
# 每个用户登录后到「设置 → API Key」自录自用录一次即记住。
# 种子不再从 APIPORTS_KEY 环境变量批量塞 key避免"免key可用"。
db.commit()
print("\n种子数据初始化完成。")
except Exception as e:
db.rollback()
print(f"[ERROR] {e}")
raise
finally:
db.close()
if __name__ == "__main__":
run()