# Core 模块业务需求文档

## 1. 模块定位

Core 是项目、试验、地点、人员、季节的基础上下文模块，是所有业务数据的总上游。它不是普通字典维护，而是为后续 Germplasm/Seed、Phenotyping、Genotyping 提供“这批数据属于哪个项目、哪次试验、哪个地点、哪个年度、谁负责”的业务台账。

推荐先完成 Core 录入，再录入 Germplasm、Phenotyping、Genotyping。

## 2. 真实业务理解

Core 不只是“基础字典”，它在真实育种业务里是项目台账：回答“谁在什么项目、什么地点、什么年度、做哪一次试验”。后续所有材料、田间布置、表型采集、样本检测、基因型结果，都需要回到 Core 上下文里解释。

最常见的业务链路是：

```text
crop 作物
  -> program 长期育种项目
  -> trial 某一批试验/区域试验
  -> study 一次真正落地执行的试验
```

例如：

```text
crop: 水稻
program: 水稻抗倒伏育种项目
trial: 2026 湖北区域试验
location: 荆门试验站
season: 2026 春季
study: 2026 荆门水稻抗倒伏田间试验
```

这里最关键的是 `study`。`program` 更像长期项目，`trial` 是试验批次，`study` 才是具体执行单元。后续 `observation_unit`、`event`、`observation`、`plate`、`sample`、`variantset` 都会直接或间接挂到 `study` 上。

## 3. 用户角色和使用场景

| 角色 | 主要职责 | 典型入口 | 权限重点 |
| --- | --- | --- | --- |
| 项目管理员 | 维护作物、项目、人员、地点等基础台账 | crop、person、program、location 列表页 | 可新增/编辑基础数据，可停用未被引用的数据 |
| 试验负责人 | 创建 trial 和 study，维护联系人、季节、试验设计 | trial 列表、study 列表、Study 工作台 | 可创建试验上下文，可配置 study 联系人、season、变量清单 |
| 数据录入员 | 进入 Study 工作台继续录观测单元、表型、样本 | Study 工作台 | 主要使用下游入口，一般不能删除 Core 主数据 |
| 数据管理员 | 处理导入、导出、停用、删除校验、数据修复 | 导入导出页、数据质量页 | 可批量导入、导出、停用，有删除前引用检查权限 |

### 3.1 项目管理员场景

项目管理员先建立 `crop`、`person`、`program`、`location`。例如创建“水稻抗倒伏育种项目”，选择 crop=水稻，负责人=张三。录完后，试验负责人才能在该项目下创建 trial 和 study。

### 3.2 试验负责人场景

试验负责人创建“2026 湖北区域试验”和“2026 荆门水稻抗倒伏田间试验”。创建 study 时选择 program、trial、location、season，保存后进入 Study 工作台。

### 3.3 数据录入员场景

数据录入员通常不从 crop/program 开始，而是从 Study 工作台进入，继续录 observation unit、observation、sample 等下游数据。

### 3.4 数据管理员场景

数据管理员负责批量导入 Core 数据、处理重复数据、做删除/停用前检查。例如某 trial 已有关联 study 时，不允许物理删除，只能停用。

## 用户为什么要录 Core

| 用户问题 | 由哪些表回答 | 录完能做什么 |
| --- | --- | --- |
| 我们做什么作物？ | `crop` | 限定项目、材料、变量、图谱的作物范围 |
| 谁负责项目和试验？ | `person`、`program.lead_person_id`、`trial_contact`、`study_contact` | 项目责任追踪、联系人展示、权限和通知 |
| 这是哪个长期育种项目？ | `program` | 聚合多个 trial/study、材料、样本、结果 |
| 这是哪一批试验？ | `trial` | 管理区域试验、年度试验、批次试验 |
| 试验在哪里、哪一季做？ | `location`、`season`、`study_season` | 支持多地点、多季节筛选和统计 |
| 哪一次试验真正落地执行？ | `study` | 进入 Study 工作台，继续录观测单元、表型、样本、基因型 |

## 6. 页面需求

Core 的核心页面不应该只是表单 CRUD，而应该围绕 `study` 做“Study 工作台”。

### 6.1 Study 工作台

