From 41b0f694eefd4b2e8b0f4efda435d9d3c6bcf91c Mon Sep 17 00:00:00 2001 From: Yuzhiran Dev Date: Sun, 17 May 2026 23:08:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B9=B3=E5=8F=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=A1=A8=E6=89=A9=E5=B1=95=E9=85=8D=E5=9B=BE/=E5=AD=97?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=BE=AE=E4=BF=A1=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7=E6=AD=A3=E6=96=87=E6=8F=92=E5=85=A5=E9=85=8D?= =?UTF-8?q?=E5=9B=BE=EF=BC=8C=E5=90=88=E8=A7=84=E6=A3=80=E6=9F=A5=E4=BB=8E?= =?UTF-8?q?DB=E8=AF=BB=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PlatformConfig模型新增requires_image、image_count_min/max、image_width/height、min_words/max_words - schemas.py同步PlatformConfigBase/Create/Update/Response新字段 - initial_data.py为三大平台填充初始值(微信需配图、字数800-1500等) - database.py添加新列ALTER TABLE迁移 - platforms.html重写编辑弹窗(正确字段名+配图/字数设置) - writer.py微信公众号文章正文h1后插入占位 - compliance_checker.py接受platform_config参数,从DB读取规则替代硬编码 - compliance_optimizer.py启动时加载DB平台配置传入checker --- PROGRESS.md | 5 +- platform/backend/app/database.py | 7 +++ platform/backend/app/initial_data.py | 44 ++++++++++++----- platform/backend/app/models.py | 14 ++++++ platform/backend/app/schemas.py | 14 ++++++ platform/frontend/platforms.html | 74 +++++++++++++++++++--------- scripts/compliance_checker.py | 39 +++++++++------ scripts/compliance_optimizer.py | 17 ++++++- scripts/writer.py | 11 +++++ 9 files changed, 174 insertions(+), 51 deletions(-) diff --git a/PROGRESS.md b/PROGRESS.md index 88a48ad..eb2fee0 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -3,7 +3,7 @@ > 本文件为项目进度唯一真理源,所有进度信息以此为准。 > 其他文档中的进度描述一律以本文为准。 -**最后更新**:2026-05-16 (v14) +**最后更新**:2026-05-17 (v15) --- @@ -117,6 +117,9 @@ | Phase 4 多租户隔离 (JWT+API 层) | 2026-05-16 | org_id 注入 JWT payload; 12 个 API 模块添加 org 过滤; 创建选题自动继承用户 org; 管理端组织 CRUD | | Phase 4 前端多租户 | 2026-05-16 | admin.html 新增组织管理标签页 (列表/创建/编辑/删除); users.html 增加组织列+H5卡片显示 | | Phase 4 预置数据修复 | 2026-05-16 | initial_data.py 管理员用户添加 org_id; opencode-go LLM config 补全 user_prompt_template | +| PlatformConfig 模型字段扩展 | 2026-05-17 | 增加 requires_image/image_count_min/image_count_max/image_width/image_height/min_words/max_words; schemas/initial_data/前端表单同步; database.py 迁移 | +| 微信公众号正文配图 | 2026-05-17 | writer.py wechat 分支在 h1 后插入 `` 占位 | +| 合规检查从 DB 读平台规则 | 2026-05-17 | compliance_checker.py 接受 platform_config 参数; compliance_optimizer.py 从 DB 加载平台配置传入 checker | ### ⏳ 待办 diff --git a/platform/backend/app/database.py b/platform/backend/app/database.py index 3399b9b..b89b624 100644 --- a/platform/backend/app/database.py +++ b/platform/backend/app/database.py @@ -53,6 +53,13 @@ def init_db(): for table, col, typ in [ ("users", "org_id", "VARCHAR DEFAULT 'default'"), ("topics", "org_id", "VARCHAR DEFAULT 'default'"), + ("platform_configs", "requires_image", "BOOLEAN DEFAULT FALSE"), + ("platform_configs", "image_count_min", "INTEGER DEFAULT 0"), + ("platform_configs", "image_count_max", "INTEGER DEFAULT 0"), + ("platform_configs", "image_width", "INTEGER DEFAULT 0"), + ("platform_configs", "image_height", "INTEGER DEFAULT 0"), + ("platform_configs", "min_words", "INTEGER DEFAULT 0"), + ("platform_configs", "max_words", "INTEGER DEFAULT 0"), ]: try: conn.execute(text(f"ALTER TABLE {table} ADD COLUMN IF NOT EXISTS {col} {typ}")) diff --git a/platform/backend/app/initial_data.py b/platform/backend/app/initial_data.py index d304c72..51690d1 100644 --- a/platform/backend/app/initial_data.py +++ b/platform/backend/app/initial_data.py @@ -81,11 +81,18 @@ def import_initial_data(): "icon": "🔍", "default_format": "长文深度分析,1500-3000字,有数据支撑", "compliance_rules": { - "max_length": 50000, - "requires_authentication": False, - "sensitive_words": ["敏感词示例1", "敏感词示例2"] + "max_title_len": 100, + "allowed_tags": ["科技", "生活", "职场", "教育", "可持续", "AI", "远程工作", "个人成长"], + "forbidden_patterns": ["加微信", "私聊", "付费咨询", "点击领取"] }, - "is_active": True + "is_active": True, + "requires_image": False, + "image_count_min": 0, + "image_count_max": 0, + "image_width": 0, + "image_height": 0, + "min_words": 1500, + "max_words": 3000 }, { "platform": "wechat", @@ -93,10 +100,18 @@ def import_initial_data(): "icon": "💚", "default_format": "公众号图文,800-1500字,亲切口语化", "compliance_rules": { - "max_length": 20000, - "requires_authentication": True + "max_title_len": 32, + "allowed_tags": ["科技", "生活", "职场", "教育", "可持续", "AI", "远程工作", "个人成长"], + "forbidden_patterns": ["诱导分享", "朋友圈", "转发群"] }, - "is_active": True + "is_active": True, + "requires_image": True, + "image_count_min": 1, + "image_count_max": 3, + "image_width": 1080, + "image_height": 1080, + "min_words": 800, + "max_words": 1500 }, { "platform": "xiaohongshu", @@ -104,11 +119,18 @@ def import_initial_data(): "icon": "📕", "default_format": "图文笔记,300-800字,emoji+标签", "compliance_rules": { - "max_length": 1000, - "requires_tags": True, - "max_tags": 10 + "max_title_len": 50, + "allowed_tags": ["生活方式", "可持续", "AI", "个人成长", "极简", "环保"], + "forbidden_patterns": ["私信", "加群", "导流"] }, - "is_active": True + "is_active": True, + "requires_image": True, + "image_count_min": 3, + "image_count_max": 6, + "image_width": 1080, + "image_height": 1440, + "min_words": 300, + "max_words": 800 } ] for p in platforms: diff --git a/platform/backend/app/models.py b/platform/backend/app/models.py index 1436b3a..afa6c25 100644 --- a/platform/backend/app/models.py +++ b/platform/backend/app/models.py @@ -361,6 +361,13 @@ class PlatformConfig(Base): compliance_rules = Column(JSON, default=dict) default_format = Column(Text, nullable=True) is_active = Column(Boolean, default=True) + requires_image = Column(Boolean, default=False) + image_count_min = Column(Integer, default=0) + image_count_max = Column(Integer, default=0) + image_width = Column(Integer, default=0) + image_height = Column(Integer, default=0) + min_words = Column(Integer, default=0) + max_words = Column(Integer, default=0) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) @@ -375,6 +382,13 @@ class PlatformConfig(Base): "compliance_rules": self.compliance_rules or {}, "default_format": self.default_format, "is_active": self.is_active, + "requires_image": self.requires_image, + "image_count_min": self.image_count_min, + "image_count_max": self.image_count_max, + "image_width": self.image_width, + "image_height": self.image_height, + "min_words": self.min_words, + "max_words": self.max_words, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, } diff --git a/platform/backend/app/schemas.py b/platform/backend/app/schemas.py index 92b694f..1a5a0cb 100644 --- a/platform/backend/app/schemas.py +++ b/platform/backend/app/schemas.py @@ -263,6 +263,13 @@ class PlatformConfigBase(BaseModel): compliance_rules: Dict[str, Any] = {} default_format: Optional[str] = None is_active: bool = True + requires_image: bool = False + image_count_min: int = 0 + image_count_max: int = 0 + image_width: int = 0 + image_height: int = 0 + min_words: int = 0 + max_words: int = 0 class PlatformConfigCreate(PlatformConfigBase): @@ -278,6 +285,13 @@ class PlatformConfigUpdate(BaseModel): compliance_rules: Optional[Dict[str, Any]] = None default_format: Optional[str] = None is_active: Optional[bool] = None + requires_image: Optional[bool] = None + image_count_min: Optional[int] = None + image_count_max: Optional[int] = None + image_width: Optional[int] = None + image_height: Optional[int] = None + min_words: Optional[int] = None + max_words: Optional[int] = None class PlatformConfigResponse(PlatformConfigBase): diff --git a/platform/frontend/platforms.html b/platform/frontend/platforms.html index 6a38f5e..8a52c7c 100644 --- a/platform/frontend/platforms.html +++ b/platform/frontend/platforms.html @@ -78,52 +78,74 @@ 平台标识 {{ p.platform }} -
+
平台名称 - {{ p.platform_name }} + {{ p.name }}
字数限制 {{ p.min_words || 0 }} - {{ p.max_words || '不限' }}
+
+ 需要配图 + {{ p.requires_image ? '是' : '否' }} +
-
-
格式规则
-
+
+
格式模板
+
{{ key }}: {{ typeof rule === 'object' ? JSON.stringify(rule) : rule }}
-
+
合规规则
-
{{ rule }}
+
+ {{ key }}: {{ typeof val === 'object' ? JSON.stringify(val) : val }} +
- - + + - + - - + + - - + 字数与配图 + + - - - - - + + + + + +