baodan/api/insurance/models/ppt_config.py

286 lines
15 KiB
Python
Raw 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.

"""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="脱敏展示名")
masking_enabled = Column(Boolean, default=False, nullable=False, 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 图片地址")
logo_enabled = Column(Boolean, default=True, nullable=False, 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())
deleted_at = Column(TIMESTAMP, nullable=True, comment="软删除时间")
def to_dict(self):
import json
return {
"id": self.id,
"displayName": self.display_name,
"maskedDisplayName": self.masked_display_name or "",
"maskingEnabled": bool(self.masking_enabled),
"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,
"logoEnabled": bool(self.logo_enabled),
"status": self.status,
"sortOrder": self.sort_order,
"deletedAt": self.deleted_at.isoformat() if self.deleted_at else None,
}
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="脱敏展示名")
masking_enabled = Column(Boolean, default=False, nullable=False, 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())
deleted_at = Column(TIMESTAMP, nullable=True, comment="软删除时间")
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 "",
"maskingEnabled": bool(self.masking_enabled),
"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,
"deletedAt": self.deleted_at.isoformat() if self.deleted_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(255), nullable=True, comment="源模板资产 ID")
asset_sha256 = Column(String(64), nullable=True, comment="当前模板资产 SHA-256")
asset_version = Column(Integer, default=1, comment="当前模板资产版本")
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())
deleted_at = Column(TIMESTAMP, nullable=True, comment="软删除时间")
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,
"assetSha256": self.asset_sha256,
"assetVersion": self.asset_version or 1,
"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,
"deletedAt": self.deleted_at.isoformat() if self.deleted_at else None,
}
class PptScenario(db.Model):
"""PPT 生成场景;场景分类与底层计算模式分离。"""
__tablename__ = "insurance_ppt_scenarios"
code = Column(String(50), primary_key=True)
name = Column(String(100), nullable=False)
base_scenario = Column(String(50), nullable=True)
generation_mode = Column(String(20), nullable=False, default="single")
description = Column(Text, nullable=True)
status = Column(SmallInteger, default=1, nullable=False)
sort_order = Column(Integer, default=0, nullable=False)
is_builtin = Column(Boolean, default=False, nullable=False)
created_at = Column(TIMESTAMP, server_default=func.now())
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP, nullable=True)
def to_dict(self):
return {
"code": self.code,
"name": self.name,
"baseScenario": self.base_scenario,
"generationMode": self.generation_mode,
"description": self.description or "",
"status": self.status,
"sortOrder": self.sort_order,
"isBuiltIn": bool(self.is_builtin),
"deletedAt": self.deleted_at.isoformat() if self.deleted_at else None,
}
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,
}