```text
Study 工作台
├─ 基本信息：名称、编号、类型、起止日期、负责人
├─ 上下文：crop、program、trial、location、season
├─ 试验设计：experimental design、observation level、growth facility
├─ 联系人/附件：study_contact、data_link、external_references
├─ 观测单元：跳转 observation_unit
├─ 表型数据：跳转 observation / matrix
├─ 样本与样本板：跳转 sample / plate
├─ 基因型数据：跳转 variantset / callset / allele_call
└─ 导入导出：按 study 导入观测单元、表型、样本、基因型
```

### 6.2 Crop 列表页

| 项目 | 需求 |
| --- | --- |
| 功能 | 查询 crop；新增、编辑、查看详情、停用 |
| 筛选 | 关键词搜索，按 crop_name 模糊匹配 |
| 表格字段 | `crop_name`、下游 program 数、下游 study 数、状态 |
| 操作 | 查看、编辑、停用 |
| 验收标准 | crop 已被 program/germplasm/study 引用时，不允许物理删除 |

### 6.3 Person 列表页

| 项目 | 需求 |
| --- | --- |
| 功能 | 查询人员；新增、编辑、查看详情、停用 |
| 筛选 | 姓名、邮箱、机构 |
| 表格字段 | 姓名、邮箱、电话、机构、负责项目数、参与 study 数 |
| 操作 | 查看、编辑、停用 |
| 验收标准 | 邮箱格式正确；作为负责人或联系人被引用时，不允许物理删除 |

### 6.4 Program 列表页

| 项目 | 需求 |
| --- | --- |
| 功能 | 查询 program；新增、编辑、查看详情、停用 |
| 筛选 | crop、负责人、program_type、关键词 |
| 表格字段 | `name`、`abbreviation`、crop、负责人、trial 数、study 数 |
| 操作 | 查看、编辑、停用、查看 trial、查看 study |
| 验收标准 | 创建 program 时必须选择 crop；负责人来自 person 选择器 |

### 6.5 Trial 列表页

| 项目 | 需求 |
| --- | --- |
| 功能 | 查询 trial；新增、编辑、查看详情、停用 |
| 筛选 | crop、program、active、起止日期、关键词 |
| 表格字段 | `trial_name`、program、crop、`start_date`、`end_date`、active |
| 操作 | 查看、编辑、停用、查看 study |
| 验收标准 | 选择 program 后自动带出 crop；已被 study 引用时不允许物理删除 |

### 6.6 Location 列表页

| 项目 | 需求 |
| --- | --- |
| 功能 | 查询 location；新增、编辑、查看详情、停用 |
| 筛选 | crop、program、location_type、country、关键词 |
| 表格字段 | `location_name`、location_type、country、program、crop、父级地点 |
| 操作 | 查看、编辑、停用、地图查看 |
| 验收标准 | 父级地点不能选择自己；坐标字段格式合法 |

### 6.7 Season 列表页

| 项目 | 需求 |
| --- | --- |
| 功能 | 查询 season；新增、编辑、查看详情、停用 |
| 筛选 | year、season |
| 表格字段 | `year`、`season`、关联 study 数 |
| 操作 | 查看、编辑、停用 |
| 验收标准 | 同一年份内 season 名称不建议重复 |

### 6.8 Study 列表页

| 项目 | 需求 |
| --- | --- |
| 功能 | 查询 study；新增、编辑、查看详情、停用；点击 study 名称进入 Study 工作台 |
| 筛选 | crop、program、trial、location、season、active、study_type、关键词 |
| 表格字段 | `study_name`、`study_code`、program、trial、location、`start_date`、`end_date`、active |
| 操作 | 查看、编辑、停用、进入工作台 |
| 验收标准 | 选择 program 后只展示该 program 下的 trial；选择 trial 后自动带出 crop；已有 observation_unit/sample/observation 时不能物理删除 |

### 6.9 Study 新增/编辑页

| 区域 | 字段/功能 | 要求 |
| --- | --- | --- |
| 基本信息 | study_name、study_code、study_type、active、start_date、end_date | study_name 必填；结束日期不早于开始日期 |
| 上下文 | crop、program、trial、location、season | program、trial、crop、location 必选；program -> trial 联动 |
| 说明信息 | study_description、cultural_practices、observation_units_description、license、documentationurl | URL 格式校验 |
| 联系人 | study_contact | 多选 person，可增删 |
| 季节 | study_season | 可多选 season |
| 提交后 | 保存成功进入 Study 工作台 | 新建成功后自动跳转 |

### 6.10 List 详情页

