# 科研开放数据接口 — 测试用例

> 依据：`科研开放数据接口功能需求.md`、`科研开放数据接口技术方案.md`  
> **Base Path**：`/open-api/v1/research`（默认服务 `http://localhost:8010`）

**通用前置**：`application.yml` 中 `research-open-api.enabled=true`，且已配置有效的 `app-key` / `app-secret` / `token-secret`。

**测试凭证（示例，与配置一致）**：

| 项 | 值 |
| --- | --- |
| appKey | `research-open-demo-key` |
| appSecret | `research-open-demo-secret-change-me` |

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| KYKF-UT-001 | 脱敏 | 手机号脱敏 | 单元测试 | JUnit5 | §7 | — | `maskPhone("13812345678")` | `138****5678` |
| KYKF-UT-002 | 脱敏 | 负责人脱敏 | 单元测试 | JUnit5 | §7 | — | `maskPersonName("张三")` | `张*` |
| KYKF-UT-003 | 脱敏 | 牧场 VO 无第三方 ID | 单元测试 | JUnit5 | §6.2 | 构造 BizPasture | `toPastureVo` | 含 `regionSummary`；无 `externalId`、无详细地址 |
| KYKF-UT-004 | 脱敏 | 档案 subjectCode | 单元测试 | JUnit5 | §6.3 | id=88 | `toYakAssetVo` | `subjectCode=YS00000088`；无 `yakNo` |
| KYKF-UT-005 | Token | 合法凭证签发校验 | 单元测试 | JUnit5 | §5 | enabled=true | `issueToken` → `validateToken` | 不抛异常 |
| KYKF-UT-006 | Token | 错误 secret 拒绝 | 单元测试 | JUnit5 | §5 | — | `issueToken(key, wrong)` | 抛 ServiceException 401 |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| KYKF-API-001 | 认证 | 申请 Token 成功 | 接口测试 | curl/Postman | §6.1 | enabled=true | POST `/auth/token` 正确 body | `code=200`；`data.accessToken` 非空；`tokenType=Bearer` |
| KYKF-API-002 | 认证 | 错误 appSecret | 接口测试 | curl | §5 | — | POST 错误 secret | `code=401` |
| KYKF-API-003 | 认证 | 未带 Token 访问数据 | 接口测试 | curl | §5 | — | GET `/pastures` 无 Header | HTTP 401；`code=401` |
| KYKF-API-004 | 认证 | 过期/伪造 Token | 接口测试 | curl | §5 | — | GET `/pastures` Bearer 无效串 | HTTP 401 |
| KYKF-API-005 | 牧场 | 分页列表 | 接口测试 | curl | §6.2 | 有效 Token；库有数据 | GET `/pastures?pageNum=1&pageSize=10` | `code=200`；`data.records` 数组；字段符合 ResearchPastureVo |
| KYKF-API-006 | 牧场 | keyword 筛选 | 接口测试 | curl | §6.2 | 已知牧场名 | GET `keyword=片段` | 仅匹配名称的记录 |
| KYKF-API-007 | 档案 | 分页列表 | 接口测试 | curl | §6.3 | 有效 Token | GET `/yak-assets?pageNum=1&pageSize=10` | `code=200`；含 `subjectCode`；无明文编号字段 |
| KYKF-API-008 | 档案 | assetStatus 筛选 | 接口测试 | curl | §6.3 | 有 status=1 数据 | GET `assetStatus=1` | 均为正常状态 |
| KYKF-API-009 | 档案 | pastureId 筛选 | 接口测试 | curl | §6.3 | 已知 pastureId | GET `pastureId=` | 仅该牧场档案 |
| KYKF-API-010 | 分页 | pageSize 超限 | 接口测试 | curl | 技术§2 | — | GET `pageSize=500` | `code=400` |
| KYKF-API-011 | 开关 | enabled=false | 接口测试 | curl | §5 | 关闭 enabled 重启 | POST `/auth/token` | HTTP 503 或 `code=503` |
| KYKF-API-012 | 脱敏 | 响应无敏感原文字段 | 接口测试 | curl | §7 | 库含手机/编号 | GET 两接口 | JSON 无 `yakNo`、`earTagNumber`、`externalId`、`detailAddress` |
| KYKF-API-013 | 存栏 | 分页列表 | 接口测试 | curl | §6.4 | 有效 Token；库有存栏数据 | GET `/yak-herd-inventories?pageNum=1&pageSize=10` | `code=200`；含 `statYear`/`townName` 等；无部门 ID |
| KYKF-API-014 | 存栏 | 年/月/乡镇村筛选 | 接口测试 | curl | §6.4 | 已知年月与乡镇名片段 | GET `statYear=&statMonth=&townName=&villageName=` | 条件生效；名称模糊匹配 |
| KYKF-API-015 | 出栏 | 分页列表 | 接口测试 | curl | §6.5 | 有效 Token；库有出栏数据 | GET `/yak-outbound-reports?pageNum=1&pageSize=10` | `code=200`；含出栏汇总字段；无部门 ID |
| KYKF-API-016 | 出栏 | 年/月/乡镇村筛选 | 接口测试 | curl | §6.5 | 已知年月与村名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-017 | 合作社 | 分页列表 | 接口测试 | curl | §6.6 | 有效 Token；库有合作社数据 | GET `/cooperative-developments?pageNum=1&pageSize=10` | `code=200`；法人已脱敏；无部门 ID |
| KYKF-API-018 | 合作社 | 年/月/乡镇村/名称筛选 | 接口测试 | curl | §6.6 | 已知合作社名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-019 | 产量 | 分页列表 | 接口测试 | curl | §6.7 | 有效 Token；库有产量数据 | GET `/livestock-product-outputs?pageNum=1&pageSize=10` | `code=200`；含肉/奶产量；无部门 ID |
| KYKF-API-020 | 产量 | 年/月/乡镇村筛选 | 接口测试 | curl | §6.7 | 已知年月与乡镇名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-021 | 家庭牧场 | 分页列表 | 接口测试 | curl | §6.8 | 有效 Token；库有牧场数据 | GET `/family-ranches?pageNum=1&pageSize=10` | `code=200`；法人已脱敏；无部门 ID |
| KYKF-API-022 | 家庭牧场 | 年/月/乡镇村/企业名筛选 | 接口测试 | curl | §6.8 | 已知企业名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-023 | 可支配收入 | 分页列表 | 接口测试 | curl | §6.9 | 有效 Token；库有收入数据 | GET `/disposable-incomes?pageNum=1&pageSize=10` | `code=200`；无操作人/备注 |
| KYKF-API-024 | 可支配收入 | 年份筛选 | 接口测试 | curl | §6.9 | 已知年份 | GET 带 `statYear` | 仅返回该年记录 |
| KYKF-API-025 | 农牧户 | 分页列表 | 接口测试 | curl | §6.10 | 有效 Token；库有农牧户数据 | GET `/farmer-households?pageNum=1&pageSize=10` | `code=200`；无部门 ID |
| KYKF-API-026 | 农牧户 | 年/月/乡镇村筛选 | 接口测试 | curl | §6.10 | 已知年月与乡镇名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-027 | 共同富裕项目 | 分页列表 | 接口测试 | curl | §6.11 | 有效 Token；库有项目数据 | GET `/common-prosperity-projects?pageNum=1&pageSize=10` | `code=200`；无本地路径/操作人 |
| KYKF-API-028 | 共同富裕项目 | 名称/类型/年限筛选 | 接口测试 | curl | §6.11 | 已知项目名片段与类型 | GET 带筛选参数 | 条件生效；名称模糊、类型与年限精确 |
| KYKF-API-029 | 养殖大户 | 分页列表 | 接口测试 | curl | §6.12 | 有效 Token；库有大户数据 | GET `/large-livestock-farmers?pageNum=1&pageSize=10` | `code=200`；法人已脱敏；无部门 ID |
| KYKF-API-030 | 养殖大户 | 年/月/乡镇村/企业名筛选 | 接口测试 | curl | §6.12 | 已知企业名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-031 | 疫苗接种 | 分页列表 | 接口测试 | curl | §6.13 | 有效 Token；库有接种数据 | GET `/vaccination-data?pageNum=1&pageSize=10` | `code=200`；无部门 ID |
| KYKF-API-032 | 疫苗接种 | 年/月/乡镇村筛选 | 接口测试 | curl | §6.13 | 已知年月与乡镇名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-033 | 有无畜户 | 分页列表 | 接口测试 | curl | §6.14 | 有效 Token；库有畜户数据 | GET `/livestock-ownership-households?pageNum=1&pageSize=10` | `code=200`；无部门 ID |
| KYKF-API-034 | 有无畜户 | 年/月/乡镇村筛选 | 接口测试 | curl | §6.14 | 已知年月与乡镇名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-035 | 农村三资 | 分页列表 | 接口测试 | curl | §6.15 | 有效 Token；库有三资数据 | GET `/rural-three-assets?pageNum=1&pageSize=10` | `code=200`；无部门 ID |
| KYKF-API-036 | 农村三资 | 乡镇村筛选 | 接口测试 | curl | §6.15 | 已知乡镇名片段 | GET 带筛选参数 | 条件生效；名称模糊匹配 |
| KYKF-API-037 | 三资具体问题 | 分页列表 | 接口测试 | curl | §6.16 | 有效 Token；库有问题数据 | GET `/three-assets-problem-reports?pageNum=1&pageSize=10` | `code=200`；无部门 ID |
| KYKF-API-038 | 三资具体问题 | 乡镇村/类型/上报区间 | 接口测试 | curl | §6.16 | 已知类型与日期区间 | GET 带筛选参数（起止日期均传） | 条件生效；仅起止同时有值时日期过滤生效 |
| KYKF-API-039 | 科技服务人员 | 详情 | 接口测试 | curl | §6.17 | 有效 Token；已配置人员数据 | GET `/tech-service-personnel` | `code=200`；返回各级人数；无操作人 |

