"""moldinsight 域模型:老师傅经验反馈表。 Human-in-Loop 闭环(D17,2026-09):老师傅对系统推荐方案给出"采纳 / 调整 / 拒绝" 反馈,按"产品指纹 + 工艺参数"为索引跨任务匹配;下次同指纹产品分析自动消费 (OCC worker payload 透传 → MultiSchemeMoldPlanner → PartingSchemeScorer 加成)。 跨模块桥接只保留裸 FK,不建 ORM relationship(base.py 约定): - processing_task_id -> processing_tasks.id - stp_file_id -> stp_files.id - user_id -> users.id 写入 feedback 时由 service 层填充 fingerprint JSON(bbox_aspect / volume_bucket / face_bucket / undercut_class / material_family / is_foam)。fingerprint 是跨任务 匹配的索引列(PG 下有 GIN 索引支持 jsonb_path_query 类查询)。 """ from sqlalchemy import Column, Integer, String, Text, DateTime, JSON, Float, ForeignKey, Index from sqlalchemy.sql import func from shared.models.base import Base class ExperienceFeedback(Base): """老师傅经验反馈:方案级整体反馈 + 跨任务指纹匹配。 关键字段: - feedback_status: 'adopted' / 'adjust' / 'rejected' - fingerprint: 跨任务匹配键,结构见 moldinsight.services.experience_feedback_service - expires_at: 90 天 TTL;写新反馈时同 stp_file_id 整体续期(D17 衰减机制) """ __tablename__ = "experience_feedback" id = Column(Integer, primary_key=True, index=True) # ── 任务归属(跨模块裸 FK,CASCADE 随任务 / 文件清理)── processing_task_id = Column( Integer, ForeignKey("processing_tasks.id", ondelete="CASCADE"), nullable=False, index=True, ) stp_file_id = Column( Integer, ForeignKey("stp_files.id", ondelete="CASCADE"), nullable=False, index=True, ) # ── 方案标识 ── scheme_id = Column(String(64), nullable=False, index=True) scheme_axis = Column(String(1), nullable=False) scheme_method = Column(String(50), nullable=True) # ── 反馈主体 ── feedback_status = Column(String(20), nullable=False, index=True) feedback_reason = Column(Text, nullable=True) adjust_suggestion = Column(Text, nullable=True) # ── 上下文快照(用于回放)── process_params_snapshot = Column(JSON, nullable=True) fingerprint = Column(JSON, nullable=False) confidence_at_submit = Column(Float, nullable=True) score_at_submit = Column(Float, nullable=True) # ── 审计(user_id RESTRICT:禁止级联删,保留审计归因)── user_id = Column( Integer, ForeignKey("users.id", ondelete="RESTRICT"), nullable=False, index=True, ) role_code = Column(String(50), nullable=False) # ── 时间戳与衰减 ── created_at = Column(DateTime, default=func.now(), index=True) expires_at = Column(DateTime, nullable=True, index=True) __table_args__ = ( Index( "ix_experience_feedback_stp_axis_status", "stp_file_id", "scheme_axis", "feedback_status", ), ) def __repr__(self) -> str: return ( f"" )