| 项目 | 需求 |
| --- | --- |
| 功能 | 维护 list 和 list_item |
| 页面形态 | 顶部 list 基本信息，下方 list_item 表格 |
| 操作 | 新增 item、批量导入 item、删除 item、排序 |
| 验收标准 | 同一 list 内 item 不重复 |

## 哪些表是业务动作痕迹

| 动作 | 痕迹表 | 说明 |
| --- | --- | --- |
| 给 trial 添加联系人 | `trial_contact` | trial 与 person 的关系，不是独立主数据 |
| 给 study 添加联系人 | `study_contact` | study 与 person 的关系 |
| 给 study 绑定季节 | `study_season` | 一个 study 可关联多个 season |
| 给 study 绑定观测变量 | `study_variable` | 表示该 study 要采集哪些指标 |
| 给 trial 记录出版物 | `trial_publication` | trial 的文献/报告引用 |
| 给 list 添加成员 | `list_item` | list 的明细项，是分组动作留下的记录 |

## 4. 核心业务流程

### 4.1 新建 Core 试验上下文流程

1. 用户创建 `crop`。
2. 用户创建 `person`。
3. 用户创建 `program`，并选择 `crop` 和负责人 `lead_person_id`。
4. 用户创建 `location`，可选绑定 `program` 和 `crop`。
5. 用户创建 `trial`，选择 `program` 后自动带出 `crop`。
6. 用户创建 `season`。
7. 用户创建 `study`，按 `program -> trial -> location -> season` 的顺序选择上下文。
8. `study` 创建成功后，系统自动进入 Study 工作台。

### 4.2 业务流程图

```mermaid
flowchart TD
    A["创建 crop"] --> B["创建 person"]
    B --> C["创建 program<br/>选择 crop / lead person"]
    C --> D["创建 location<br/>可选 program / crop"]
    C --> E["创建 trial<br/>选择 program 自动带出 crop"]
    E --> F["创建 season"]
    D --> G["创建 study<br/>选择 program -> trial -> location -> season"]
    F --> G
    G --> H["进入 Study 工作台"]
```

## 5. 核心对象说明

### 5.1 crop 作物

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `crop` |
| 前置依赖 | 无 |
| 主要字段 | 作物 ID、作物名称、公共作物名、描述 |
| 下游引用 | `program`、`location`、`trial`、`study`、`germplasm`、`observation_variable`、`genome_map` |

#### 界面形态

列表页展示作物名称、描述、更新时间、下游引用数量。新增页是轻量表单，重点是作物名称唯一校验。详情页展示该作物下的项目、试验、研究、种质、图谱入口。

#### 校验规则

1. 作物名称必填且唯一。
2. 已被下游引用的作物不能直接删除，只允许停用或提示引用关系。

### 5.2 person 人员

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `person` |
| 前置依赖 | 无 |
| 主要字段 | 姓名、邮箱、机构、联系方式、角色 |
| 下游引用 | `program.lead_person_id`、`trial_contact`、`study_contact`、`list.list_owner_person_id` |

#### 界面形态

列表页支持姓名、邮箱、机构筛选。新增/编辑页用联系人表单。详情页展示负责的 program、参与的 trial/study、拥有的 list。

#### 校验规则

1. 姓名必填。
2. 邮箱格式正确。
3. 同一邮箱不建议重复录入。

### 5.3 program 项目

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `program` |
| 前置依赖 | `crop`，可选 `person` |
| 主要字段 | 项目名称、作物、负责人、缩写、目标、描述 |
| 下游引用 | `trial`、`study`、`location`、`crossing_project`、`seed_lot`、`plate`、`sample` |

#### 界面形态

新增页分为“基本信息”和“负责人/作物”两组。作物和负责人使用搜索选择器。详情页 Tab 展示 trial、study、location、seed lot、sample。

#### 校验规则

1. 项目名称必填。
2. 作物必选。
3. 选择负责人时必须存在于 `person`。

### 5.4 location 地点

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `location` |
| 前置依赖 | 可选 `crop`、`program`、父级 `location`、坐标 |
| 主要字段 | 地点名称、地点类型、国家/地区、经纬度、父级地点、所属项目 |
| 下游引用 | `study`、`seed_lot` |

#### 界面形态

列表页支持地图/表格两种视图。新增页包含基本信息、行政区、坐标、父级地点。详情页展示该地点下的 study 和 seed lot。

#### 校验规则