---

## 三、需求追溯矩阵

| 功能需求 | 单元 | 接口 |
| --- | --- | --- |
| §5 Token 认证 | UT-005～006 | API-001～004、011 |
| §6.2 牧场列表 | UT-003 | API-005～006 |
| §6.3 档案列表 | UT-004 | API-007～009 |
| §6.4 存栏列表 | — | API-013～014 |
| §6.5 出栏列表 | — | API-015～016 |
| §6.6 合作社列表 | — | API-017～018 |
| §6.7 产量列表 | — | API-019～020 |
| §6.8 家庭牧场列表 | — | API-021～022 |
| §6.9 可支配收入列表 | — | API-023～024 |
| §6.10 农牧户列表 | — | API-025～026 |
| §6.11 共同富裕项目列表 | — | API-027～028 |
| §6.12 养殖大户列表 | — | API-029～030 |
| §6.13 疫苗接种列表 | — | API-031～032 |
| §6.14 有无畜户列表 | — | API-033～034 |
| §6.15 农村三资列表 | — | API-035～036 |
| §6.16 三资具体问题列表 | — | API-037～038 |
| §6.17 科技服务人员详情 | — | API-039 |
| §7 脱敏 | UT-001～004 | API-012 |
| §2 分页 | — | API-010 |
| §3 无机构区分 | UT-005 | API-001（单一凭证） |

