from datetime import date from sqlalchemy import Boolean, Date, ForeignKey, Index, Integer, Numeric, String, Text, text from sqlalchemy.orm import Mapped, declared_attr, mapped_column from app.core.base_model import ModelMixin, UserMixin class BreedingGermplasmModel(ModelMixin, UserMixin): """亲本资源表(桃育种) 依据: doc/桃育种系统业务功能规划报告.md 模块1 审计字段(id/uuid/is_deleted/created_time/updated_time/deleted_time/ created_id/updated_id/deleted_id)由 ModelMixin/UserMixin 提供。 说明: 本表无 status 字段, 故覆盖基类 __table_args__, 仅保留 (created_time, is_deleted) 索引, 避免基类对 status 列建索引而报错。 """ __tablename__ = "bre_germplasm" @declared_attr.directive def __table_args__(cls) -> tuple: table_name = cls.__tablename__ return ( Index(f"ix_{table_name}_created_deleted", "created_time", "is_deleted"), # 品种/种质名称唯一(仅约束未软删记录,与 service 层查重语义一致) Index( f"uniq_{table_name}_cultivar_name", "cultivar_name", unique=True, postgresql_where=text("is_deleted = false"), ), # 登记号唯一(FAO-MCPD 护照块,规格 §4),仅约束未软删记录 Index( f"uniq_{table_name}_accession_no", "accession_no", unique=True, postgresql_where=text("is_deleted = false"), ), {"comment": "亲本资源表(桃育种)"}, ) cultivar_name: Mapped[str] = mapped_column(String(100), nullable=False, comment="品种/种质名称") accession_no: Mapped[str | None] = mapped_column(String(64), nullable=True, comment="登记号(引种/库存登记)") variety_type: Mapped[str | None] = mapped_column(String(20), nullable=True, comment="变种类型(普通桃/油桃/蟠桃/油蟠桃/黄肉)") origin: Mapped[str | None] = mapped_column(String(200), nullable=True, comment="来源") preservation_site: Mapped[str | None] = mapped_column(String(200), nullable=True, comment="保存地") avg_fruit_weight: Mapped[float | None] = mapped_column(Numeric(8, 2), nullable=True, comment="平均果重(g)") ssc: Mapped[float | None] = mapped_column(Numeric(4, 1), nullable=True, comment="可溶性固形物(%)") firmness: Mapped[str | None] = mapped_column(String(20), nullable=True, comment="硬度") maturity_period: Mapped[str | None] = mapped_column(String(20), nullable=True, comment="成熟期(早/中/晚)") flowering_period: Mapped[str | None] = mapped_column(String(20), nullable=True, comment="花期(粗粒度5档字典)") bloom_start_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="花期起始(结构化区间,年际重复,取月日比较)") bloom_end_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="花期结束(结构化区间,年际重复,取月日比较)") chilling_requirement: Mapped[int | None] = mapped_column(Integer, nullable=True, comment="需冷量(h)") disease_resistance: Mapped[str | None] = mapped_column(Text, nullable=True, comment="抗病性描述") s_alleles: Mapped[str | None] = mapped_column(String(32), nullable=True, comment="S-等位基因(如 S1/S3;Sf=自交亲和型)") can_be_female: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, comment="可作母本") can_be_male: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, comment="可作父本") pedigree_note: Mapped[str | None] = mapped_column(Text, nullable=True, comment="系谱备注") photo_path: Mapped[str | None] = mapped_column(String(500), nullable=True, comment="照片地址(图片URL)") # ---- 护照块(规格 §4 line 558) ---- stage: Mapped[str | None] = mapped_column(String(32), nullable=True, comment="当前育种阶段(breeding_stage)") generation: Mapped[str | None] = mapped_column(String(16), nullable=True, comment="世代(breeding_generation)") storage_type: Mapped[str | None] = mapped_column(String(16), nullable=True, comment="保存方式(田间/离体/种子/DNA)") is_rootstock: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, comment="是否砧木材料") rootstock_id: Mapped[int | None] = mapped_column( Integer, ForeignKey("bre_germplasm.id"), nullable=True, comment="砧木材料(自关联,引种砧木时指向砧木种质)" ) institute_code: Mapped[str | None] = mapped_column(String(64), nullable=True, comment="保存机构代码") country_origin: Mapped[str | None] = mapped_column(String(64), nullable=True, comment="原产国/原产地") collection_site: Mapped[str | None] = mapped_column(String(200), nullable=True, comment="采集地点") acquisition_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="引种/收集日期") biological_status: Mapped[str | None] = mapped_column(String(32), nullable=True, comment="生物学状态(wild/landrace/breeding_line)") breeding_program: Mapped[str | None] = mapped_column(String(128), nullable=True, comment="所属育种项目(自由文本,按规格 v1.6)")