1. 地点名称必填。
2. 经纬度格式合法。
3. 父级地点不能选择自己。

### 5.5 trial 试验批次

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `trial` |
| 前置依赖 | `program`、`crop` |
| 主要字段 | 试验名称、项目、作物、开始/结束日期、联系人、出版物 |
| 下游引用 | `study`、`observation_unit`、`observation`、`plate`、`sample` |

#### 界面形态

新增页包含基本信息、项目作物、联系人、出版物四个区域。联系人用可增删表格选择 `person`。详情页展示 study 列表和 phenotyping/genotyping 入口。

#### 校验规则

1. 试验名称必填。
2. program 与 crop 必须匹配。
3. 结束日期不能早于开始日期。

### 5.6 season 季节

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `season` |
| 前置依赖 | 无 |
| 主要字段 | 季节名称、年份、开始日期、结束日期 |
| 下游引用 | `study_season`、部分 `observation` |

#### 界面形态

列表页按年份和季节筛选。新增页是简单表单。详情页展示关联 study。

#### 校验规则

1. 名称和年份必填。
2. 同一年份内季节名称不建议重复。

### 5.7 study 研究/试验实施单元

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `study` |
| 前置依赖 | `crop`、`program`、`trial`、`location`，可选 `season` |
| 主要字段 | study 名称、study 类型、项目、试验、地点、季节、开始/结束日期、实验设计 |
| 下游引用 | `observation_unit`、`event`、`observation`、`plate`、`sample`、`variantset` |

#### 界面形态

新增页是分组表单：基本信息、上下文、地点季节、实验设计、联系人。上下文字段按顺序联动：选择 program 后过滤 trial 和 crop，选择 trial 后带出 crop。详情页作为工作台，展示 observation unit、observation、sample、variantset。

#### 校验规则

1. study 名称必填。
2. program、trial、crop、location 必选。
3. trial 必须属于所选 program。
4. study 是下游核心引用，删除必须强提示。

### 5.8 list / list_item 通用列表

#### 数据库录入

| 项目 | 要求 |
| --- | --- |
| 表 | `list`、`list_item` |
| 前置依赖 | 可选 `person` |
| 主要字段 | 列表名称、列表类型、owner、项目项 ID、项目项名称 |
| 下游引用 | 业务查询和分组 |

#### 界面形态

列表页展示 list。详情页内嵌 list item 表格，支持手动添加、批量导入、排序。新增 item 时可选择目标类型，例如 germplasm、study、sample。

#### 校验规则

1. list 名称必填。
2. list item 在同一 list 内不应重复。
3. 如果绑定 owner，owner 必须存在于 `person`。

## 7. 字段需求

### crop

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 作物主键，新增时系统生成，也可导入时指定 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `crop_name` | varchar(255) | 文本框 | 作物名称，用于所有下拉选择展示 | 必填、建议唯一 |

### person

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 人员主键 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `description` | varchar(255) | 多行文本 | 人员说明、职责补充 | 可选 |
| `email_address` | varchar(255) | 邮箱输入框 | 邮箱地址 | 邮箱格式校验，建议唯一 |
| `first_name` | varchar(255) | 文本框 | 名 | 与 `last_name` 至少填写一个 |
| `institute_name` | varchar(255) | 文本框/选择器 | 所属机构 | 可选 |
| `last_name` | varchar(255) | 文本框 | 姓 | 与 `first_name` 至少填写一个 |
| `mailing_address` | varchar(255) | 多行文本 | 通讯地址 | 可选 |
| `middle_name` | varchar(255) | 文本框 | 中间名 | 可选 |
| `phone_number` | varchar(255) | 电话输入框 | 联系电话 | 可选，格式提示 |
| `userid` | varchar(255) | 文本框 | 外部用户 ID 或登录名 | 可选，建议唯一 |

### program

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 项目主键 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `abbreviation` | varchar(255) | 文本框 | 项目缩写 | 可选 |
| `documentationurl` | varchar(255) | URL 输入框 | 项目文档链接 | URL 格式校验 |
| `funding_information` | varchar(255) | 多行文本 | 经费来源说明 | 可选 |
| `name` | varchar(255) | 文本框 | 项目名称 | 必填 |
| `objective` | varchar(255) | 多行文本 | 项目目标 | 可选 |
| `program_type` | integer | 下拉框 | 项目类型枚举 | 可选，按 BrAPI 枚举 |
| `crop_id` | varchar(255) | 作物选择器 | 所属作物 | 必选，来源 `crop.id` |
| `lead_person_id` | varchar(255) | 人员选择器 | 项目负责人 | 可选，来源 `person.id` |

