|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+# 科研开放数据接口 — 技术方案
|
|
|
2
|
+
|
|
|
3
|
+> 依据:同目录 `科研开放数据接口功能需求.md`。对外只读开放 API;**无**前端页面、**无**新表。
|
|
|
4
|
+
|
|
|
5
|
+---
|
|
|
6
|
+
|
|
|
7
|
+## 1. 技术架构
|
|
|
8
|
+
|
|
|
9
|
+| 项 | 说明 |
|
|
|
10
|
+| --- | --- |
|
|
|
11
|
+| **后端** | RuoYi **v3.9.2**(**springboot2** 分支):JDK 8、Spring MVC、MyBatis |
|
|
|
12
|
+| **数据库** | 只读查询既有表 `biz_pasture`、`biz_yak_asset` |
|
|
|
13
|
+| **鉴权** | 独立 JWT + Servlet Filter;`@Anonymous` 绕过若依登录,Filter 校验 Bearer Token |
|
|
|
14
|
+| **响应** | `OpenApiResponse<T>` + `OpenApiPageResult<T>`(与第三方 farming OpenAPI 同结构) |
|
|
|
15
|
+
|
|
|
16
|
+**代码包**:`baqing-admin` → `com.ruoyi.web.modules.industryservice.research`
|
|
|
17
|
+
|
|
|
18
|
+| 类 | 职责 |
|
|
|
19
|
+| --- | --- |
|
|
|
20
|
+| `ResearchOpenApiController` | HTTP 入口 |
|
|
|
21
|
+| `ResearchOpenApiServiceImpl` | 分页查询 + 脱敏 VO 转换 |
|
|
|
22
|
+| `ResearchOpenApiTokenService` | 签发/校验 JWT |
|
|
|
23
|
+| `ResearchOpenApiAuthFilter` | 除 `/auth/token` 外校验 Token |
|
|
|
24
|
+| `ResearchOpenApiMasking` / `ResearchOpenApiSupport` | 脱敏与响应组装 |
|
|
|
25
|
+
|
|
|
26
|
+---
|
|
|
27
|
+
|
|
|
28
|
+## 2. 配置
|
|
|
29
|
+
|
|
|
30
|
+`application.yml` → `research-open-api`:
|
|
|
31
|
+
|
|
|
32
|
+| 键 | 说明 |
|
|
|
33
|
+| --- | --- |
|
|
|
34
|
+| `enabled` | 是否启用;`false` 时返回 503 |
|
|
|
35
|
+| `app-key` | 申请 Token 用(全局唯一) |
|
|
|
36
|
+| `app-secret` | 申请 Token 用 |
|
|
|
37
|
+| `token-secret` | JWT 签名密钥(**仅服务端**,勿下发) |
|
|
|
38
|
+| `token-expire-minutes` | Token 有效期,默认 120 |
|
|
|
39
|
+| `max-page-size` | 单页上限,默认 200 |
|
|
|
40
|
+
|
|
|
41
|
+**示例:**
|
|
|
42
|
+
|
|
|
43
|
+```yaml
|
|
|
44
|
+research-open-api:
|
|
|
45
|
+ enabled: true
|
|
|
46
|
+ app-key: research-open-demo-key
|
|
|
47
|
+ app-secret: research-open-demo-secret-change-me
|
|
|
48
|
+ token-secret: research-open-api-change-me-in-production
|
|
|
49
|
+ token-expire-minutes: 120
|
|
|
50
|
+ max-page-size: 200
|
|
|
51
|
+```
|
|
|
52
|
+
|
|
|
53
|
+---
|
|
|
54
|
+
|
|
|
55
|
+## 3. 接口设计
|
|
|
56
|
+
|
|
|
57
|
+**Base Path**:`/open-api/v1/research`
|
|
|
58
|
+
|
|
|
59
|
+### 3.1 申请 Token
|
|
|
60
|
+
|
|
|
61
|
+```
|
|
|
62
|
+POST /open-api/v1/research/auth/token
|
|
|
63
|
+Content-Type: application/json
|
|
|
64
|
+
|
|
|
65
|
+{"appKey":"research-open-demo-key","appSecret":"research-open-demo-secret-change-me"}
|
|
|
66
|
+```
|
|
|
67
|
+
|
|
|
68
|
+**成功响应 `data`:**
|
|
|
69
|
+
|
|
|
70
|
+```json
|
|
|
71
|
+{
|
|
|
72
|
+ "accessToken": "eyJhbGciOiJIUzUxMiJ9...",
|
|
|
73
|
+ "tokenType": "Bearer",
|
|
|
74
|
+ "expiresIn": 7200
|
|
|
75
|
+}
|
|
|
76
|
+```
|
|
|
77
|
+
|
|
|
78
|
+JWT Claims:`type=research_open_api`;**不含**机构 ID。
|
|
|
79
|
+
|
|
|
80
|
+### 3.2 牧场列表
|
|
|
81
|
+
|
|
|
82
|
+```
|
|
|
83
|
+GET /open-api/v1/research/pastures?pageNum=1&pageSize=20&keyword=
|
|
|
84
|
+Authorization: Bearer <accessToken>
|
|
|
85
|
+```
|
|
|
86
|
+
|
|
|
87
|
+**`records[]` 元素(ResearchPastureVo):**
|
|
|
88
|
+
|
|
|
89
|
+| 字段 | 类型 | 说明 |
|
|
|
90
|
+| --- | --- | --- |
|
|
|
91
|
+| `pastureId` | long | 本地牧场 ID |
|
|
|
92
|
+| `pastureName` | string | 牧场名称 |
|
|
|
93
|
+| `farmType` | string | 类型 |
|
|
|
94
|
+| `county` / `town` | string | 区县、乡镇 |
|
|
|
95
|
+| `regionSummary` | string | 区县+乡镇摘要 |
|
|
|
96
|
+| `latitude` / `longitude` | number | 约百米精度 |
|
|
|
97
|
+| `floorArea` / `scaleBreeding` / `breedSpecies` | string | 规模类 |
|
|
|
98
|
+| `introduction` | string | 简介 |
|
|
|
99
|
+| `personInCharge` | string | 脱敏负责人 |
|
|
|
100
|
+| `contactPhone` | string | 脱敏手机 |
|
|
|
101
|
+
|
|
|
102
|
+### 3.3 牦牛资产档案列表
|
|
|
103
|
+
|
|
|
104
|
+```
|
|
|
105
|
+GET /open-api/v1/research/yak-assets?pageNum=1&pageSize=20&assetStatus=1&gender=母&pastureId=
|
|
|
106
|
+Authorization: Bearer <accessToken>
|
|
|
107
|
+```
|
|
|
108
|
+
|
|
|
109
|
+**`records[]` 元素(ResearchYakAssetVo):**
|
|
|
110
|
+
|
|
|
111
|
+| 字段 | 类型 | 说明 |
|
|
|
112
|
+| --- | --- | --- |
|
|
|
113
|
+| `subjectCode` | string | 科研主体码,如 `YS00000088` |
|
|
|
114
|
+| `pastureName` / `enclosureName` | string | 牧场、圈舍名称 |
|
|
|
115
|
+| `gender` / `cattleVariety` | string | 性别、品种 |
|
|
|
116
|
+| `ageMonths` | int | 月龄 |
|
|
|
117
|
+| `birthYear` / `birthMonth` | int | 出生年月 |
|
|
|
118
|
+| `assetStatus` | int | 1~4 |
|
|
|
119
|
+| `assetStatusLabel` | string | 正常/死淘/丢失/出栏 |
|
|
|
120
|
+| `entryWeightKg` | decimal | 入栏体重 |
|
|
|
121
|
+| `source` / `breedingMethod` / `entryCycle` | string | 来源、养殖方式、周期 |
|
|
|
122
|
+| `realtimeTemp` / `realtimeSteps` / `envTemp` | number | IoT 快照 |
|
|
|
123
|
+| `fatherSubjectCode` / `motherSubjectCode` | string | 系谱 pseudonym |
|
|
|
124
|
+
|
|
|
125
|
+---
|
|
|
126
|
+
|
|
|
127
|
+## 4. 鉴权流程
|
|
|
128
|
+
|
|
|
129
|
+1. `ResearchOpenApiController` 标注 `@Anonymous` → Spring Security 放行。
|
|
|
130
|
+2. `ResearchOpenApiAuthFilter` 拦截 `/open-api/v1/research/*`(**排除** `/auth/token`)。
|
|
|
131
|
+3. 解析 `Authorization: Bearer …`,`ResearchOpenApiTokenService.validateToken` 验签与过期。
|
|
|
132
|
+4. 失败返回 JSON `{ code:401, message:"..." }`,HTTP 401。
|
|
|
133
|
+
|
|
|
134
|
+Token 申请:`appKey`/`appSecret` 与配置项精确匹配后签发 JWT(HS512 + `token-secret`)。
|
|
|
135
|
+
|
|
|
136
|
+---
|
|
|
137
|
+
|
|
|
138
|
+## 5. 数据查询
|
|
|
139
|
+
|
|
|
140
|
+复用既有 Service / Mapper:
|
|
|
141
|
+
|
|
|
142
|
+| 接口 | Service | 表 | 条件 |
|
|
|
143
|
+| --- | --- | --- | --- |
|
|
|
144
|
+| 牧场列表 | `IBizPastureService.selectBizPastureList` | `biz_pasture` | `del_flag=0` |
|
|
|
145
|
+| 档案列表 | `IBizYakAssetService.selectBizYakAssetList` | `biz_yak_asset` | `del_flag=0`;支持 `assetStatus`、`gender`、`pastureId` |
|
|
|
146
|
+
|
|
|
147
|
+实体 → VO:`ResearchOpenApiSupport.toPastureVo` / `toYakAssetVo`(内部调用 `ResearchOpenApiMasking`)。
|
|
|
148
|
+
|
|
|
149
|
+---
|
|
|
150
|
+
|
|
|
151
|
+## 6. 脱敏实现
|
|
|
152
|
+
|
|
|
153
|
+| 方法 | 说明 |
|
|
|
154
|
+| --- | --- |
|
|
|
155
|
+| `maskPhone` | 复用 `SubsidyGrantRecordMasking.maskPhone` |
|
|
|
156
|
+| `maskPersonName` | 首字 + `*` |
|
|
|
157
|
+| `regionSummary` | 区县 + 乡镇 |
|
|
|
158
|
+| `roundCoordinate` | 经纬度 3 位小数 |
|
|
|
159
|
+| `subjectCode` | `YS` + 8 位 `id` |
|
|
|
160
|
+| `pseudonymCode` | 系谱编号哈希短码 |
|
|
|
161
|
+
|
|
|
162
|
+---
|
|
|
163
|
+
|
|
|
164
|
+## 7. 异常码
|
|
|
165
|
+
|
|
|
166
|
+| code | HTTP | 场景 |
|
|
|
167
|
+| --- | --- | --- |
|
|
|
168
|
+| 200 | 200 | 成功 |
|
|
|
169
|
+| 401 | 401 | appKey/appSecret 错误、Token 缺失/无效/过期 |
|
|
|
170
|
+| 400 | 200* | pageSize 超限(body.code=400) |
|
|
|
171
|
+| 503 | 503 | `enabled=false` 或未配置凭证 |
|
|
|
172
|
+
|
|
|
173
|
+\* 业务错误仍返回 JSON body,HTTP 状态与 `code` 一致(Filter 401/503;Controller 内 ServiceException 写入 body.code)。
|
|
|
174
|
+
|
|
|
175
|
+---
|
|
|
176
|
+
|
|
|
177
|
+## 8. 调用示例
|
|
|
178
|
+
|
|
|
179
|
+```bash
|
|
|
180
|
+# 1. 申请 Token
|
|
|
181
|
+curl -X POST "http://localhost:8010/open-api/v1/research/auth/token" \
|
|
|
182
|
+ -H "Content-Type: application/json" \
|
|
|
183
|
+ -d '{"appKey":"research-open-demo-key","appSecret":"research-open-demo-secret-change-me"}'
|
|
|
184
|
+
|
|
|
185
|
+# 2. 牧场列表
|
|
|
186
|
+curl "http://localhost:8010/open-api/v1/research/pastures?pageNum=1&pageSize=10" \
|
|
|
187
|
+ -H "Authorization: Bearer <accessToken>"
|
|
|
188
|
+
|
|
|
189
|
+# 3. 牦牛档案
|
|
|
190
|
+curl "http://localhost:8010/open-api/v1/research/yak-assets?pageNum=1&pageSize=10&assetStatus=1" \
|
|
|
191
|
+ -H "Authorization: Bearer <accessToken>"
|
|
|
192
|
+```
|
|
|
193
|
+
|
|
|
194
|
+---
|
|
|
195
|
+
|
|
|
196
|
+## 9. 测试
|
|
|
197
|
+
|
|
|
198
|
+见同目录 `科研开放数据接口测试用例.md`。单元测试:
|
|
|
199
|
+
|
|
|
200
|
+- `ResearchOpenApiMaskingTest`
|
|
|
201
|
+- `ResearchOpenApiTokenServiceTest`
|
|
|
202
|
+- `ResearchOpenApiControllerApiTest`
|
|
|
203
|
+
|
|
|
204
|
+---
|
|
|
205
|
+
|
|
|
206
|
+## 10. 需求追溯
|
|
|
207
|
+
|
|
|
208
|
+| 功能需求 | 技术落点 |
|
|
|
209
|
+| --- | --- |
|
|
|
210
|
+| §5 认证 | **§2** 配置、**§4** Filter + TokenService |
|
|
|
211
|
+| §6.2 牧场 | **§3.2**、**§5** BizPasture |
|
|
|
212
|
+| §6.3 档案 | **§3.3**、**§5** BizYakAsset |
|
|
|
213
|
+| §7 脱敏 | **§6** Masking |
|
|
|
214
|
+| §3 不实现机构区分 | 单一 app-key/secret,JWT 无 clientId |
|
|
|
215
|
+
|
|
|
216
|
+---
|
|
|
217
|
+
|
|
|
218
|
+## 11. 修订记录
|
|
|
219
|
+
|
|
|
220
|
+| 版本 | 日期 | 说明 |
|
|
|
221
|
+| --- | --- | --- |
|
|
|
222
|
+| 1.0 | 2026-07-04 | 初版:Token + 牧场/档案脱敏列表;全局一对凭证 |
|