添加产品规格文档并优化项目结构

Made-with: Cursor
This commit is contained in:
刘国栋
2026-04-30 18:37:46 +08:00
parent bf2551d529
commit fb309299bf
101 changed files with 9586 additions and 14386 deletions
+298
View File
@@ -0,0 +1,298 @@
# Tab9:平台同步(从 geo_tool.py 迁移,通过 render_tab_platform_sync() 供主入口调用。)
import pandas as pd
import streamlit as st
def render_tab_platform_sync(storage, brand: str) -> None:
"""渲染 Tab9:平台同步。由主入口在 with tab9 内调用。"""
st.markdown("### 📤 平台文章同步")
st.caption("将生成的文章自动发布到各平台,支持API发布和一键复制")
# 品牌信息:优先使用主入口传入的 brand(来自侧边栏 cfg),与其它 Tab 一致
brand_to_use = (st.session_state.get("brand") or brand or "").strip()
if not brand_to_use:
st.info("请先在侧边栏设置品牌信息")
else:
# 平台账号配置
st.markdown("---")
st.markdown("#### 🔐 平台账号配置")
platform_config_tabs = st.tabs(["GitHub", "其他平台(开发中)"])
with platform_config_tabs[0]:
st.markdown("##### GitHub 配置")
st.caption("配置GitHub账号信息,用于自动发布文章到GitHub仓库")
# 检查是否已有配置
existing_config = storage.get_platform_account("GitHub", brand_to_use)
github_api_key = st.text_input(
"GitHub Personal Access Token",
value=existing_config.get('api_key', '') if existing_config else '',
type="password",
help="在 https://github.com/settings/tokens 创建Token,需要 repo 权限",
key="github_api_key"
)
github_repo_owner = st.text_input(
"仓库所有者(用户名)",
value=existing_config.get('config', {}).get('repo_owner', '') if existing_config else '',
help="GitHub用户名或组织名",
key="github_repo_owner"
)
github_repo_name = st.text_input(
"仓库名称",
value=existing_config.get('config', {}).get('repo_name', '') if existing_config else '',
help="要发布到的仓库名称",
key="github_repo_name"
)
col1, col2 = st.columns([1, 4])
with col1:
if st.button("💾 保存配置", type="primary", use_container_width=True):
if github_api_key and github_repo_owner and github_repo_name:
try:
# 验证账号
from platform_sync.github_publisher import GitHubPublisher
publisher = GitHubPublisher(github_api_key, github_repo_owner, github_repo_name)
if publisher.validate_account():
storage.save_platform_account(
platform="GitHub",
account_config={
'account_type': 'api',
'api_key': github_api_key,
'config': {
'repo_owner': github_repo_owner,
'repo_name': github_repo_name
}
},
brand=brand_to_use
)
st.success("✅ GitHub配置已保存并验证成功!")
else:
st.error("❌ GitHub Token验证失败,请检查Token是否正确")
except Exception as e:
st.error(f"❌ 配置保存失败: {str(e)}")
else:
st.error("请填写完整信息")
with col2:
if existing_config:
st.info("✅ 已配置GitHub账号")
# 发布功能
st.markdown("---")
st.markdown("#### 📝 发布文章")
# 选择文章
articles = storage.get_articles(brand=brand_to_use)
if articles:
# 文章选择
article_options = {}
for article in articles:
display_name = f"{article.get('keyword', 'N/A')} - {article.get('platform', 'N/A')}"
article_options[display_name] = article.get('id')
if article_options:
selected_article_key = st.selectbox(
"选择要发布的文章",
list(article_options.keys()),
key="publish_article_select"
)
selected_article_id = article_options[selected_article_key]
# 选择平台
# 定义平台列表
api_platforms = ["GitHub"]
copy_platforms = [
"头条号(资讯软文)", "小红书(生活种草)", "抖音图文(短内容)", "简书(文艺)",
"QQ空间(社交)", "新浪博客(博客)", "新浪新闻(资讯)", "搜狐号(资讯)",
"一点号(资讯)", "东方财富(财经)", "邦阅网(外贸)", "原创力文档(文档)"
]
all_publish_platforms = api_platforms + copy_platforms
publish_platform = st.selectbox(
"选择发布平台",
all_publish_platforms,
key="publish_platform_select"
)
if publish_platform == "GitHub":
# 检查配置
account_config = storage.get_platform_account("GitHub", brand_to_use)
if not account_config:
st.warning("⚠️ 请先配置GitHub账号")
else:
# 获取文章
article = next((a for a in articles if a.get('id') == selected_article_id), None)
if article:
# 显示文章预览
with st.expander("📄 文章预览", expanded=False):
st.markdown(f"**关键词**: {article.get('keyword', 'N/A')}")
st.markdown(f"**平台**: {article.get('platform', 'N/A')}")
st.markdown(f"**内容长度**: {len(article.get('content', ''))} 字符")
st.markdown("---")
st.text_area("内容", article.get('content', ''), height=200, disabled=True)
# 发布选项
file_path = st.text_input(
"文件路径(可选)",
value=f"content/{article.get('keyword', 'article').replace(' ', '_')[:50]}.md",
help="GitHub仓库中的文件路径,留空使用默认路径",
key="github_file_path"
)
if st.button("🚀 发布到GitHub", type="primary", use_container_width=True):
try:
from platform_sync.github_publisher import GitHubPublisher
publisher = GitHubPublisher(
api_key=account_config['api_key'],
repo_owner=account_config['config']['repo_owner'],
repo_name=account_config['config']['repo_name']
)
with st.spinner("正在发布到GitHub..."):
result = publisher.publish(
content=article.get('content', ''),
title=article.get('keyword', 'Untitled'),
file_path=file_path if file_path else None
)
# 保存发布记录
storage.save_publish_record(
article_id=selected_article_id,
platform="GitHub",
publish_method="api",
publish_status="success" if result['success'] else "failed",
publish_url=result.get('publish_url', ''),
publish_id=result.get('publish_id', ''),
error_message=result.get('error', '')
)
# 显示结果
if result['success']:
st.success(f"✅ 发布成功!")
st.markdown(f"**发布链接**: [{result['publish_url']}]({result['publish_url']})")
st.balloons()
else:
st.error(f"❌ 发布失败: {result.get('error', '未知错误')}")
except Exception as e:
st.error(f"❌ 发布过程出错: {str(e)}")
storage.save_publish_record(
article_id=selected_article_id,
platform="GitHub",
publish_method="api",
publish_status="failed",
error_message=str(e)
)
else:
# 一键复制平台
article = next((a for a in articles if a.get('id') == selected_article_id), None)
if article:
from platform_sync.copy_manager import CopyManager
copy_manager = CopyManager()
# 格式化内容
formatted_content = copy_manager.format_for_platform(
platform=publish_platform,
content=article.get('content', ''),
title=article.get('keyword', 'Untitled'),
keyword=article.get('keyword', ''),
brand=brand_to_use
)
# 显示格式化后的内容
with st.expander("📄 格式化后的内容(已复制到剪贴板)", expanded=True):
st.text_area(
"内容",
formatted_content,
height=300,
key="formatted_content_display"
)
# 发布指南
guide = copy_manager.generate_publish_guide(publish_platform)
with st.expander("📋 发布指南", expanded=True):
st.markdown(guide)
# 复制按钮
col1, col2 = st.columns([1, 1])
with col1:
if st.button("📋 复制到剪贴板", type="primary", use_container_width=True):
if copy_manager.copy_to_clipboard(formatted_content):
st.success("✅ 内容已复制到剪贴板!")
st.info("💡 请按照上方发布指南,将内容粘贴到对应平台发布")
# 保存发布记录(标记为已复制)
storage.save_publish_record(
article_id=selected_article_id,
platform=publish_platform,
publish_method="copy",
publish_status="copied",
error_message=""
)
else:
st.error("❌ 复制失败,请手动复制内容")
with col2:
if st.button("📥 下载内容", use_container_width=True):
# 生成下载文件
safe_title = article.get('keyword', 'article').replace(' ', '_')[:50]
filename = f"{publish_platform.replace('', '_').replace('', '')}_{safe_title}.txt"
st.download_button(
label="⬇️ 下载",
data=formatted_content,
file_name=filename,
mime="text/plain",
key="download_formatted_content"
)
else:
st.info("📝 请先在【2 自动创作】中生成文章")
# 发布记录
st.markdown("---")
st.markdown("#### 📊 发布记录")
publish_records = storage.get_publish_records(brand=brand_to_use)
if publish_records:
# 统计信息
total_records = len(publish_records)
success_records = len([r for r in publish_records if r.get('publish_status') == 'success'])
copied_records = len([r for r in publish_records if r.get('publish_status') == 'copied'])
failed_records = len([r for r in publish_records if r.get('publish_status') == 'failed'])
stat_col1, stat_col2, stat_col3, stat_col4 = st.columns(4)
with stat_col1:
st.metric("总发布数", total_records)
with stat_col2:
st.metric("API成功", success_records, delta=f"{success_records/total_records*100:.1f}%" if total_records > 0 else "0%")
with stat_col3:
st.metric("已复制", copied_records, delta=f"{copied_records/total_records*100:.1f}%" if total_records > 0 else "0%")
with stat_col4:
st.metric("失败", failed_records)
# 记录列表
st.markdown("##### 最近发布记录")
records_df = pd.DataFrame(publish_records[:20]) # 显示最近20条
if not records_df.empty:
# 格式化显示
display_df = records_df[['platform', 'publish_method', 'publish_status', 'publish_url', 'published_at', 'created_at']].copy()
display_df.columns = ['平台', '发布方式', '状态', '链接', '发布时间', '创建时间']
display_df['状态'] = display_df['状态'].map({
'success': '✅ 成功',
'failed': '❌ 失败',
'pending': '⏳ 待发布',
'copied': '📋 已复制'
})
display_df['发布方式'] = display_df['发布方式'].map({
'api': 'API',
'copy': '一键复制'
})
st.dataframe(display_df, use_container_width=True, hide_index=True)
else:
st.info("暂无发布记录")
# =======================