### location

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 地点主键 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `abbreviation` | varchar(255) | 文本框 | 地点缩写 | 可选 |
| `coordinate_description` | varchar(255) | 多行文本 | 坐标描述 | 可选 |
| `coordinate_uncertainty` | varchar(255) | 文本框 | 坐标不确定性 | 可选 |
| `country_code` | varchar(255) | 国家代码选择器 | 国家代码 | 可选，建议 ISO 代码 |
| `country_name` | varchar(255) | 文本框 | 国家名称 | 可选，可由国家代码带出 |
| `documentationurl` | varchar(255) | URL 输入框 | 地点文档链接 | URL 格式校验 |
| `environment_type` | varchar(255) | 下拉框/文本框 | 环境类型 | 可选 |
| `exposure` | varchar(255) | 文本框 | 暴露条件 | 可选 |
| `institute_address` | varchar(255) | 多行文本 | 机构地址 | 可选 |
| `institute_name` | varchar(255) | 文本框 | 机构名称 | 可选 |
| `location_name` | varchar(255) | 文本框 | 地点名称 | 必填 |
| `location_type` | varchar(255) | 下拉框 | 地点类型，如 field、greenhouse、storage | 可选 |
| `site_status` | varchar(255) | 下拉框 | 地点状态 | 可选 |
| `slope` | varchar(255) | 文本框 | 坡度 | 可选 |
| `topography` | varchar(255) | 文本框 | 地形 | 可选 |
| `coordinates_id` | varchar(255) | 坐标选择器/地图取点 | 坐标对象 | 可选，来源 `geojson/coordinate` |
| `crop_id` | varchar(255) | 作物选择器 | 关联作物 | 可选，来源 `crop.id` |
| `parent_location_id` | varchar(255) | 地点选择器 | 父级地点 | 可选，不能选择自己 |
| `program_id` | varchar(255) | 项目选择器 | 所属项目 | 可选，来源 `program.id` |

### trial

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 试验主键 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `active` | boolean | 开关 | 是否启用 | 默认启用 |
| `documentationurl` | varchar(255) | URL 输入框 | 试验文档链接 | URL 格式校验 |
| `end_date` | timestamp | 日期选择器 | 结束日期 | 不早于 `start_date` |
| `start_date` | timestamp | 日期选择器 | 开始日期 | 可选 |
| `trial_description` | varchar(255) | 多行文本 | 试验描述 | 可选 |
| `trial_name` | varchar(255) | 文本框 | 试验名称 | 必填 |
| `trialpui` | varchar(255) | 文本框 | 试验永久标识 | 可选，建议唯一 |
| `crop_id` | varchar(255) | 作物选择器 | 所属作物 | 必选，来源 `crop.id` |
| `program_id` | varchar(255) | 项目选择器 | 所属项目 | 必选，来源 `program.id` |

### season

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 季节主键 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `season` | varchar(255) | 文本框/下拉框 | 季节名称，如 Spring、Summer | 必填 |
| `year` | integer | 年份选择器 | 年份 | 必填，四位年份 |

### study

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | study 主键 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `active` | boolean | 开关 | 是否启用 | 默认启用 |
| `cultural_practices` | varchar(255) | 多行文本 | 栽培管理说明 | 可选 |
| `documentationurl` | varchar(255) | URL 输入框 | study 文档链接 | URL 格式校验 |
| `end_date` | timestamp | 日期选择器 | 结束日期 | 不早于 `start_date` |
| `license` | varchar(255) | 文本框 | 数据许可证 | 可选 |
| `observation_units_description` | varchar(255) | 多行文本 | 观测单元说明 | 可选 |
| `start_date` | timestamp | 日期选择器 | 开始日期 | 可选 |
| `study_code` | varchar(255) | 文本框 | study 编码 | 可选，建议同项目内唯一 |
| `study_description` | varchar(255) | 多行文本 | study 描述 | 可选 |
| `study_name` | varchar(255) | 文本框 | study 名称 | 必填 |
| `studypui` | varchar(255) | 文本框 | study 永久标识 | 可选，建议唯一 |
| `study_type` | varchar(255) | 下拉框 | study 类型 | 可选 |
| `crop_id` | varchar(255) | 作物选择器 | 所属作物 | 必选，来源 `crop.id` |
| `location_id` | varchar(255) | 地点选择器 | 实施地点 | 必选，来源 `location.id` |
| `program_id` | varchar(255) | 项目选择器 | 所属项目 | 必选，来源 `program.id` |
| `trial_id` | varchar(255) | 试验选择器 | 所属 trial | 必选，来源 `trial.id` |

