89 lines
4.1 KiB
Python
89 lines
4.1 KiB
Python
"""用户私有产品小册子模型。"""
|
|
import json
|
|
|
|
from sqlalchemy import BigInteger, Column, Integer, SmallInteger, String, Text, TIMESTAMP, func
|
|
|
|
from insurance.db.compat import db
|
|
|
|
|
|
class UserProductMaterial(db.Model):
|
|
"""用户上传、解析并确认的产品小册子。"""
|
|
|
|
__tablename__ = "insurance_user_product_materials"
|
|
|
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
|
owner_user_id = Column(String(64), nullable=False, index=True, comment="上传用户 ID")
|
|
tenant_id = Column(String(64), nullable=False, default="default", comment="租户 ID")
|
|
company_id = Column(String(50), nullable=True, comment="关联保司 ID")
|
|
company_name = Column(String(100), nullable=True, comment="保司名称")
|
|
product_name = Column(String(150), nullable=True, comment="产品名称")
|
|
plan_type = Column(String(20), nullable=True, comment="savings/ci/iul/other")
|
|
original_name = Column(String(200), nullable=False, comment="原始文件名")
|
|
file_key = Column(String(500), nullable=False, comment="相对 storage 根目录的文件键")
|
|
file_size = Column(BigInteger, nullable=False, default=0, comment="文件字节数")
|
|
page_count = Column(Integer, nullable=True, comment="PDF 页数")
|
|
sha256 = Column(String(64), nullable=False, comment="文件 SHA-256")
|
|
parse_status = Column(String(20), nullable=False, default="pending")
|
|
parse_message = Column(String(500), nullable=False, default="")
|
|
parse_error = Column(Text, nullable=True)
|
|
parse_task_id = Column(String(200), nullable=True)
|
|
parse_started_at = Column(TIMESTAMP, nullable=True)
|
|
parse_finished_at = Column(TIMESTAMP, nullable=True)
|
|
parsed_rules = Column(Text, nullable=True, comment="系统解析结果 JSON")
|
|
confirmed_rules = Column(Text, nullable=True, comment="用户确认结果 JSON")
|
|
confirmed_by = Column(String(64), nullable=True)
|
|
confirmed_at = Column(TIMESTAMP, nullable=True)
|
|
review_status = Column(String(20), nullable=False, default="private")
|
|
status = Column(SmallInteger, nullable=False, default=1)
|
|
deleted_at = Column(TIMESTAMP, nullable=True)
|
|
created_at = Column(TIMESTAMP, server_default=func.now())
|
|
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
|
|
|
|
@staticmethod
|
|
def _safe_json(value):
|
|
if not value:
|
|
return None
|
|
try:
|
|
return json.loads(value)
|
|
except (json.JSONDecodeError, TypeError):
|
|
return None
|
|
|
|
@staticmethod
|
|
def _safe_parse_error(value):
|
|
if not value:
|
|
return ""
|
|
message = str(value).strip()
|
|
unsafe_markers = (
|
|
"traceback", "file \"", "\\", "/app/", "/api/",
|
|
"http://", "https://", "api_key", "token=",
|
|
)
|
|
if "\n" in message or any(marker in message.lower() for marker in unsafe_markers):
|
|
return "解析失败,请重试;如多次失败请联系管理员"
|
|
return message[:300]
|
|
|
|
def to_dict(self):
|
|
return {
|
|
"id": self.id,
|
|
"ownerUserId": self.owner_user_id,
|
|
"tenantId": self.tenant_id,
|
|
"companyId": self.company_id,
|
|
"companyName": self.company_name or "",
|
|
"productName": self.product_name or "",
|
|
"displayName": self.product_name or self.original_name,
|
|
"planType": self.plan_type or "other",
|
|
"originalName": self.original_name,
|
|
"fileSize": self.file_size or 0,
|
|
"pageCount": self.page_count,
|
|
"parseStatus": self.parse_status,
|
|
"parseMessage": self.parse_message or "",
|
|
"parseError": self._safe_parse_error(self.parse_error),
|
|
"parsedRules": self._safe_json(self.parsed_rules),
|
|
"confirmedRules": self._safe_json(self.confirmed_rules),
|
|
"confirmedBy": self.confirmed_by,
|
|
"confirmedAt": self.confirmed_at.isoformat() if self.confirmed_at else None,
|
|
"reviewStatus": self.review_status,
|
|
"status": self.status,
|
|
"createdAt": self.created_at.isoformat() if self.created_at else None,
|
|
"updatedAt": self.updated_at.isoformat() if self.updated_at else None,
|
|
}
|