**用例统计**：单元 **6**、接口 **39**，合计 **45** 条。

---

## 四、Postman / curl 快速脚本

```bash
BASE=http://localhost:8010/open-api/v1/research
TOKEN=$(curl -s -X POST "$BASE/auth/token" \
  -H "Content-Type: application/json" \
  -d '{"appKey":"research-open-demo-key","appSecret":"research-open-demo-secret-change-me"}' \
  | jq -r '.data.accessToken')

curl -s "$BASE/pastures?pageNum=1&pageSize=5" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/yak-assets?pageNum=1&pageSize=5&assetStatus=1" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/yak-herd-inventories?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/yak-outbound-reports?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/cooperative-developments?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/livestock-product-outputs?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/family-ranches?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/disposable-incomes?pageNum=1&pageSize=5&statYear=2025" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/farmer-households?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/common-prosperity-projects?pageNum=1&pageSize=5&implYear=2025" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/large-livestock-farmers?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/vaccination-data?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/livestock-ownership-households?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/rural-three-assets?pageNum=1&pageSize=5" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/three-assets-problem-reports?pageNum=1&pageSize=5&beginReportTime=2026-01-01&endReportTime=2026-12-31" -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/tech-service-personnel" -H "Authorization: Bearer $TOKEN"
```

（Windows 环境可用 PowerShell `Invoke-RestMethod`，见技术方案 **§8**。）