### list

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 列表主键 | 必填、唯一 |
| `auth_user_id` | varchar(255) | 隐藏 | 数据所属用户 | 登录上下文自动写入 |
| `date_created` | timestamp | 只读日期时间 | 创建时间 | 系统自动写入 |
| `date_modified` | timestamp | 只读日期时间 | 修改时间 | 系统自动更新 |
| `description` | varchar(255) | 多行文本 | 列表描述 | 可选 |
| `list_name` | varchar(255) | 文本框 | 列表名称 | 必填 |
| `list_owner_name` | varchar(255) | 文本框 | 列表 owner 名称 | 可选，可由 owner person 带出 |
| `list_source` | varchar(255) | 文本框 | 列表来源 | 可选 |
| `list_type` | integer | 下拉框 | 列表类型 | 必填，按 BrAPI 枚举 |
| `list_owner_person_id` | varchar(255) | 人员选择器 | 列表 owner | 可选，来源 `person.id` |

### list_item

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 列表项主键 | 必填、唯一 |
| `item` | varchar(255) | 文本框/对象选择器 | 列表项值，可存目标对象 ID 或文本 | 必填，同一 list 内不重复 |
| `list_id` | varchar(255) | List 选择器 | 所属列表 | 必选，来源 `list.id` |

### trial_contact

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `trial_db_id` | varchar(255) | Trial 选择器 | 所属 trial | 必选，来源 `trial.id` |
| `person_db_id` | varchar(255) | 人员选择器 | 联系人 | 必选，来源 `person.id` |

### trial_publication

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `id` | varchar(255) | 隐藏/只读 | 出版物记录主键 | 必填、唯一 |
| `publicationpui` | varchar(255) | 文本框 | 出版物 PUI | 可选 |
| `publication_reference` | varchar(255) | 文本框/URL | 出版物引用 | 可选 |
| `trial_id` | varchar(255) | Trial 选择器 | 所属 trial | 必选，来源 `trial.id` |

### study_contact

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `study_db_id` | varchar(255) | Study 选择器 | 所属 study | 必选，来源 `study.id` |
| `person_db_id` | varchar(255) | 人员选择器 | 联系人 | 必选，来源 `person.id` |

### study_season

| 字段 | 类型 | 控件 | 录入说明 | 校验/来源 |
| --- | --- | --- | --- | --- |
| `study_db_id` | varchar(255) | Study 选择器 | 所属 study | 必选，来源 `study.id` |
| `season_db_id` | varchar(255) | Season 选择器 | 关联季节 | 必选，来源 `season.id` |

## 8. 接口需求

接口命名可以按项目现有 BrAPI Controller 适配；本节描述前端页面需要的能力，不强制限定最终 URL 必须完全一致。

### 8.1 CRUD 接口

| 对象 | 接口 | 用途 |
| --- | --- | --- |
| crop | `GET /crops` | crop 分页查询 |
| crop | `POST /crops` | 新增 crop |
| crop | `GET /crops/{id}` | crop 详情 |
| crop | `PUT /crops/{id}` | 编辑 crop |
| crop | `DELETE /crops/{id}` | 删除或停用 crop，需做引用检查 |
| person | `GET /persons` | person 分页查询 |
| person | `POST /persons` | 新增 person |
| person | `GET /persons/{id}` | person 详情 |
| person | `PUT /persons/{id}` | 编辑 person |
| person | `DELETE /persons/{id}` | 删除或停用 person，需做引用检查 |
| program | `GET /programs` | program 分页查询 |
| program | `POST /programs` | 新增 program |
| program | `GET /programs/{id}` | program 详情 |
| program | `PUT /programs/{id}` | 编辑 program |
| program | `DELETE /programs/{id}` | 删除或停用 program，需做引用检查 |
| location | `GET /locations` | location 分页查询 |
| location | `POST /locations` | 新增 location |
| location | `GET /locations/{id}` | location 详情 |
| location | `PUT /locations/{id}` | 编辑 location |
| trial | `GET /trials` | trial 分页查询 |
| trial | `POST /trials` | 新增 trial |
| trial | `GET /trials/{id}` | trial 详情 |
| trial | `PUT /trials/{id}` | 编辑 trial |
| trial | `DELETE /trials/{id}` | 删除或停用 trial，需检查 study 引用 |
| season | `GET /seasons` | season 查询 |
| season | `POST /seasons` | 新增 season |
| season | `PUT /seasons/{id}` | 编辑 season |
| study | `GET /studies` | study 分页查询 |
| study | `POST /studies` | 新增 study |
| study | `GET /studies/{id}` | study 详情 |
| study | `PUT /studies/{id}` | 编辑 study |
| study | `DELETE /studies/{id}` | 删除或停用 study，需检查下游引用 |
| list | `GET /lists` | list 查询 |
| list | `POST /lists` | 新增 list |
| list | `PUT /lists/{id}` | 编辑 list |
| list item | `POST /lists/{id}/items` | 给 list 添加 item |
| list item | `DELETE /lists/{id}/items/{itemId}` | 删除 list item |

