feat: 创作工作台统一 + AI味检测/白标 + 移动端补全 + 合规发布闭环

- 新增创作工作台 studio.html:合并选题/内容工厂/文章管理为单一 tab 入口(iframe embed 模式)
- 新增 AI味检测模块(ai_slop API + 页面,合规软硬问题分级)
- 新增白标品牌配置(branding API + 页面 + deploy 私有化交付包)
- 发布闭环:publishing 放宽至 editor + records/mark-published 接口
- 移动端响应式补全(admin/calendar/ai-slop 表格卡片兜底)
- 修复菜单幂等播种缺陷(按 path 对齐,避免功能页孤立)
- 新增短视频脚本 shortvideo.py 与 2026 市场调研简报
This commit is contained in:
yuzhiran
2026-07-11 12:36:01 +08:00
parent 3d596275d5
commit 7e953afbd2
39 changed files with 2418 additions and 311 deletions
+51 -14
View File
@@ -76,6 +76,17 @@ def import_initial_data():
for cfg in default_system_configs:
if db.query(SystemConfig).filter(SystemConfig.key == cfg["key"]).first() is None:
db.add(SystemConfig(**cfg))
# 白标默认值(私有化部署/卖给他人时可改)
default_branding = [
{"key": "brand_name", "value": "宇之然内容创作平台", "description": "白标:平台名称"},
{"key": "brand_logo", "value": "", "description": "白标:Logo URL(留空用文字)"},
{"key": "brand_primary_color", "value": "#2563eb", "description": "白标:主色(导航/按钮)"},
{"key": "brand_support_email", "value": "", "description": "白标:支持邮箱"},
]
for b in default_branding:
if db.query(SystemConfig).filter(SystemConfig.key == b["key"]).first() is None:
db.add(SystemConfig(**b))
if db.query(SystemConfig).filter(SystemConfig.key == "review_llm_id").first() is None:
first_llm = db.query(LLMConfig).filter(LLMConfig.is_active == True).first()
if first_llm:
@@ -294,21 +305,47 @@ def import_initial_data():
db.commit()
print("✅ 插入默认角色")
# 初始化默认菜单(与 uni-nav.js 对齐)
if db.query(Menu).count() == 0:
default_menus = [
{"name": "仪表盘", "path": "/", "icon": "IconHome", "sort_order": 0, "roles": ["admin", "editor"]},
{"name": "选题", "path": "topics.html", "icon": "IconTopic", "sort_order": 1, "roles": ["admin", "editor"]},
{"name": "数据", "path": "metrics.html", "icon": "IconData", "sort_order": 2, "roles": ["admin", "editor"]},
{"name": "日历", "path": "calendar.html", "icon": "IconCalendar", "sort_order": 3, "roles": ["admin", "editor"]},
{"name": "素材", "path": "assets.html", "icon": "IconFolder", "sort_order": 4, "roles": ["admin", "editor"]},
{"name": "任务", "path": "tasks.html", "icon": "IconMenu", "sort_order": 5, "roles": ["admin", "editor"]},
{"name": "系统", "path": "admin.html", "icon": "IconSetting", "sort_order": 6, "roles": ["admin"]},
]
for m in default_menus:
db.add(Menu(**m))
# 归一化首页菜单路径,避免 index.html 与 /index.html 产生重复入口
idx_plain = db.query(Menu).filter(Menu.path == "index.html").first()
idx_slash = db.query(Menu).filter(Menu.path == "/index.html").first()
if idx_plain and not idx_slash:
idx_plain.path = "/index.html"
db.commit()
print("✅ 插入默认菜单")
elif idx_plain and idx_slash:
db.delete(idx_plain)
db.commit()
# 初始化默认菜单:幂等按 path 对齐(已有时更新排序/角色,缺失时插入)。
# 运行时会用 DB 菜单覆盖 uni-nav.js 的 fallback,因此这里必须保证文章管理/内容工厂/白标设置等入口存在。
default_menus = [
{"name": "工作台", "path": "/index.html", "icon": "IconHome", "sort_order": 0, "roles": ["admin", "editor"]},
{"name": "创作工作台", "path": "studio.html", "icon": "IconEdit", "sort_order": 1, "roles": ["admin", "editor"]},
{"name": "数据", "path": "metrics.html", "icon": "IconData", "sort_order": 2, "roles": ["admin", "editor"]},
{"name": "日历", "path": "calendar.html", "icon": "IconCalendar", "sort_order": 3, "roles": ["admin", "editor"]},
{"name": "素材", "path": "assets.html", "icon": "IconFolder", "sort_order": 4, "roles": ["admin", "editor"]},
{"name": "任务", "path": "tasks.html", "icon": "IconMenu", "sort_order": 5, "roles": ["admin", "editor"]},
{"name": "外推营销", "path": "campaigns.html", "icon": "IconStar", "sort_order": 6, "roles": ["admin", "editor"]},
{"name": "AI味检测", "path": "ai-slop.html", "icon": "IconSearch", "sort_order": 7, "roles": ["admin", "editor"]},
{"name": "白标设置", "path": "branding.html", "icon": "IconSetting", "sort_order": 8, "roles": ["admin"]},
{"name": "系统", "path": "admin.html", "icon": "IconSetting", "sort_order": 9, "roles": ["admin"]},
]
for m in default_menus:
found = db.query(Menu).filter(Menu.path == m["path"]).first()
if found:
found.sort_order = m["sort_order"]
found.roles = m["roles"]
found.icon = m["icon"]
found.is_active = True
# 保留原名称,避免意外重命名
else:
db.add(Menu(**m))
# 原独立的选题/内容工厂/文章管理已合并进「创作工作台」,从主导航隐藏(页面仍可被 studio 内嵌与直接访问)
for legacy in ["topics.html", "factory.html", "articles.html"]:
legacy_menu = db.query(Menu).filter(Menu.path == legacy).first()
if legacy_menu:
legacy_menu.is_active = False
db.commit()
print("✅ 菜单已对齐(创作工作台合并选题/内容工厂/文章管理)")
# 同步 PostgreSQL 自增序列
if os.getenv('USE_POSTGRES', 'true').lower() == 'true':