240 lines
13 KiB
Python
240 lines
13 KiB
Python
"""PPT 配置模型(公司、产品、模板)。"""
|
||
import uuid
|
||
from sqlalchemy import Column, String, Text, Boolean, Integer, SmallInteger, TIMESTAMP, func
|
||
from insurance.db.compat import db
|
||
|
||
|
||
class PptCompany(db.Model):
|
||
"""保险公司配置表。"""
|
||
__tablename__ = "insurance_ppt_companies"
|
||
|
||
id = Column(String(50), primary_key=True, comment="公司 ID,如 aia/ctf/fwd")
|
||
display_name = Column(String(100), nullable=False, comment="显示名称")
|
||
aliases_json = Column(Text, nullable=True, comment="别名列表 JSON")
|
||
tenant_id = Column(String(50), default="default", comment="租户 ID")
|
||
knowledge_directories_json = Column(Text, nullable=True, comment="知识库目录 JSON")
|
||
evidence_ranking_json = Column(Text, nullable=True, comment="证据排序关键词 JSON")
|
||
company_intro = Column(Text, nullable=True, comment="公司简介")
|
||
company_highlights_json = Column(Text, nullable=True, comment="公司亮点 JSON")
|
||
# 脱敏展示名(PPT/海报导出时替代真实名称)
|
||
masked_display_name = Column(String(100), nullable=True, comment="脱敏展示名")
|
||
# 品牌信息
|
||
name_zh = Column(String(100), nullable=True, comment="中文名称")
|
||
name_en = Column(String(100), nullable=True, comment="英文名称")
|
||
short_en = Column(String(20), nullable=True, comment="英文缩写")
|
||
rating = Column(String(50), nullable=True, comment="评级")
|
||
founded_year = Column(Integer, nullable=True, comment="成立年份")
|
||
# 扩展字段(PPT/海报功能)
|
||
logo_url = Column(String(500), nullable=True, comment="公司 Logo 图片地址")
|
||
status = Column(SmallInteger, default=1, comment="1=启用, 0=停用")
|
||
sort_order = Column(Integer, default=0, comment="排序权重")
|
||
created_at = Column(TIMESTAMP, server_default=func.now())
|
||
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
|
||
|
||
def to_dict(self):
|
||
import json
|
||
return {
|
||
"id": self.id,
|
||
"displayName": self.display_name,
|
||
"maskedDisplayName": self.masked_display_name or "",
|
||
"aliases": json.loads(self.aliases_json) if self.aliases_json else [],
|
||
"tenantId": self.tenant_id,
|
||
"knowledgeDirectories": json.loads(self.knowledge_directories_json) if self.knowledge_directories_json else [],
|
||
"evidenceRanking": json.loads(self.evidence_ranking_json) if self.evidence_ranking_json else [],
|
||
"companyIntro": self.company_intro,
|
||
"companyHighlights": json.loads(self.company_highlights_json) if self.company_highlights_json else [],
|
||
"rating": self.rating,
|
||
"foundedYear": self.founded_year,
|
||
"nameZh": self.name_zh,
|
||
"nameEn": self.name_en,
|
||
"shortEn": self.short_en,
|
||
"logoUrl": self.logo_url,
|
||
"status": self.status,
|
||
"sortOrder": self.sort_order,
|
||
}
|
||
|
||
|
||
class PptProduct(db.Model):
|
||
"""保险产品配置表。"""
|
||
__tablename__ = "insurance_ppt_products"
|
||
|
||
id = Column(String(50), primary_key=True, comment="产品 ID")
|
||
company_id = Column(String(50), nullable=False, comment="所属公司 ID")
|
||
plan_type = Column(String(10), nullable=False, comment="产品类型: savings/ci/iul")
|
||
display_name = Column(String(100), nullable=False, comment="显示名称")
|
||
aliases_json = Column(Text, nullable=True, comment="别名列表 JSON")
|
||
required_modules_json = Column(Text, nullable=True, comment="所需模块 JSON")
|
||
# 脱敏展示名(PPT/海报导出时替代真实名称)
|
||
masked_display_name = Column(String(100), nullable=True, comment="脱敏展示名")
|
||
# 扩展字段
|
||
product_code = Column(String(50), nullable=True, comment="产品编码")
|
||
product_type = Column(String(20), nullable=True, comment="产品类型: savings/ci/iul")
|
||
coverage_period = Column(String(50), nullable=True, comment="保障期限")
|
||
payment_period = Column(String(50), nullable=True, comment="缴费期限")
|
||
insured_age_range = Column(String(50), nullable=True, comment="投保年龄范围")
|
||
waiting_period = Column(String(50), nullable=True, comment="等待期")
|
||
highlights = Column(Text, nullable=True, comment="产品亮点 JSON")
|
||
extra_fields = Column(Text, nullable=True, comment="扩展字段 JSON")
|
||
status = Column(SmallInteger, default=1, comment="1=启用, 0=停用")
|
||
sort_order = Column(Integer, default=0, comment="排序")
|
||
# 海报相关
|
||
manual_file_url = Column(String(500), nullable=True, comment="产品小册子 PDF 地址")
|
||
manual_parse_status = Column(String(20), default="none", comment="none/pending/queued/parsing/parsed/reviewed/failed")
|
||
manual_parse_message = Column(String(500), default="", comment="解析进度消息")
|
||
manual_parse_error = Column(Text, nullable=True, comment="解析失败原因")
|
||
manual_parse_task_id = Column(String(200), nullable=True, comment="Celery 任务ID")
|
||
manual_parse_started_at = Column(TIMESTAMP, nullable=True, comment="解析开始时间")
|
||
manual_parse_finished_at = Column(TIMESTAMP, nullable=True, comment="解析完成时间")
|
||
manual_parsed_rules = Column(Text, nullable=True, comment="解析出的产品规则 JSON")
|
||
manual_reviewed_by = Column(String(50), nullable=True, comment="核对人")
|
||
manual_reviewed_at = Column(TIMESTAMP, nullable=True, comment="核对时间")
|
||
created_at = Column(TIMESTAMP, server_default=func.now())
|
||
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
|
||
|
||
def to_dict(self):
|
||
import json
|
||
return {
|
||
"id": self.id,
|
||
"companyId": self.company_id,
|
||
"planType": self.plan_type,
|
||
"displayName": self.display_name,
|
||
"maskedDisplayName": self.masked_display_name or "",
|
||
"aliases": json.loads(self.aliases_json) if self.aliases_json else [],
|
||
"requiredModules": json.loads(self.required_modules_json) if self.required_modules_json else [],
|
||
"productCode": self.product_code,
|
||
"productType": self.product_type,
|
||
"coveragePeriod": self.coverage_period,
|
||
"paymentPeriod": self.payment_period,
|
||
"insuredAgeRange": self.insured_age_range,
|
||
"waitingPeriod": self.waiting_period,
|
||
"highlights": json.loads(self.highlights) if self.highlights else [],
|
||
"extraFields": json.loads(self.extra_fields) if self.extra_fields else {},
|
||
"status": self.status,
|
||
"sortOrder": self.sort_order,
|
||
"manualFileUrl": self.manual_file_url,
|
||
"manualParseStatus": self.manual_parse_status,
|
||
"manualParseMessage": self.manual_parse_message or "",
|
||
"manualParseError": self.manual_parse_error or "",
|
||
"manualParseTaskId": self.manual_parse_task_id,
|
||
"manualParseStartedAt": self.manual_parse_started_at.isoformat() if self.manual_parse_started_at else None,
|
||
"manualParseFinishedAt": self.manual_parse_finished_at.isoformat() if self.manual_parse_finished_at else None,
|
||
"manualParsedRules": json.loads(self.manual_parsed_rules) if self.manual_parsed_rules else None,
|
||
"manualReviewedBy": self.manual_reviewed_by,
|
||
"manualReviewedAt": self.manual_reviewed_at.isoformat() if self.manual_reviewed_at else None,
|
||
}
|
||
|
||
|
||
class PptTemplate(db.Model):
|
||
"""PPT 模板配置表。"""
|
||
__tablename__ = "insurance_ppt_templates"
|
||
|
||
id = Column(String(50), primary_key=True, comment="模板 ID")
|
||
plan_type = Column(String(10), nullable=False, comment="产品类型: savings/ci/iul")
|
||
style_preset = Column(String(20), nullable=False, comment="风格: broker/business/minimal/chinese/ink")
|
||
source_template_asset_id = Column(String(50), nullable=True, comment="源模板资产 ID")
|
||
clone_ready = Column(Boolean, default=False, comment="是否克隆就绪")
|
||
clone_renderer = Column(String(100), nullable=True, comment="克隆渲染器 ID")
|
||
required_page_types_json = Column(Text, nullable=True, comment="必需页面类型 JSON")
|
||
# 扩展字段
|
||
name = Column(String(100), nullable=True, comment="模板名称")
|
||
scenario_tag = Column(String(50), nullable=True, comment="场景标签")
|
||
preview_image = Column(String(500), nullable=True, comment="预览图地址")
|
||
applicable_company_ids = Column(Text, nullable=True, comment="适用保司 ID 列表 JSON")
|
||
applicable_product_ids = Column(Text, nullable=True, comment="适用产品 ID 列表 JSON")
|
||
slides_config_json = Column(Text, nullable=True, comment="逐页幻灯片配置 JSON")
|
||
status = Column(SmallInteger, default=1, comment="1=启用, 0=停用")
|
||
created_at = Column(TIMESTAMP, server_default=func.now())
|
||
|
||
def to_dict(self):
|
||
import json
|
||
try:
|
||
slides = json.loads(self.slides_config_json) if self.slides_config_json else []
|
||
except (json.JSONDecodeError, TypeError):
|
||
slides = []
|
||
try:
|
||
required_types = json.loads(self.required_page_types_json) if self.required_page_types_json else []
|
||
except (json.JSONDecodeError, TypeError):
|
||
required_types = []
|
||
try:
|
||
company_ids = json.loads(self.applicable_company_ids) if self.applicable_company_ids else []
|
||
except (json.JSONDecodeError, TypeError):
|
||
company_ids = []
|
||
try:
|
||
product_ids = json.loads(self.applicable_product_ids) if self.applicable_product_ids else []
|
||
except (json.JSONDecodeError, TypeError):
|
||
product_ids = []
|
||
return {
|
||
"id": self.id,
|
||
"planType": self.plan_type,
|
||
"stylePreset": self.style_preset,
|
||
"sourceTemplateAssetId": self.source_template_asset_id,
|
||
"sourceFileUrl": (
|
||
f"/insurance/admin/ppt/templates/{self.id}/file"
|
||
if self.source_template_asset_id else ""
|
||
),
|
||
"isBuiltIn": str(self.source_template_asset_id or "").startswith("builtin://"),
|
||
"cloneReady": self.clone_ready,
|
||
"cloneRenderer": self.clone_renderer,
|
||
"requiredPageTypes": required_types,
|
||
"name": self.name,
|
||
"scenarioTag": self.scenario_tag,
|
||
"previewImage": self.preview_image,
|
||
"applicableCompanyIds": company_ids,
|
||
"applicableProductIds": product_ids,
|
||
"slidesConfig": slides,
|
||
"slideCount": len(slides),
|
||
"status": self.status,
|
||
}
|
||
|
||
|
||
class PptBundle(db.Model):
|
||
"""PPT Bundle 组合配置表。"""
|
||
__tablename__ = "insurance_ppt_bundles"
|
||
|
||
id = Column(String(50), primary_key=True, comment="Bundle ID")
|
||
display_name = Column(String(100), nullable=False, comment="显示名称")
|
||
products_json = Column(Text, nullable=False, comment="产品类型列表 JSON")
|
||
template_family = Column(String(50), default="bundle", comment="模板族")
|
||
status = Column(String(20), default="active", comment="状态: active/inactive")
|
||
modules_json = Column(Text, nullable=True, comment="模块列表 JSON")
|
||
created_at = Column(TIMESTAMP, server_default=func.now())
|
||
|
||
def to_dict(self):
|
||
import json
|
||
return {
|
||
"id": self.id,
|
||
"displayName": self.display_name,
|
||
"products": json.loads(self.products_json) if self.products_json else [],
|
||
"templateFamily": self.template_family,
|
||
"status": self.status,
|
||
"modules": json.loads(self.modules_json) if self.modules_json else [],
|
||
}
|
||
|
||
|
||
class CompanyLogo(db.Model):
|
||
"""保司Logo图片表(支持多张)。"""
|
||
__tablename__ = "insurance_ppt_company_logos"
|
||
|
||
id = Column(String(36), primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
company_id = Column(String(50), nullable=False, index=True, comment="所属公司 ID")
|
||
file_path = Column(String(500), nullable=False, comment="服务器文件路径")
|
||
file_url = Column(String(500), nullable=True, comment="可访问的URL")
|
||
original_name = Column(String(200), nullable=True, comment="原始文件名")
|
||
mime_type = Column(String(50), nullable=True, comment="MIME类型")
|
||
file_size = Column(Integer, nullable=True, comment="文件大小(字节)")
|
||
is_primary = Column(Boolean, default=False, comment="是否为主Logo")
|
||
sort_order = Column(Integer, default=0, comment="排序权重")
|
||
created_at = Column(TIMESTAMP, server_default=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"companyId": self.company_id,
|
||
"url": self.file_url or f"/insurance/admin/ppt/assets/company-logos/{self.id}",
|
||
"originalName": self.original_name or "",
|
||
"mimeType": self.mime_type,
|
||
"fileSize": self.file_size,
|
||
"isPrimary": self.is_primary,
|
||
"sortOrder": self.sort_order,
|
||
}
|