### 8.2 详情聚合接口

| 接口 | 用途 | 返回重点 |
| --- | --- | --- |
| `GET /programs/{id}/trials` | 查询 program 下 trial | trial 基础信息、active |
| `GET /programs/{id}/studies` | 查询 program 下 study | study 基础信息、location、trial |
| `GET /trials/{id}/studies` | 查询 trial 下 study | study 列表 |
| `GET /studies/{id}/workbench` | Study 工作台聚合信息 | study 基本信息、联系人、season、observation_unit 数、observation 数、sample 数、variantset 数 |
| `GET /studies/{id}/contacts` | study 联系人 | person 列表 |
| `GET /studies/{id}/seasons` | study 季节 | season 列表 |

### 8.3 选择器接口

| 接口 | 用途 | 参数 |
| --- | --- | --- |
| `GET /selectors/crops` | crop 搜索下拉 | `keyword` |
| `GET /selectors/persons` | person 搜索下拉 | `keyword`、`instituteName` |
| `GET /selectors/programs` | program 搜索下拉 | `keyword`、`cropId` |
| `GET /selectors/trials` | trial 搜索下拉 | `keyword`、`programId`、`cropId`、`active` |
| `GET /selectors/locations` | location 搜索下拉 | `keyword`、`programId`、`cropId`、`locationType` |
| `GET /selectors/seasons` | season 搜索下拉 | `year`、`keyword` |
| `GET /selectors/studies` | study 搜索下拉 | `keyword`、`programId`、`trialId`、`locationId`、`active` |

### 8.4 导入导出接口

| 接口 | 用途 |
| --- | --- |
| `GET /imports/templates/core/{objectType}` | 下载 Core 对象导入模板 |
| `POST /imports/core/{objectType}/preview` | 上传文件并预校验，不落库 |
| `POST /imports/core/{objectType}/commit` | 确认导入 |
| `GET /exports/core/{objectType}` | 导出当前筛选结果 |
| `GET /imports/{jobId}/errors` | 获取导入错误报告 |

## 9. 联动规则

| 场景 | 联动规则 |
| --- | --- |
| 创建 program | 必须先选择 crop；负责人从 person 选择器选择 |
| 创建 trial | 选择 program 后自动带出 crop；trial 的 crop 必须与 program.crop_id 一致 |
| 创建 location | 选择 program 后可自动带出 crop，但允许地点作为公共地点不绑定 program |
| 创建 study | 先选 program，再按 program 过滤 trial；选择 trial 后自动带出 crop |
| 创建 study | location 可按 program/crop 过滤；season 支持多选 |
| 编辑 study | 如果已有 observation_unit/sample/observation，变更 program/trial/crop/location 前必须二次确认并检查一致性 |
| Study 工作台 | 入口必须携带 study_id；下游新增 observation_unit、sample、variantset 默认继承该 study 上下文 |
| 外键显示 | 前端展示名称，提交保存 ID；表格列需要同时支持名称展示和 ID 调试查看 |

## 10. 删除/停用规则

