# Phenotyping 模块专业数据录入需求文档 V2 ## 1. 文档目的 本文档用于指导 Phenotyping 表型模块的前端页面、后端接口、字段校验、数据导入和测试验收设计。本文档不只描述表关系,而是从真实育种表型采集业务出发,解释每个对象和字段的业务意义、录入方式、控件建议、校验规则和上下游影响。 ## 2. 模块定位 Phenotyping 模块用于管理田间、温室、实验室或高通量设备采集到的表型数据。它的核心不是“录一张 observation 表”,而是完整描述: ```text 在哪个 study 里 对哪个 observation_unit 按照哪个 observation_variable 在什么时间、由谁、用什么方法 采集到了什么 value 并能关联事件、图片、坐标和上下文 ``` ## 3. 核心业务概念 | 概念 | 业务含义 | 关键表 | | ---------------------------- | ---------------------------------------------------------- | ------------------------------------------------- | | Ontology 本体 | 性状、方法、标尺、变量的术语来源,可以是本地本体或公开本体 | `ontology` | | Trait 性状 | 测什么。例如株高、穗长、病害等级、籽粒颜色 | `trait` | | Method 方法 | 怎么测。例如尺测、人工评分、无人机图像分析、实验室检测 | `method` | | Scale 标尺 | 用什么单位或尺度表达。例如 cm、kg、0-5 等级、文本、布尔值 | `scale`、`scale_valid_value_category` | | ObservationVariable 观测变量 | Trait + Method + Scale 的组合,表示一个可采集指标 | `observation_variable` | | StudyVariable 研究采集指标 | 某个 study 计划采集哪些 observation variable | `study_variable` | | ObservationUnit 观测单元 | 被观测对象。通常是 plot、plant、block、field、sample | `observation_unit` | | Event 事件 | 管理动作或环境事件,如施肥、灌溉、打药、移栽、极端天气 | `event`、`event_param`、`event_observation_units` | | Image 图像 | 田间图片、无人机图片、病害图片、长势图像等 | `image`、`image_observations` | | Observation 观测值 | 某个观测单元在某个变量上的一次实际测量结果 | `observation` | ## 4. 推荐业务流程 ```text 1. 准备 Core 上下文:crop、program、trial、study、location、season 2. 维护或导入本体 ontology 3. 定义 trait / method / scale 4. 组合 observation_variable 5. 把 observation_variable 绑定到 study_variable,形成本 study 的采集指标清单 6. 根据田间设计生成 observation_unit,例如 block / plot / plant 7. 给 observation_unit 绑定材料来源:germplasm / seed_lot / cross 8. 记录 event,例如播种、移栽、施肥、灌溉、打药、采样、极端天气 9. 通过矩阵方式批量录入 observation 10. 上传 image,并关联 observation_unit 或 observation 11. 做数据质控:缺失值、异常值、重复采集、单位和分类值校验 ``` ## 5. 推荐页面形态 ### 5.1 Study 表型工作台 ```text Study 表型工作台 ├─ 试验上下文:study / trial / program / crop / location / season ├─ 指标清单:study_variable ├─ 田间设计:observation_unit 层级、block、plot、plant、row、column ├─ 材料布置:germplasm、seed_lot、cross ├─ 表型矩阵:observation_unit × observation_variable ├─ 田间事件:event + event_param + event_observation_units ├─ 图片证据:image + image_observations ├─ 数据质控:缺失值、异常值、重复值、单位校验、分类值校验 └─ 导入导出:观测单元模板、表型采集模板、图片元数据模板 ``` ### 5.2 表型矩阵录入 真实采集时不应只提供单条 observation 表单,而应支持矩阵录入: | observation_unit | germplasm | 株高 cm | 穗长 cm | 倒伏等级 | 备注 | | ---------------- | --------- | ------: | ------: | -------: | ---------- | | Plot-001 | 华占 | 112.5 | 24.1 | 1 | 正常 | | Plot-002 | 抗倒伏A | 98.3 | 22.8 | 0 | 正常 | | Plot-003 | F2-001 | 101.6 | 23.0 | 4 | 台风后倒伏 | 保存时,系统按每个非空单元格生成一条 `observation`,每一列对应一个 `observation_variable`。 --- # 6. 字段级专业录入需求 ## 6.1 ontology 本体 ### 业务说明 `ontology` 表示术语体系来源,用来说明 trait、method、scale、observation_variable 的定义来自哪里。它可以是本地维护的术语库,也可以是公开 Crop Ontology、Trait Ontology 或机构内部标准。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ------------------ | ----------------------------------------- | -------------------------------- | --------------- | -------------------------------- | | `id` | 本体主键,系统内部唯一标识 | 新增时系统生成;导入时可允许指定 | 隐藏/只读 | 必填、唯一;编辑时不可修改 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许前端手填 | | `authors` | 本体作者或维护者 | 用户录入 | 文本框 | 可选;多个作者可用分号或数组扩展 | | `copyright` | 本体版权说明 | 用户录入 | 文本框/多行文本 | 可选 | | `description` | 本体用途说明 | 用户录入 | 多行文本 | 可选,建议填写 | | `documentationurl` | 本体文档链接 | 用户录入 | URL 输入框 | 可选;若填写必须是合法 URL | | `licence` | 本体许可证,例如 CC-BY、MIT、机构内部许可 | 用户录入/选择 | 文本框/下拉框 | 可选 | | `ontology_name` | 本体名称,例如 Rice Trait Ontology | 用户录入 | 文本框 | 必填;同一用户下建议唯一 | | `version` | 本体版本,例如 2024.1、v2.0 | 用户录入 | 文本框 | 可选;建议与外部本体版本一致 | ### 录入建议 - 若系统只是内部使用,可先创建一个“默认本体”或“本地表型本体”。 - 若对接 Crop Ontology,建议保存 ontology 名称、版本、文档 URL。 - 本体删除前必须检查是否被 trait、method、scale、observation_variable 引用。 --- ## 6.2 trait 性状 ### 业务说明 `trait` 描述“测什么”。例如株高、叶长、穗长、倒伏等级、病害严重度、籽粒颜色。专业上 trait 可以拆成 `entity` 和 `attribute`: ```text Trait = Entity + Attribute 例如:grain colour entity = grain attribute = colour ``` ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ------------------- | ----------------------------------------------------- | -------------------- | ----------------- | ---------------------------------------- | | `id` | 性状主键,系统内部唯一标识 | 系统生成;导入可指定 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `attribute` | 性状属性,即被观察的特征,如 height、colour、severity | 用户录入/本体选择 | 文本框/本体选择器 | 可选;若填写 PUI,建议同步填写 attribute | | `attributepui` | 属性永久唯一标识,通常是 URI | 用户录入/本体带出 | 文本框/URL 输入框 | 可选;若填写建议唯一 | | `entity` | 性状实体,即观察对象部位,如 plant、leaf、grain、root | 用户录入/本体选择 | 文本框/本体选择器 | 可选 | | `entitypui` | 实体永久唯一标识,通常是 URI | 用户录入/本体带出 | 文本框/URL 输入框 | 可选;若填写建议唯一 | | `main_abbreviation` | 性状主缩写,如 PH、GY、DTH | 用户录入 | 文本框 | 可选;同一 ontology 下建议唯一 | | `status` | 性状状态,如 recommended、obsolete、legacy | 用户选择 | 下拉框 | 可选;推荐使用枚举 | | `trait_class` | 性状分类,如 phenology、morphology、disease、quality | 用户选择/录入 | 下拉框/文本框 | 可选;建议字典化 | | `trait_description` | 性状定义和说明 | 用户录入 | 多行文本 | 可选,但专业使用强烈建议填写 | | `trait_name` | 性状名称,给用户看的名称 | 用户录入 | 文本框 | 必填;作为下拉展示名称 | | `traitpui` | 性状永久唯一标识 | 用户录入/本体带出 | 文本框/URL 输入框 | 可选;若填写必须唯一 | | `ontology_id` | 所属本体 | 从 ontology 选择 | 本体选择器 | 可选;必须引用存在的 ontology | ### 录入建议 - 简单业务可只填:`trait_name`、`trait_description`、`trait_class`。 - 专业数据交换场景建议填写:`entity`、`attribute`、`traitpui`、`ontology_id`。 - 不要把单位写进 trait。比如“株高 cm”不应该作为 trait,正确拆分是 trait=株高,scale=cm。 --- ## 6.3 method 方法 ### 业务说明 `method` 描述“怎么测”。同一个 trait 用不同方法测,会形成不同 observation_variable。例如: ```text Trait: plant height Method 1: tape measure Method 2: drone image processing Scale: cm ``` 这两个变量不能混为一谈,因为它们的数据来源、误差和可比性不同。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | -------------- | ------------------------------------------------------------ | ------------------ | ----------------- | -------------------------------- | | `id` | 方法主键 | 系统生成 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `description` | 方法说明,如测量位置、工具、采样规则 | 用户录入 | 多行文本 | 可选;建议必填 | | `formula` | 如果该方法通过计算得到结果,填写公式 | 用户录入 | 文本框/公式编辑器 | 可选;需要格式提示 | | `method_class` | 方法分类,如 measurement、estimation、computed、image_analysis | 用户选择/录入 | 下拉框/文本框 | 可选;建议字典化 | | `methodpui` | 方法永久唯一标识 | 用户录入/本体带出 | 文本框/URL 输入框 | 可选;若填写建议唯一 | | `name` | 方法名称 | 用户录入 | 文本框 | 必填;同一 ontology 下不建议重复 | | `reference` | 方法参考文献、SOP、标准链接 | 用户录入 | 文本框/URL 输入框 | 可选;若为 URL 需校验格式 | | `ontology_id` | 所属本体 | 从 ontology 选择 | 本体选择器 | 可选;必须引用存在的 ontology | ### 录入建议 - 方法页面应重点让用户说明“测量步骤”,否则后续数据不可复现。 - 对于人工评分,必须在 description 中说明评分标准,并通过 scale 配置有效分类值。 - 对于高通量图像方法,reference 建议填写算法版本、模型版本或 SOP 链接。 --- ## 6.4 scale 标尺 ### 业务说明 `scale` 描述“用什么尺度表达结果”。它决定 observation.value 的合法值、单位、数据类型、小数位和分类值。常见类型: ```text Numerical: 株高 cm、产量 kg/ha Ordinal: 倒伏等级 0-5、病害等级 1-9 Categorical: 籽粒颜色 red/yellow/white Text: 描述性备注 Boolean: 是否开花、是否倒伏 Date: 开花日期 ``` ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ----------------- | ----------------------------------------------------- | ------------------ | ----------------- | --------------------------------- | | `id` | 标尺主键 | 系统生成 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `data_type` | 数据类型,决定 observation.value 的输入控件和校验方式 | 用户选择 | 下拉框 | 必填;使用系统枚举 | | `decimal_places` | 小数位数 | 用户录入 | 数字输入框 | 数值型可填;必须为非负整数 | | `scale_name` | 标尺名称,如 cm、0-5 lodging score、disease score 1-9 | 用户录入 | 文本框 | 必填;同一 ontology 下建议唯一 | | `scalepui` | 标尺永久唯一标识 | 用户录入/本体带出 | 文本框/URL 输入框 | 可选;若填写建议唯一 | | `units` | 单位,如 cm、mm、kg/ha、days | 用户选择/录入 | 单位选择器/文本框 | 数值型建议必填;分类型可为空 | | `valid_value_max` | 最大有效值 | 用户录入 | 数字输入框/文本框 | 数值/等级型校验;必须大于等于 min | | `valid_value_min` | 最小有效值 | 用户录入 | 数字输入框/文本框 | 数值/等级型校验;必须小于等于 max | | `ontology_id` | 所属本体 | 从 ontology 选择 | 本体选择器 | 可选;必须引用存在 ontology | ### 录入建议 - `data_type` 是动态表单核心字段。前端要根据它渲染 observation.value: - Numerical:数字输入框,按 min/max/decimal_places 校验; - Ordinal:数字或下拉,通常有 min/max; - Categorical:下拉框,选项来自 `scale_valid_value_category`; - Text:文本框; - Boolean:开关/单选; - Date:日期选择器。 - 如果 data_type 是分类型,必须至少配置一个 `scale_valid_value_category`。 - 如果 data_type 是数值型,建议填写单位和有效范围。 --- ## 6.5 scale_valid_value_category 分类有效值 ### 业务说明 `scale_valid_value_category` 是分类型或等级型 scale 的合法取值表。例如倒伏等级 0-5、病害等级 1-9、颜色 red/yellow/white。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ---------- | --------------------------------- | --------------------------- | ----------------- | ------------------------ | | `id` | 分类值主键 | 系统生成 | 隐藏/只读 | 必填、唯一 | | `label` | 用户界面展示文案,如 “0 - 不倒伏” | 用户录入 | 文本框 | 必填 | | `value` | 实际保存值,如 0、1、red | 用户录入 | 文本框 | 必填;同一 scale 内唯一 | | `scale_id` | 所属 scale | 从 scale 选择或由详情页带出 | Scale 选择器/隐藏 | 必选;必须引用存在 scale | ### 录入建议 - 在 scale 详情页中以内嵌表格维护。 - 保存 observation 时,如果 scale 是分类型,value 必须在该表中存在。 - label 可以给用户看,value 用于保存和导出,二者可以不同。 --- ## 6.6 observation_variable 观测变量 ### 业务说明 `observation_variable` 是真正进入采集模板的指标。它由 trait、method、scale 组合而成。 ```text observation_variable = trait + method + scale 例如:株高 + 尺子测量 + cm = 株高-尺测法-cm ``` 它不是观测结果,而是“可以被测的一列数据”。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ---------------------- | ------------------------------------------ | ------------------------ | ----------------- | ---------------------------------------- | | `id` | 观测变量主键 | 系统生成 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `default_value` | 默认观测值,例如默认等级、默认文本 | 用户录入 | 动态控件 | 可选;按 scale 校验 | | `documentationurl` | 变量说明文档链接 | 用户录入 | URL 输入框 | 可选;若填写必须合法 URL | | `growth_stage` | 建议采集生育期,如 flowering、maturity | 用户录入/选择 | 下拉框/文本框 | 可选;建议字典化 | | `institution` | 定义该变量的机构 | 用户录入 | 文本框 | 可选 | | `language` | 变量语言,如 zh、en | 用户选择 | 下拉框 | 可选;建议 ISO 639-1 | | `scientist` | 提交或维护该变量的科学家/负责人 | 用户录入/人员选择 | 文本框/选择器 | 可选 | | `status` | 变量状态,如 recommended、obsolete、legacy | 用户选择 | 下拉框 | 可选;obsolete 不建议用于新 study | | `submission_timestamp` | 变量提交时间 | 系统默认当前时间,可调整 | 日期时间选择器 | 可选;新增默认当前时间 | | `crop_id` | 适用作物 | 从 crop 选择 | 作物选择器 | 可选;如果填写,下游 study 应尽量同 crop | | `method_id` | 测定方法 | 从 method 选择 | 方法选择器 | 必选 | | `ontology_id` | 所属本体 | 从 ontology 选择 | 本体选择器 | 可选 | | `scale_id` | 标尺 | 从 scale 选择 | 标尺选择器 | 必选 | | `trait_id` | 被测性状 | 从 trait 选择 | 性状选择器 | 必选 | | `name` | 变量名称,采集矩阵列名 | 自动生成后允许修改 | 文本框 | 必填;同一 crop/ontology 下建议唯一 | | `pui` | 变量永久唯一标识 | 用户录入/本体带出 | 文本框/URL 输入框 | 可选;若填写建议唯一 | ### 录入建议 - 新建变量采用“三段式选择”:先选 trait,再选 method,再选 scale。 - 系统自动生成 name,例如:`trait_name + method_name + scale_name`,用户可修改。 - 变量详情页应展示:被哪些 study 使用、已有多少 observation、是否仍推荐使用。 - 如果变量已经产生 observation,不建议修改 trait/method/scale,只能停用旧变量并创建新变量。 --- ## 6.7 study_variable 研究采集指标 ### 业务说明 `study_variable` 是 study 和 observation_variable 的多对多关系,表示某个 study 计划采集哪些指标。它是“配置采集模板”的动作痕迹。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ---------------- | --------------------- | ---------------------------- | ----------------- | --------------------------- | | `study_db_id` | 所属 study | 从 study 选择或由工作台带出 | Study 选择器/隐藏 | 必选;必须引用存在 study | | `variable_db_id` | 该 study 要采集的变量 | 从 observation_variable 选择 | 变量多选器 | 必选;必须引用存在 variable | ### 录入建议 - 在 Study 表型工作台的“指标清单”Tab 中维护。 - 支持批量选择变量和从历史 study 复制变量清单。 - 若某变量已有 observation,不允许从 study_variable 中直接移除,除非先处理已有观测数据。 --- ## 6.8 observation_unit 观测单元 ### 业务说明 `observation_unit` 是“被观测对象”。通常是 plot 或 plant,也可以是 field、block、sub-plot、sample 等。它连接了 Core 上下文和材料来源,是表型数据的入口,也可以成为后续 genotyping sample 的来源。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ----------------------- | ------------------------------------ | ------------------------- | ----------------- | -------------------------------------------------- | | `id` | 观测单元主键 | 系统生成;导入可指定 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `observation_unit_name` | 观测单元名称,如 Plot-001、Plant-003 | 用户录入/批量生成 | 文本框 | 必填;同一 study 内建议唯一 | | `observation_unitpui` | 观测单元永久唯一标识 | 用户录入/系统生成 | 文本框/URL 输入框 | 可选;若填写建议唯一 | | `crop_id` | 作物 | 由 study 自动带出,可只读 | 作物选择器/只读 | 可选但建议有;需与 study.crop 一致 | | `cross_id` | 材料来源 cross | 从 cross_entity 选择 | Cross 选择器 | 可选;与 germplasm/seed_lot 至少建议选一个材料来源 | | `germplasm_id` | 材料来源 germplasm | 从 germplasm 选择 | 材料选择器 | 可选;常用字段 | | `program_id` | 项目 | 由 study 自动带出 | 项目选择器/只读 | 可选但建议有;需与 study.program 一致 | | `seed_lot_id` | 种子批次来源 | 从 seed_lot 选择 | SeedLot 选择器 | 可选;田间播种建议填写 | | `study_id` | 所属 study | 从 study 选择或工作台带出 | Study 选择器/隐藏 | 必选 | | `trial_id` | 所属 trial | 由 study 自动带出 | Trial 选择器/只读 | 可选但建议有;需与 study.trial 一致 | ### 录入建议 - 推荐从 Study 表型工作台批量生成,不建议逐条手工建。 - 必须支持田间布局字段:field、block、rep、plot、row、column、plant。若当前表没有这些字段,应通过 position/level 附属表或 additional_info 保存。 - `germplasm_id` 代表材料身份;`seed_lot_id` 代表实际播种批次;二者建议都保留。 - 选择 seed_lot 时可自动带出 germplasm,但不应强行覆盖用户选择。 ### 观测层级建议 | 层级 | 含义 | 例子 | | ------ | -------------- | ---------- | | field | 田块或试验场 | Field-01 | | block | 区组 | Block-01 | | rep | 重复 | Rep-01 | | plot | 小区 | Plot-001 | | plant | 单株 | Plant-001 | | sample | 样本级观测对象 | Sample-001 | --- ## 6.9 event 事件 ### 业务说明 `event` 记录发生在 study 或 observation_unit 上的离散事件。它既可以是处理的一部分,也可以是影响结果的外部背景。例如播种、移栽、施肥、灌溉、打药、收获、采样、暴雨、台风、病害暴发等。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ------------------- | ---------------------------------------------------------- | ------------------------- | ----------------- | -------------------------- | | `id` | 事件主键 | 系统生成 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `event_description` | 事件说明,如“台风后倒伏严重” | 用户录入 | 多行文本 | 可选;异常事件建议必填 | | `event_type` | 事件类型名称,如 fertilizer、irrigation、planting、harvest | 用户选择/录入 | 下拉框/文本框 | 必填;建议使用事件类型字典 | | `event_type_db_id` | 事件类型 ID,可引用标准事件类型字典 | 用户选择/系统带出 | 选择器/隐藏 | 可选 | | `study_id` | 事件所属 study | 从 study 选择或工作台带出 | Study 选择器/隐藏 | 必选 | ### 录入建议 - 在 Study 表型工作台的“事件”Tab 中维护。 - 新增事件时先选择作用范围:整个 study 或部分 observation_unit。 - 如果只作用于部分 plot/plant,必须通过 `event_observation_units` 记录范围。 - 事件发生时间字段在你当前表结构中未显式列出,但业务上非常重要;如果数据库已有 event_date/time 字段,应强制录入;如果没有,建议补字段或放入 additional_info。 --- ## 6.10 event_param 事件参数 ### 业务说明 `event_param` 是事件的参数明细。例如施肥事件可以有肥料类型、剂量、单位;灌溉事件可以有水量、持续时间;打药事件可以有药剂名称、浓度、剂量。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ------------------- | ------------------------------------- | ----------------- | ----------------- | ------------------------- | | `id` | 事件参数主键 | 系统生成 | 隐藏/只读 | 必填、唯一 | | `code` | 参数代码,如 N_RATE、IRR_VOL | 用户录入/字典带出 | 文本框 | 可选;同一事件内建议唯一 | | `description` | 参数解释 | 用户录入 | 多行文本 | 可选 | | `key` | 参数键,如 fertilizerType、dose、unit | 用户录入/字典选择 | 文本框 | 必填;同一事件内不应重复 | | `name` | 参数名称,如 氮肥施用量 | 用户录入 | 文本框 | 可选 | | `rdf_value` | RDF 或本体语义值 | 用户录入/系统带出 | 文本框 | 可选;普通业务可隐藏 | | `units` | 参数单位,如 kg/ha、mm、L | 用户选择/录入 | 单位选择器 | 可选;数值参数建议必填 | | `value` | 参数值 | 用户录入 | 动态控件 | 可选;若 key 需要值则必填 | | `value_description` | 参数值说明 | 用户录入 | 多行文本 | 可选 | | `event_id` | 所属事件 | 由事件详情页带出 | Event 选择器/隐藏 | 必选 | ### 录入建议 - 前端以键值表格方式维护。 - 可以为常用事件类型配置参数模板,例如 fertilizer 事件默认出现 fertilizerType、amount、units。 - 参数值是否必填取决于事件类型配置。 --- ## 6.11 event_observation_units 事件作用范围 ### 业务说明 `event_observation_units` 记录事件作用到哪些 observation_unit。比如一次施肥作用于整个 study,则可以不逐个绑定;如果某个处理只作用于部分 plot,则必须明确绑定。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ---------------------- | ------------ | --------------------------------------- | ----------------- | ------------------------ | | `event_entity_id` | 事件 | 由事件详情页带出 | Event 选择器/隐藏 | 必选 | | `observation_units_id` | 事件作用对象 | 从当前 study 的 observation_unit 中选择 | 多选器/表格 | 必选;必须属于同一 study | ### 录入建议 - 支持按 block、rep、plot 范围批量选择。 - 如果事件作用于整个 study,可以在 event 上记录 scope=study,不必生成大量关系记录。 - 如果 event_observation_units 有记录,所有 observation_units 必须属于 event.study_id。 --- ## 6.12 image 图像 ### 业务说明 `image` 用于保存图片或图片元数据。图片可以是人工拍摄、无人机、固定相机、显微图、病害图片等。图片可以直接关联 observation_unit,也可以通过 `image_observations` 关联具体 observation。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | --------------------- | ------------------------------------- | ------------------------ | ------------------- | -------------------------------------- | | `id` | 图片主键 | 系统生成 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `copyright` | 图片版权或授权说明 | 用户录入 | 文本框 | 可选 | | `description` | 图片说明,如“Plot-003 台风后倒伏照片” | 用户录入 | 多行文本 | 可选,建议填写 | | `image_data` | 图片二进制内容 | 用户上传 | 文件上传 | 与 `imageurl` 至少一个;限制大小和格式 | | `image_file_name` | 图片文件名 | 上传时自动填充 | 只读/文本框 | 可选;上传时自动生成 | | `image_file_size` | 文件大小 | 上传时自动识别 | 只读 | 可选;应限制最大大小 | | `image_height` | 图片高度 | 上传后自动识别 | 只读/数字框 | 可选;必须为非负整数 | | `imagemimetype` | MIME 类型,如 image/jpeg、image/png | 上传后自动识别 | 只读/下拉框 | 可选;只允许图片 MIME 类型 | | `imageurl` | 外部图片 URL 或对象存储 URL | 用户填写/上传后生成 | URL 输入框 | 与 `image_data` 至少一个;URL 格式校验 | | `image_width` | 图片宽度 | 上传后自动识别 | 只读/数字框 | 可选;必须为非负整数 | | `name` | 图片名称 | 用户录入/文件名带出 | 文本框 | 必填 | | `time_stamp` | 拍摄或上传时间 | 用户录入/系统默认 | 日期时间选择器 | 可选;建议填写拍摄时间 | | `coordinates_id` | 图片坐标 | 地图取点/坐标对象选择 | 地图控件/坐标选择器 | 可选;建议支持 Point/Polygon | | `observation_unit_id` | 所属观测单元 | 从 observation_unit 选择 | 观测单元选择器 | 可选;若填写应属于同一 study 上下文 | ### 录入建议 - 推荐图片文件存对象存储,数据库只存 URL 和元数据,避免 bytea 过大。 - 手机拍照或无人机导入时,应自动提取文件名、大小、宽高、MIME、拍摄时间和 GPS 信息。 - 对无人机俯拍图,可保存图像中心点,也可保存图像覆盖范围 Polygon。 --- ## 6.13 image_observations 图片与观测值关系 ### 业务说明 `image_observations` 表示某张图片支持或关联哪些 observation。比如一张倒伏照片可以关联一个 plot 的倒伏等级 observation。 ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ----------------- | ---------- | ------------------- | ------------------ | ---------------------------------------- | | `image_entity_id` | 图片 | 由图片详情页带出 | Image 选择器/隐藏 | 必选 | | `observations_id` | 关联观测值 | 从 observation 选择 | Observation 选择器 | 必选;建议与图片的 observation_unit 一致 | ### 录入建议 - 图片详情页支持关联多个 observation。 - observation 详情页也支持反向查看相关图片。 - 如果图片已绑定 observation_unit,则关联 observation 时应过滤为同一 observation_unit 下的 observation。 --- ## 6.14 observation 观测值 ### 业务说明 `observation` 是 Phenotyping 模块最核心的事实表。它表示“某个 observation_unit 在某个 observation_variable 上的一次实际采集结果”。 ```text observation_unit = Plot-003 observation_variable = 株高-尺测法-cm value = 112.5 observation_time_stamp = 2026-07-12 09:30 ``` ### 字段说明 | 字段 | 业务意义 | 录入方式 | 控件建议 | 校验规则 | | ------------------------- | ------------------ | ---------------------------------------- | ------------------- | ------------------------------------------ | | `id` | 观测值主键 | 系统生成;导入可指定 | 隐藏/只读 | 必填、唯一 | | `auth_user_id` | 数据所属用户或租户 | 登录上下文自动写入 | 隐藏 | 不允许手填 | | `collector` | 采集人 | 用户选择/登录用户带出 | 人员选择器/文本框 | 可选;人工采集建议填写 | | `observation_time_stamp` | 观测时间 | 用户录入/采集设备带出 | 日期时间选择器 | 建议必填;按 ISO 8601 保存 | | `uploaded_by` | 上传人 | 登录用户自动写入 | 只读 | 可选;批量导入时自动填 | | `value` | 实际观测值 | 用户录入/导入/设备上传 | 动态控件 | 必填;按 variable.scale 校验 | | `crop_id` | 作物上下文 | 由 observation_unit 或 study 自动带出 | 只读/隐藏 | 可选但建议保存;需与 observation_unit 一致 | | `geo_coordinates_id` | 观测发生坐标 | 地图取点/设备 GPS | 地图控件/坐标选择器 | 可选;坐标需合法 | | `observation_unit_id` | 被观测对象 | 从 observation_unit 选择或矩阵行带出 | 观测单元选择器/隐藏 | 必选 | | `observation_variable_id` | 观测变量 | 从 observation_variable 选择或矩阵列带出 | 变量选择器/隐藏 | 必选 | | `program_id` | 项目上下文 | 由 observation_unit/study 自动带出 | 只读/隐藏 | 可选但建议保存 | | `season_id` | 季节 | 从 study season 或用户选择 | Season 选择器 | 可选;多季节 study 建议填写 | | `study_id` | 所属 study | 由 observation_unit 自动带出 | 只读/隐藏 | 必选;需与 observation_unit.study 一致 | | `trial_id` | 所属 trial | 由 observation_unit/study 自动带出 | 只读/隐藏 | 可选但建议保存 | ### 录入建议 - 普通录入优先走矩阵:行是 observation_unit,列是 observation_variable。 - 单条表单只用于补录、纠错、详情编辑。 - value 必须使用 observation_variable.scale 决定控件和校验: - 数值型:数字、小数位、范围; - 分类型:只能从 valid category 中选; - 日期型:日期格式; - 布尔型:true/false; - 文本型:长度限制。 - 同一 `observation_unit_id + observation_variable_id + observation_time_stamp` 可允许重复,但必须在页面展示重复标记、采集人和上传来源。 --- # 7. 跨表联动规则 | 场景 | 联动规则 | | ------------------------- | ------------------------------------------------------------ | | 创建 observation_variable | 必须先选择 trait、method、scale;系统自动生成变量名称 | | 配置 study_variable | 变量选择器优先展示同 crop 或未限定 crop 的变量 | | 创建 observation_unit | 选择 study 后自动带出 crop、program、trial | | 选择 seed_lot | 可自动带出 germplasm,但允许用户确认 | | 录入 observation | 选择 observation_unit 后自动带出 study、trial、program、crop | | 录入 observation.value | 根据 observation_variable.scale 动态渲染控件和校验 | | 新增 event | 从 study 工作台进入时自动带出 study_id | | 选择事件作用范围 | 只能选择同一 study 下的 observation_unit | | 上传 image | 如果从 observation_unit 详情页上传,自动绑定 observation_unit_id | | 关联 image_observations | 如果图片已绑定 observation_unit,只能关联同 observation_unit 的 observation | --- # 8. 删除和停用规则 | 对象 | 删除限制 | | -------------------- | ------------------------------------------------------------ | | ontology | 已被 trait/method/scale/variable 引用时不可物理删除 | | trait | 已被 observation_variable 或 germplasm_attribute_definition 引用时不可物理删除 | | method | 已被 observation_variable 引用时不可物理删除 | | scale | 已被 observation_variable 或 attribute definition 引用时不可物理删除 | | observation_variable | 已产生 observation 时不可删除,只能停用或设为 obsolete | | study_variable | 若已有该变量的 observation,不允许直接移除 | | observation_unit | 已有 observation、image、event、sample 时不可删除 | | event | 已有关联 event_param 或 event_observation_units 时删除需级联确认或禁止 | | image | 已关联 observation 时不可直接删除,应先解除关系或作废 | | observation | 原始数据原则上不建议物理删除;建议保留修改历史或作废状态 | --- # 9. 批量导入要求 ## 9.1 ObservationUnit 导入模板 必需列建议: ```text study_id 或 study_name observation_unit_name observation_level_name observation_level_code ``` 强烈建议列: ```text germplasm_id 或 germplasm_name seed_lot_id 或 seed_lot_name block rep plot row column ``` ## 9.2 ObservationVariable 导入模板 必需列建议: ```text trait_name method_name scale_name variable_name ``` 可选列: ```text trait_pui method_pui scale_pui variable_pui crop_name growth_stage status ``` ## 9.3 Observation 矩阵导入模板 推荐格式: ```text observation_unit_name,germplasm_name,collection_time,collector,PlantHeight_cm,LodgingScore_0_5,DiseaseScore_1_9 Plot-001,华占,2026-07-12T09:30:00+08:00,张三,112.5,1,2 Plot-002,抗倒伏A,2026-07-12T09:35:00+08:00,张三,98.3,0,1 ``` 导入规则: 1. 每个变量列必须能映射到一个 observation_variable。 2. 每一行必须能匹配一个 observation_unit。 3. 每个非空单元格生成一条 observation。 4. 空值不生成 observation,除非用户选择“导入为空观测”。 5. 导入前必须预校验,错误报告包含行号、列名、错误原因、建议修复方式。 ## 9.4 Image 导入模板 必需列建议: ```text image_name image_url 或 image_file ``` 可选列: ```text observation_unit_name observation_id capture_time longitude latitude polygon copyright description ``` --- # 10. 后端接口建议 ## 10.1 主数据接口 ```text GET /ontologies POST /ontologies GET /ontologies/{id} PUT /ontologies/{id} DELETE /ontologies/{id} GET /traits POST /traits GET /traits/{id} PUT /traits/{id} GET /methods POST /methods GET /methods/{id} PUT /methods/{id} GET /scales POST /scales GET /scales/{id} PUT /scales/{id} GET /scales/{id}/valid-values POST /scales/{id}/valid-values GET /observation-variables POST /observation-variables GET /observation-variables/{id} PUT /observation-variables/{id} GET /studies/{studyId}/variables POST /studies/{studyId}/variables DELETE /studies/{studyId}/variables/{variableId} GET /studies/{studyId}/observation-units POST /studies/{studyId}/observation-units/batch-generate POST /observation-units PUT /observation-units/{id} GET /studies/{studyId}/events POST /studies/{studyId}/events GET /events/{id}/params POST /events/{id}/params POST /events/{id}/observation-units GET /images POST /images POST /images/upload POST /images/{id}/observations GET /observations POST /observations POST /observations/matrix POST /observations/import ``` ## 10.2 选择器接口 ```text GET /selectors/ontologies GET /selectors/traits?ontologyId=&keyword= GET /selectors/methods?ontologyId=&keyword= GET /selectors/scales?ontologyId=&dataType=&keyword= GET /selectors/observation-variables?cropId=&studyId=&keyword= GET /selectors/studies?programId=&trialId=&keyword= GET /selectors/observation-units?studyId=&level=&germplasmId=&keyword= GET /selectors/events?studyId=&eventType=&keyword= GET /selectors/images?observationUnitId=&keyword= ``` --- # 11. 测试验收清单 1. 创建 trait 时,`trait_name` 必填。 2. 创建 method 时,`name` 必填。 3. 创建 scale 时,`scale_name` 和 `data_type` 必填。 4. 分类型 scale 没有 valid value 时,应提示或禁止用于 observation_variable。 5. 创建 observation_variable 时,trait、method、scale 必选。 6. observation_variable 名称可自动生成,也允许用户手动修改。 7. 配置 study_variable 时,不能重复绑定同一个 variable。 8. 创建 observation_unit 时,study 必选。 9. 选择 study 后,crop、program、trial 自动带出。 10. 同一 study 内 observation_unit_name 重复时提示。 11. observation_unit 至少建议绑定 germplasm、seed_lot、cross 中一个材料来源。 12. 创建 event 时,study 和 event_type 必填。 13. event 绑定 observation_unit 时,所有 observation_unit 必须属于该 event 的 study。 14. 图片上传时,image_data 和 imageurl 至少一个存在。 15. 图片 MIME 类型必须是允许的图片类型。 16. observation 创建时,observation_unit、observation_variable、value 必填。 17. observation.value 必须按 scale.data_type 校验。 18. 数值型 value 必须在 valid_value_min 和 valid_value_max 范围内。 19. 分类型 value 必须属于 scale_valid_value_category。 20. observation 的 study/trial/program/crop 必须与 observation_unit 上下文一致。 21. 矩阵录入保存时,每个有效单元格生成一条 observation。 22. 导入失败时必须返回行号、字段、错误原因和修复建议。 23. 已产生 observation 的 observation_variable 不允许直接删除。 24. 已被 observation、image、event、sample 引用的 observation_unit 不允许物理删除。 25. 所有日期时间字段按 ISO 8601 保存,并保留时区或统一转换为 UTC。 --- # 12. 开发实现重点 1. `observation_variable` 是表型指标定义,不是观测值。 2. `observation_unit` 是被观测对象,不是实际测量值。 3. `observation` 才是真正的表型事实数据。 4. `value` 字段虽然数据库是字符串,但前端和后端必须按 scale 做类型校验。 5. `study` 是 Phenotyping 的核心上下文入口,几乎所有表型数据都应能追溯到 study。 6. 表型采集页面应优先做矩阵录入,不要只做单条 CRUD。 7. 图片不要直接塞数据库大字段为主,建议走对象存储 URL + 元数据。 8. Event 不只是备注,它是解释表型差异的重要上下文,应支持作用范围和参数化。