| 对象 | 删除/停用规则 |
| --- | --- |
| crop | 已被 program、location、trial、study、germplasm、observation_variable、genome_map 引用时，不允许物理删除 |
| person | 已作为 program 负责人、trial 联系人、study 联系人、list owner 时，不允许物理删除 |
| program | 已有关联 trial、study、location、crossing_project、seed_lot、plate、sample 时，不允许物理删除 |
| location | 已被 study 或 seed_lot 引用时，不允许物理删除 |
| trial | 已被 study、observation_unit、observation、plate、sample 引用时，不允许物理删除 |
| season | 已被 study_season 或 observation 引用时，不允许物理删除 |
| study | 已被 observation_unit、event、observation、plate、sample、variantset 引用时，不允许物理删除 |
| list | 已有 list_item 时，删除前必须提示；可先清空明细再删除 |

删除失败时，后端应返回被引用对象类型和数量，前端弹窗展示，例如：

```text
该 study 已被以下数据引用，不能删除：
- observation_unit: 120
- observation: 560
- sample: 96
请先处理下游数据，或将 study 停用。
```

## 11. 导入导出需求

### 11.1 导入对象

Core 模块至少支持以下对象导入：

```text
crop
person
program
location
trial
season
study
list
list_item
trial_contact
study_contact
study_season
```

### 11.2 导入流程

1. 用户选择对象类型，例如 `study`。
2. 下载 CSV/Excel 模板。
3. 用户填写模板并上传。
4. 系统做预校验：字段完整性、必填、格式、外键是否存在、联动关系是否一致。
5. 预览页展示可导入行、错误行、警告行。
6. 用户确认后提交导入。
7. 导入完成后生成导入报告。

### 11.3 导入模板要求

| 要求 | 说明 |
| --- | --- |
| 字段名 | 使用数据库字段名作为模板列名 |
| 外键列 | 支持填写 ID；可额外支持名称匹配，但名称重复时必须报错 |
| 错误报告 | 返回行号、字段名、错误原因、建议修正方式 |
| 幂等策略 | 可按 ID 更新；无 ID 时新增 |
| 权限 | 数据录入员可导入 study 下游数据；Core 主数据导入建议仅项目管理员/数据管理员可操作 |

### 11.4 导出需求

1. 所有列表页支持导出当前筛选结果。
2. 导出文件应包含 ID 和展示名称。
3. study 导出应支持导出工作台摘要，包括联系人、season、下游数据数量。
4. 导出大数据量时应走异步任务。

## 12. 验收标准

### 12.1 前端验收标准

1. 所有列表页支持分页、关键词搜索、基础筛选。
2. 所有外键字段前端展示名称，提交保存 ID。
3. 创建 program 时，必须选择 crop。
4. 创建 trial 时，选择 program 后自动带出 crop。
5. 创建 study 时，必须选择 program、trial、crop、location。
6. Study 列表页支持按 crop、program、trial、location、season、active 筛选。
7. 点击 study 名称或“进入工作台”按钮后进入 Study 工作台。
8. Study 工作台可以看到观测单元、表型、样本、基因型入口。
9. 删除被引用数据时，前端展示引用详情，不允许静默失败。
10. 导入预校验结果能展示错误行、错误字段、错误原因。

### 12.2 后端验收标准

1. 创建 program 时校验 `crop_id` 存在。
2. 创建 trial 时校验 `program_id` 存在，且 trial.crop_id 与 program.crop_id 一致。
3. 创建 study 时校验 `program_id`、`trial_id`、`crop_id`、`location_id` 存在。
4. 创建 study 时校验 trial 属于所选 program。
5. 删除 crop/program/trial/study 前必须检查下游引用。
6. `GET /studies/{id}/workbench` 能返回工作台所需聚合数量。
7. 选择器接口支持 keyword 模糊搜索和上游过滤参数。
8. 导入接口支持 preview 和 commit 两阶段。

### 12.3 测试点

1. 选择 program 后，trial 下拉框只展示该 program 下的数据。
2. 选择 trial 后，系统自动带出 crop。
3. 已被 study 引用的 trial 不能直接删除。
4. 已被 observation_unit、sample、observation 引用的 study 不能直接删除。
5. study 创建成功后自动进入 Study 工作台。
6. 修改 study 的 program/trial/crop 时，如果已有下游数据，必须提示风险。
7. 导入 study 时，如果 trial 不属于 program，应报错并指出行号。
8. 导入 location 时，如果 parent_location_id 指向自己，应报错。
9. 导出列表时，筛选条件必须生效。
10. 所有接口返回的分页字段一致，便于前端表格复用。
