wwh hai 1 semana
pai
achega
9ebb6cd3fe

+ 25 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/research/controller/ResearchOpenApiController.java

@@ -17,6 +17,7 @@ import com.ruoyi.web.modules.industryservice.research.domain.dto.ResearchTokenVo
17 17
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchCooperativeDevelopmentVo;
18 18
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchDisposableIncomeVo;
19 19
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFamilyRanchVo;
20
+import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFarmerHouseholdVo;
20 21
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchLivestockProductOutputVo;
21 22
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchPastureVo;
22 23
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchYakAssetVo;
@@ -244,4 +245,28 @@ public class ResearchOpenApiController
244 245
             return ResearchOpenApiSupport.error(code, ex.getMessage());
245 246
         }
246 247
     }
248
+
249
+    /**
250
+     * 农牧户数据列表(村级汇总)。
251
+     */
252
+    @GetMapping("/farmer-households")
253
+    public OpenApiResponse<OpenApiPageResult<ResearchFarmerHouseholdVo>> listFarmerHouseholds(
254
+            @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
255
+            @RequestParam(value = "pageSize", defaultValue = "20") int pageSize,
256
+            @RequestParam(value = "statYear", required = false) Integer statYear,
257
+            @RequestParam(value = "statMonth", required = false) Integer statMonth,
258
+            @RequestParam(value = "townName", required = false) String townName,
259
+            @RequestParam(value = "villageName", required = false) String villageName)
260
+    {
261
+        try
262
+        {
263
+            return ResearchOpenApiSupport.success(researchOpenApiService.listFarmerHouseholds(
264
+                    pageNum, pageSize, statYear, statMonth, townName, villageName));
265
+        }
266
+        catch (ServiceException ex)
267
+        {
268
+            int code = ex.getCode() != null ? ex.getCode() : 500;
269
+            return ResearchOpenApiSupport.error(code, ex.getMessage());
270
+        }
271
+    }
247 272
 }

+ 321 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/research/domain/vo/ResearchFarmerHouseholdVo.java

@@ -0,0 +1,321 @@
1
+package com.ruoyi.web.modules.industryservice.research.domain.vo;
2
+
3
+import java.math.BigDecimal;
4
+
5
+/**
6
+ * 科研开放接口 — 农牧户填报记录(村级汇总;不含部门 ID / 备注)。
7
+ */
8
+public class ResearchFarmerHouseholdVo
9
+{
10
+    /** 农牧户记录 ID */
11
+    private Long householdId;
12
+
13
+    /** 所属年份 */
14
+    private Integer statYear;
15
+
16
+    /** 所属月份(1~12) */
17
+    private Integer statMonth;
18
+
19
+    /** 所属乡镇名称 */
20
+    private String townName;
21
+
22
+    /** 所属村名称 */
23
+    private String villageName;
24
+
25
+    /** 户数 */
26
+    private Integer householdCount;
27
+
28
+    /** 人数 */
29
+    private Integer populationCount;
30
+
31
+    /** 草场承包面积(亩,派生:禁牧面积 + 草畜平衡面积) */
32
+    private BigDecimal pastureContractArea;
33
+
34
+    /** 禁牧面积(亩) */
35
+    private BigDecimal grazingBanArea;
36
+
37
+    /** 草畜平衡面积(亩) */
38
+    private BigDecimal grassBalanceArea;
39
+
40
+    /** 核定年末草畜平衡载畜量(绵羊单位) */
41
+    private BigDecimal approvedCarryingCapacity;
42
+
43
+    /** 年底牲畜存栏(绵羊单位) */
44
+    private BigDecimal yearEndStockSheepUnit;
45
+
46
+    /** 是否纯牧户(是/否) */
47
+    private String isPureHerder;
48
+
49
+    /** 是否无畜户(是/否) */
50
+    private String isNoLivestock;
51
+
52
+    /** 是否消亡户(是/否) */
53
+    private String isExtinctHousehold;
54
+
55
+    /** 是否超载牧户(是/否) */
56
+    private String isOverloadHerder;
57
+
58
+    /** 是否需要封顶(是/否) */
59
+    private String needCap;
60
+
61
+    /** 是否需要保底(是/否) */
62
+    private String needFloor;
63
+
64
+    /** 禁牧补助(元) */
65
+    private BigDecimal grazingBanSubsidy;
66
+
67
+    /** 草畜平衡奖励(元) */
68
+    private BigDecimal grassBalanceReward;
69
+
70
+    /** 补助奖励小计(元,派生:禁牧补助 + 草畜平衡奖励) */
71
+    private BigDecimal subsidyRewardSubtotal;
72
+
73
+    /** 封顶保底后享受补助奖励总资金(元) */
74
+    private BigDecimal afterCapFloorSubsidy;
75
+
76
+    /** 村级草原监督员补助(元) */
77
+    private BigDecimal villageSupervisorSubsidy;
78
+
79
+    /** 补助奖励总资金(元,派生) */
80
+    private BigDecimal subsidyTotal;
81
+
82
+    public Long getHouseholdId()
83
+    {
84
+        return householdId;
85
+    }
86
+
87
+    public void setHouseholdId(Long householdId)
88
+    {
89
+        this.householdId = householdId;
90
+    }
91
+
92
+    public Integer getStatYear()
93
+    {
94
+        return statYear;
95
+    }
96
+
97
+    public void setStatYear(Integer statYear)
98
+    {
99
+        this.statYear = statYear;
100
+    }
101
+
102
+    public Integer getStatMonth()
103
+    {
104
+        return statMonth;
105
+    }
106
+
107
+    public void setStatMonth(Integer statMonth)
108
+    {
109
+        this.statMonth = statMonth;
110
+    }
111
+
112
+    public String getTownName()
113
+    {
114
+        return townName;
115
+    }
116
+
117
+    public void setTownName(String townName)
118
+    {
119
+        this.townName = townName;
120
+    }
121
+
122
+    public String getVillageName()
123
+    {
124
+        return villageName;
125
+    }
126
+
127
+    public void setVillageName(String villageName)
128
+    {
129
+        this.villageName = villageName;
130
+    }
131
+
132
+    public Integer getHouseholdCount()
133
+    {
134
+        return householdCount;
135
+    }
136
+
137
+    public void setHouseholdCount(Integer householdCount)
138
+    {
139
+        this.householdCount = householdCount;
140
+    }
141
+
142
+    public Integer getPopulationCount()
143
+    {
144
+        return populationCount;
145
+    }
146
+
147
+    public void setPopulationCount(Integer populationCount)
148
+    {
149
+        this.populationCount = populationCount;
150
+    }
151
+
152
+    public BigDecimal getPastureContractArea()
153
+    {
154
+        return pastureContractArea;
155
+    }
156
+
157
+    public void setPastureContractArea(BigDecimal pastureContractArea)
158
+    {
159
+        this.pastureContractArea = pastureContractArea;
160
+    }
161
+
162
+    public BigDecimal getGrazingBanArea()
163
+    {
164
+        return grazingBanArea;
165
+    }
166
+
167
+    public void setGrazingBanArea(BigDecimal grazingBanArea)
168
+    {
169
+        this.grazingBanArea = grazingBanArea;
170
+    }
171
+
172
+    public BigDecimal getGrassBalanceArea()
173
+    {
174
+        return grassBalanceArea;
175
+    }
176
+
177
+    public void setGrassBalanceArea(BigDecimal grassBalanceArea)
178
+    {
179
+        this.grassBalanceArea = grassBalanceArea;
180
+    }
181
+
182
+    public BigDecimal getApprovedCarryingCapacity()
183
+    {
184
+        return approvedCarryingCapacity;
185
+    }
186
+
187
+    public void setApprovedCarryingCapacity(BigDecimal approvedCarryingCapacity)
188
+    {
189
+        this.approvedCarryingCapacity = approvedCarryingCapacity;
190
+    }
191
+
192
+    public BigDecimal getYearEndStockSheepUnit()
193
+    {
194
+        return yearEndStockSheepUnit;
195
+    }
196
+
197
+    public void setYearEndStockSheepUnit(BigDecimal yearEndStockSheepUnit)
198
+    {
199
+        this.yearEndStockSheepUnit = yearEndStockSheepUnit;
200
+    }
201
+
202
+    public String getIsPureHerder()
203
+    {
204
+        return isPureHerder;
205
+    }
206
+
207
+    public void setIsPureHerder(String isPureHerder)
208
+    {
209
+        this.isPureHerder = isPureHerder;
210
+    }
211
+
212
+    public String getIsNoLivestock()
213
+    {
214
+        return isNoLivestock;
215
+    }
216
+
217
+    public void setIsNoLivestock(String isNoLivestock)
218
+    {
219
+        this.isNoLivestock = isNoLivestock;
220
+    }
221
+
222
+    public String getIsExtinctHousehold()
223
+    {
224
+        return isExtinctHousehold;
225
+    }
226
+
227
+    public void setIsExtinctHousehold(String isExtinctHousehold)
228
+    {
229
+        this.isExtinctHousehold = isExtinctHousehold;
230
+    }
231
+
232
+    public String getIsOverloadHerder()
233
+    {
234
+        return isOverloadHerder;
235
+    }
236
+
237
+    public void setIsOverloadHerder(String isOverloadHerder)
238
+    {
239
+        this.isOverloadHerder = isOverloadHerder;
240
+    }
241
+
242
+    public String getNeedCap()
243
+    {
244
+        return needCap;
245
+    }
246
+
247
+    public void setNeedCap(String needCap)
248
+    {
249
+        this.needCap = needCap;
250
+    }
251
+
252
+    public String getNeedFloor()
253
+    {
254
+        return needFloor;
255
+    }
256
+
257
+    public void setNeedFloor(String needFloor)
258
+    {
259
+        this.needFloor = needFloor;
260
+    }
261
+
262
+    public BigDecimal getGrazingBanSubsidy()
263
+    {
264
+        return grazingBanSubsidy;
265
+    }
266
+
267
+    public void setGrazingBanSubsidy(BigDecimal grazingBanSubsidy)
268
+    {
269
+        this.grazingBanSubsidy = grazingBanSubsidy;
270
+    }
271
+
272
+    public BigDecimal getGrassBalanceReward()
273
+    {
274
+        return grassBalanceReward;
275
+    }
276
+
277
+    public void setGrassBalanceReward(BigDecimal grassBalanceReward)
278
+    {
279
+        this.grassBalanceReward = grassBalanceReward;
280
+    }
281
+
282
+    public BigDecimal getSubsidyRewardSubtotal()
283
+    {
284
+        return subsidyRewardSubtotal;
285
+    }
286
+
287
+    public void setSubsidyRewardSubtotal(BigDecimal subsidyRewardSubtotal)
288
+    {
289
+        this.subsidyRewardSubtotal = subsidyRewardSubtotal;
290
+    }
291
+
292
+    public BigDecimal getAfterCapFloorSubsidy()
293
+    {
294
+        return afterCapFloorSubsidy;
295
+    }
296
+
297
+    public void setAfterCapFloorSubsidy(BigDecimal afterCapFloorSubsidy)
298
+    {
299
+        this.afterCapFloorSubsidy = afterCapFloorSubsidy;
300
+    }
301
+
302
+    public BigDecimal getVillageSupervisorSubsidy()
303
+    {
304
+        return villageSupervisorSubsidy;
305
+    }
306
+
307
+    public void setVillageSupervisorSubsidy(BigDecimal villageSupervisorSubsidy)
308
+    {
309
+        this.villageSupervisorSubsidy = villageSupervisorSubsidy;
310
+    }
311
+
312
+    public BigDecimal getSubsidyTotal()
313
+    {
314
+        return subsidyTotal;
315
+    }
316
+
317
+    public void setSubsidyTotal(BigDecimal subsidyTotal)
318
+    {
319
+        this.subsidyTotal = subsidyTotal;
320
+    }
321
+}

+ 10 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/research/service/IResearchOpenApiService.java

@@ -5,6 +5,7 @@ import com.ruoyi.web.modules.industryservice.research.domain.dto.ResearchTokenVo
5 5
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchCooperativeDevelopmentVo;
6 6
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchDisposableIncomeVo;
7 7
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFamilyRanchVo;
8
+import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFarmerHouseholdVo;
8 9
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchLivestockProductOutputVo;
9 10
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchPastureVo;
10 11
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchYakAssetVo;
@@ -76,4 +77,13 @@ public interface IResearchOpenApiService
76 77
      * @param statYear 所属年份精确匹配
77 78
      */
78 79
     OpenApiPageResult<ResearchDisposableIncomeVo> listDisposableIncomes(int pageNum, int pageSize, Integer statYear);
80
+
81
+    /**
82
+     * 农牧户填报列表。
83
+     *
84
+     * @param townName 所属乡镇名称模糊匹配
85
+     * @param villageName 所属村名称模糊匹配
86
+     */
87
+    OpenApiPageResult<ResearchFarmerHouseholdVo> listFarmerHouseholds(int pageNum, int pageSize,
88
+            Integer statYear, Integer statMonth, String townName, String villageName);
79 89
 }

+ 33 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/research/service/impl/ResearchOpenApiServiceImpl.java

@@ -11,6 +11,7 @@ import com.ruoyi.common.utils.StringUtils;
11 11
 import com.ruoyi.web.modules.industryservice.domain.BizCooperativeDevelopment;
12 12
 import com.ruoyi.web.modules.industryservice.domain.BizDisposableIncome;
13 13
 import com.ruoyi.web.modules.industryservice.domain.BizFamilyRanch;
14
+import com.ruoyi.web.modules.industryservice.domain.BizFarmerHousehold;
14 15
 import com.ruoyi.web.modules.industryservice.domain.BizLivestockProductOutput;
15 16
 import com.ruoyi.web.modules.industryservice.domain.BizPasture;
16 17
 import com.ruoyi.web.modules.industryservice.domain.BizYakAsset;
@@ -20,6 +21,7 @@ import com.ruoyi.web.modules.industryservice.domain.dto.OpenApiPageResult;
20 21
 import com.ruoyi.web.modules.industryservice.mapper.BizCooperativeDevelopmentMapper;
21 22
 import com.ruoyi.web.modules.industryservice.mapper.BizDisposableIncomeMapper;
22 23
 import com.ruoyi.web.modules.industryservice.mapper.BizFamilyRanchMapper;
24
+import com.ruoyi.web.modules.industryservice.mapper.BizFarmerHouseholdMapper;
23 25
 import com.ruoyi.web.modules.industryservice.mapper.BizLivestockProductOutputMapper;
24 26
 import com.ruoyi.web.modules.industryservice.mapper.BizYakHerdInventoryMapper;
25 27
 import com.ruoyi.web.modules.industryservice.mapper.BizYakOutboundReportMapper;
@@ -28,6 +30,7 @@ import com.ruoyi.web.modules.industryservice.research.domain.dto.ResearchTokenVo
28 30
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchCooperativeDevelopmentVo;
29 31
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchDisposableIncomeVo;
30 32
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFamilyRanchVo;
33
+import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFarmerHouseholdVo;
31 34
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchLivestockProductOutputVo;
32 35
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchPastureVo;
33 36
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchYakAssetVo;
@@ -72,6 +75,9 @@ public class ResearchOpenApiServiceImpl implements IResearchOpenApiService
72 75
     @Autowired
73 76
     private BizDisposableIncomeMapper bizDisposableIncomeMapper;
74 77
 
78
+    @Autowired
79
+    private BizFarmerHouseholdMapper bizFarmerHouseholdMapper;
80
+
75 81
     @Override
76 82
     public ResearchTokenVo authenticate(String appKey, String appSecret)
77 83
     {
@@ -272,6 +278,33 @@ public class ResearchOpenApiServiceImpl implements IResearchOpenApiService
272 278
                 rows.stream().map(ResearchOpenApiSupport::toDisposableIncomeVo).collect(Collectors.toList()));
273 279
     }
274 280
 
281
+    @Override
282
+    public OpenApiPageResult<ResearchFarmerHouseholdVo> listFarmerHouseholds(int pageNum, int pageSize,
283
+            Integer statYear, Integer statMonth, String townName, String villageName)
284
+    {
285
+        tokenService.assertEnabled();
286
+        int safePageNum = normalizePageNum(pageNum);
287
+        int safePageSize = normalizePageSize(pageSize);
288
+        BizFarmerHousehold query = new BizFarmerHousehold();
289
+        query.setStatYear(statYear);
290
+        query.setStatMonth(statMonth);
291
+        if (StringUtils.isNotEmpty(townName))
292
+        {
293
+            query.setTownName(townName.trim());
294
+        }
295
+        if (StringUtils.isNotEmpty(villageName))
296
+        {
297
+            query.setVillageName(villageName.trim());
298
+        }
299
+        // 开放接口无登录上下文,清空 dataScope,避免走填报部门权限
300
+        query.getParams().put("dataScope", "");
301
+        PageHelper.startPage(safePageNum, safePageSize);
302
+        List<BizFarmerHousehold> rows = bizFarmerHouseholdMapper.selectBizFarmerHouseholdList(query);
303
+        PageInfo<BizFarmerHousehold> page = new PageInfo<>(rows);
304
+        return toPageResult(safePageNum, safePageSize, page.getTotal(),
305
+                rows.stream().map(ResearchOpenApiSupport::toFarmerHouseholdVo).collect(Collectors.toList()));
306
+    }
307
+
275 308
     private int normalizePageNum(int pageNum)
276 309
     {
277 310
         return pageNum > 0 ? pageNum : 1;

+ 36 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/research/support/ResearchOpenApiSupport.java

@@ -5,6 +5,7 @@ import java.util.UUID;
5 5
 import com.ruoyi.web.modules.industryservice.domain.BizCooperativeDevelopment;
6 6
 import com.ruoyi.web.modules.industryservice.domain.BizDisposableIncome;
7 7
 import com.ruoyi.web.modules.industryservice.domain.BizFamilyRanch;
8
+import com.ruoyi.web.modules.industryservice.domain.BizFarmerHousehold;
8 9
 import com.ruoyi.web.modules.industryservice.domain.BizLivestockProductOutput;
9 10
 import com.ruoyi.web.modules.industryservice.domain.BizPasture;
10 11
 import com.ruoyi.web.modules.industryservice.domain.BizYakAsset;
@@ -14,6 +15,7 @@ import com.ruoyi.web.modules.industryservice.domain.dto.OpenApiResponse;
14 15
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchCooperativeDevelopmentVo;
15 16
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchDisposableIncomeVo;
16 17
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFamilyRanchVo;
18
+import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFarmerHouseholdVo;
17 19
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchLivestockProductOutputVo;
18 20
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchPastureVo;
19 21
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchYakAssetVo;
@@ -290,4 +292,38 @@ public final class ResearchOpenApiSupport
290 292
         vo.setIncomeGrowthRate(row.getIncomeGrowthRate());
291 293
         return vo;
292 294
     }
295
+
296
+    public static ResearchFarmerHouseholdVo toFarmerHouseholdVo(BizFarmerHousehold row)
297
+    {
298
+        if (row == null)
299
+        {
300
+            return null;
301
+        }
302
+        ResearchFarmerHouseholdVo vo = new ResearchFarmerHouseholdVo();
303
+        vo.setHouseholdId(row.getId());
304
+        vo.setStatYear(row.getStatYear());
305
+        vo.setStatMonth(row.getStatMonth());
306
+        vo.setTownName(row.getTownName());
307
+        vo.setVillageName(row.getVillageName());
308
+        vo.setHouseholdCount(row.getHouseholdCount());
309
+        vo.setPopulationCount(row.getPopulationCount());
310
+        vo.setPastureContractArea(row.getPastureContractArea());
311
+        vo.setGrazingBanArea(row.getGrazingBanArea());
312
+        vo.setGrassBalanceArea(row.getGrassBalanceArea());
313
+        vo.setApprovedCarryingCapacity(row.getApprovedCarryingCapacity());
314
+        vo.setYearEndStockSheepUnit(row.getYearEndStockSheepUnit());
315
+        vo.setIsPureHerder(row.getIsPureHerder());
316
+        vo.setIsNoLivestock(row.getIsNoLivestock());
317
+        vo.setIsExtinctHousehold(row.getIsExtinctHousehold());
318
+        vo.setIsOverloadHerder(row.getIsOverloadHerder());
319
+        vo.setNeedCap(row.getNeedCap());
320
+        vo.setNeedFloor(row.getNeedFloor());
321
+        vo.setGrazingBanSubsidy(row.getGrazingBanSubsidy());
322
+        vo.setGrassBalanceReward(row.getGrassBalanceReward());
323
+        vo.setSubsidyRewardSubtotal(row.getSubsidyRewardSubtotal());
324
+        vo.setAfterCapFloorSubsidy(row.getAfterCapFloorSubsidy());
325
+        vo.setVillageSupervisorSubsidy(row.getVillageSupervisorSubsidy());
326
+        vo.setSubsidyTotal(row.getSubsidyTotal());
327
+        return vo;
328
+    }
293 329
 }

+ 6 - 0
baqing-admin/src/main/resources/mapper/industryservice/BizFarmerHouseholdMapper.xml

@@ -91,6 +91,12 @@
91 91
             <if test="villageDeptId != null">
92 92
                 and r.village_dept_id = #{villageDeptId}
93 93
             </if>
94
+            <if test="townName != null and townName != ''">
95
+                and r.town_name like concat('%', #{townName}, '%')
96
+            </if>
97
+            <if test="villageName != null and villageName != ''">
98
+                and r.village_name like concat('%', #{villageName}, '%')
99
+            </if>
94 100
             ${params.dataScope}
95 101
         </where>
96 102
         order by r.stat_year desc, r.stat_month desc, d.order_num asc, r.village_dept_id asc, r.id desc

+ 30 - 0
baqing-admin/src/test/java/com/ruoyi/web/modules/industryservice/research/controller/ResearchOpenApiControllerApiTest.java

@@ -24,6 +24,7 @@ import com.ruoyi.web.modules.industryservice.research.domain.dto.ResearchTokenVo
24 24
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchCooperativeDevelopmentVo;
25 25
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchDisposableIncomeVo;
26 26
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFamilyRanchVo;
27
+import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchFarmerHouseholdVo;
27 28
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchLivestockProductOutputVo;
28 29
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchPastureVo;
29 30
 import com.ruoyi.web.modules.industryservice.research.domain.vo.ResearchYakHerdInventoryVo;
@@ -253,4 +254,33 @@ class ResearchOpenApiControllerApiTest
253 254
                 .andExpect(jsonPath("$.code").value(200))
254 255
                 .andExpect(jsonPath("$.data.records[0].statYear").value(2025));
255 256
     }
257
+
258
+    @Test
259
+    @DisplayName("GET /farmer-households 成功")
260
+    void listFarmerHouseholds() throws Exception
261
+    {
262
+        ResearchFarmerHouseholdVo row = new ResearchFarmerHouseholdVo();
263
+        row.setHouseholdId(1L);
264
+        row.setStatYear(2026);
265
+        row.setStatMonth(5);
266
+        row.setTownName("雅安镇");
267
+        row.setVillageName("荣嘎村");
268
+        OpenApiPageResult<ResearchFarmerHouseholdVo> page = new OpenApiPageResult<>();
269
+        page.setPageNum(1);
270
+        page.setPageSize(20);
271
+        page.setTotal(1L);
272
+        page.setRecords(Collections.singletonList(row));
273
+        when(researchOpenApiService.listFarmerHouseholds(anyInt(), anyInt(), any(), any(), any(), any()))
274
+                .thenReturn(page);
275
+
276
+        mockMvc.perform(get("/open-api/v1/research/farmer-households")
277
+                .param("statYear", "2026")
278
+                .param("statMonth", "5")
279
+                .param("townName", "雅安")
280
+                .param("villageName", "荣嘎"))
281
+                .andExpect(status().isOk())
282
+                .andExpect(jsonPath("$.code").value(200))
283
+                .andExpect(jsonPath("$.data.records[0].townName").value("雅安镇"))
284
+                .andExpect(jsonPath("$.data.records[0].villageName").value("荣嘎村"));
285
+    }
256 286
 }

+ 17 - 0
doc/产业数据模型及服务/科研开放数据接口/科研开放数据接口功能需求.md

@@ -34,6 +34,7 @@
34 34
 | 畜牧产品产量登记列表 | 分页返回村级肉/奶产量 |
35 35
 | 家庭牧场数据列表 | 分页返回家庭牧场填报;法人姓名脱敏 |
36 36
 | 可支配收入数据列表 | 分页返回县级年度可支配收入填报 |
37
+| 农牧户数据列表 | 分页返回村级农牧户填报 |
37 38
 
38 39
 **本期不实现:**
39 40
 
@@ -204,6 +205,20 @@ sequenceDiagram
204 205
 
205 206
 **不返回**:创建人、备注等内部字段。
206 207
 
208
+### 6.10 农牧户数据列表
209
+
210
+| 项 | 说明 |
211
+| --- | --- |
212
+| 方法 | GET |
213
+| 路径 | `/farmer-households` |
214
+| 鉴权 | Bearer Token |
215
+| 分页 | 同 §6.2 |
216
+| 筛选 | `statYear`、`statMonth`、`townName`(乡镇模糊)、`villageName`(村模糊) |
217
+
218
+**返回字段**:`householdId`、年月、乡镇村、户数人数、草场面积与载畜量、户类型标识、补助奖励等。
219
+
220
+**不返回**:部门 ID、创建人、备注等内部字段。
221
+
207 222
 ---
208 223
 
209 224
 ## 7. 脱敏规则摘要
@@ -247,6 +262,7 @@ sequenceDiagram
247 262
 | 畜牧产品产量登记 | 数据源 `biz_livestock_product_output` |
248 263
 | 家庭牧场数据填报 | 数据源 `biz_family_ranch` |
249 264
 | 可支配收入数据填报 | 数据源 `biz_disposable_income` |
265
+| 农牧户数据填报 | 数据源 `biz_farmer_household` |
250 266
 | 若依权限体系 | **无**关联;开放接口走独立 Token |
251 267
 
252 268
 ---
@@ -264,5 +280,6 @@ sequenceDiagram
264 280
 | 畜牧产品产量列表 | §6.7 |
265 281
 | 家庭牧场列表 | §6.8 |
266 282
 | 可支配收入列表 | §6.9 |
283
+| 农牧户列表 | §6.10 |
267 284
 | 脱敏合规 | §7 |
268 285
 | 本期不实现 | §3 |

+ 22 - 2
doc/产业数据模型及服务/科研开放数据接口/科研开放数据接口技术方案.md

@@ -9,7 +9,7 @@
9 9
 | 项 | 说明 |
10 10
 | --- | --- |
11 11
 | **后端** | RuoYi **v3.9.2**(**springboot2** 分支):JDK 8、Spring MVC、MyBatis |
12
-| **数据库** | 只读查询既有表 `biz_pasture`、`biz_yak_asset`、`biz_yak_herd_inventory`、`biz_yak_outbound_report`、`biz_cooperative_development`、`biz_livestock_product_output`、`biz_family_ranch`、`biz_disposable_income` |
12
+| **数据库** | 只读查询既有表 `biz_pasture`、`biz_yak_asset`、`biz_yak_herd_inventory`、`biz_yak_outbound_report`、`biz_cooperative_development`、`biz_livestock_product_output`、`biz_family_ranch`、`biz_disposable_income`、`biz_farmer_household` |
13 13
 | **鉴权** | 独立 JWT + Servlet Filter;`@Anonymous` 绕过若依登录,Filter 校验 Bearer Token |
14 14
 | **响应** | `OpenApiResponse<T>` + `OpenApiPageResult<T>`(与第三方 farming OpenAPI 同结构) |
15 15
 
@@ -208,6 +208,19 @@ Authorization: Bearer <accessToken>
208 208
 
209 209
 实现说明:直接查 `BizDisposableIncomeMapper.selectBizDisposableIncomeList`。
210 210
 
211
+### 3.10 农牧户数据列表
212
+
213
+```
214
+GET /open-api/v1/research/farmer-households?pageNum=1&pageSize=20&statYear=2026&statMonth=5&townName=&villageName=
215
+Authorization: Bearer <accessToken>
216
+```
217
+
218
+**查询参数:** `statYear` / `statMonth`(精确);`townName` / `villageName`(模糊);分页同前。
219
+
220
+**`records[]` 元素(ResearchFarmerHouseholdVo):** 农牧户业务字段;**不含**部门 ID、操作人、备注。
221
+
222
+实现说明:直接查 `BizFarmerHouseholdMapper`(清空 `params.dataScope`)。
223
+
211 224
 ---
212 225
 
213 226
 ## 4. 鉴权流程
@@ -235,8 +248,9 @@ Token 申请:`appKey`/`appSecret` 与配置项精确匹配后签发 JWT(HS51
235 248
 | 产量列表 | `BizLivestockProductOutputMapper.selectBizLivestockProductOutputList` | `biz_livestock_product_output` | 年/月精确;乡镇/村名模糊;清空 `params.dataScope` |
236 249
 | 家庭牧场列表 | `BizFamilyRanchMapper.selectBizFamilyRanchList` | `biz_family_ranch` | 年/月精确;乡镇/村/企业名模糊;清空 `params.dataScope` |
237 250
 | 可支配收入列表 | `BizDisposableIncomeMapper.selectBizDisposableIncomeList` | `biz_disposable_income` | 年份精确 |
251
+| 农牧户列表 | `BizFarmerHouseholdMapper.selectBizFarmerHouseholdList` | `biz_farmer_household` | 年/月精确;乡镇/村名模糊;清空 `params.dataScope` |
238 252
 
239
-实体 → VO:`ResearchOpenApiSupport.toPastureVo` / `toYakAssetVo` / `toYakHerdInventoryVo` / `toYakOutboundReportVo` / `toCooperativeDevelopmentVo` / `toLivestockProductOutputVo` / `toFamilyRanchVo` / `toDisposableIncomeVo`。
253
+实体 → VO:`ResearchOpenApiSupport.toPastureVo` / `toYakAssetVo` / `toYakHerdInventoryVo` / `toYakOutboundReportVo` / `toCooperativeDevelopmentVo` / `toLivestockProductOutputVo` / `toFamilyRanchVo` / `toDisposableIncomeVo` / `toFarmerHouseholdVo`
240 254
 
241 255
 ---
242 256
 
@@ -305,6 +319,10 @@ curl "http://localhost:8010/open-api/v1/research/family-ranches?pageNum=1&pageSi
305 319
 # 9. 可支配收入
306 320
 curl "http://localhost:8010/open-api/v1/research/disposable-incomes?pageNum=1&pageSize=10&statYear=2025" \
307 321
   -H "Authorization: Bearer <accessToken>"
322
+
323
+# 10. 农牧户
324
+curl "http://localhost:8010/open-api/v1/research/farmer-households?pageNum=1&pageSize=10&statYear=2026&townName=雅安" \
325
+  -H "Authorization: Bearer <accessToken>"
308 326
 ```
309 327
 
310 328
 ---
@@ -332,6 +350,7 @@ curl "http://localhost:8010/open-api/v1/research/disposable-incomes?pageNum=1&pa
332 350
 | §6.7 产量 | **§3.7**、**§5** BizLivestockProductOutput |
333 351
 | §6.8 家庭牧场 | **§3.8**、**§5** BizFamilyRanch |
334 352
 | §6.9 可支配收入 | **§3.9**、**§5** BizDisposableIncome |
353
+| §6.10 农牧户 | **§3.10**、**§5** BizFarmerHousehold |
335 354
 | §7 脱敏 | **§6** Masking |
336 355
 | §3 不实现机构区分 | 单一 app-key/secret,JWT 无 clientId |
337 356
 
@@ -341,6 +360,7 @@ curl "http://localhost:8010/open-api/v1/research/disposable-incomes?pageNum=1&pa
341 360
 
342 361
 | 版本 | 日期 | 说明 |
343 362
 | --- | --- | --- |
363
+| 1.7 | 2026-07-22 | 新增农牧户开放列表(年/月/乡镇村模糊+分页) |
344 364
 | 1.6 | 2026-07-22 | 新增可支配收入开放列表(年份精确+分页) |
345 365
 | 1.5 | 2026-07-22 | 新增家庭牧场开放列表(年/月/乡镇村/企业名模糊+分页;法人脱敏) |
346 366
 | 1.4 | 2026-07-22 | 新增畜牧产品产量登记开放列表(年/月/乡镇村模糊+分页) |

+ 5 - 1
doc/产业数据模型及服务/科研开放数据接口/科研开放数据接口测试用例.md

@@ -55,6 +55,8 @@
55 55
 | KYKF-API-022 | 家庭牧场 | 年/月/乡镇村/企业名筛选 | 接口测试 | curl | §6.8 | 已知企业名片段 | GET 带筛选参数 | 条件生效;名称模糊匹配 |
56 56
 | KYKF-API-023 | 可支配收入 | 分页列表 | 接口测试 | curl | §6.9 | 有效 Token;库有收入数据 | GET `/disposable-incomes?pageNum=1&pageSize=10` | `code=200`;无操作人/备注 |
57 57
 | KYKF-API-024 | 可支配收入 | 年份筛选 | 接口测试 | curl | §6.9 | 已知年份 | GET 带 `statYear` | 仅返回该年记录 |
58
+| KYKF-API-025 | 农牧户 | 分页列表 | 接口测试 | curl | §6.10 | 有效 Token;库有农牧户数据 | GET `/farmer-households?pageNum=1&pageSize=10` | `code=200`;无部门 ID |
59
+| KYKF-API-026 | 农牧户 | 年/月/乡镇村筛选 | 接口测试 | curl | §6.10 | 已知年月与乡镇名片段 | GET 带筛选参数 | 条件生效;名称模糊匹配 |
58 60
 
59 61
 ---
60 62
 
@@ -71,11 +73,12 @@
71 73
 | §6.7 产量列表 | — | API-019~020 |
72 74
 | §6.8 家庭牧场列表 | — | API-021~022 |
73 75
 | §6.9 可支配收入列表 | — | API-023~024 |
76
+| §6.10 农牧户列表 | — | API-025~026 |
74 77
 | §7 脱敏 | UT-001~004 | API-012 |
75 78
 | §2 分页 | — | API-010 |
76 79
 | §3 无机构区分 | UT-005 | API-001(单一凭证) |
77 80
 
78
-**用例统计**:单元 **6**、接口 **24**,合计 **30** 条。
81
+**用例统计**:单元 **6**、接口 **26**,合计 **32** 条。
79 82
 
80 83
 ---
81 84
 
@@ -96,6 +99,7 @@ curl -s "$BASE/cooperative-developments?pageNum=1&pageSize=5&statYear=2026" -H "
96 99
 curl -s "$BASE/livestock-product-outputs?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
97 100
 curl -s "$BASE/family-ranches?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
98 101
 curl -s "$BASE/disposable-incomes?pageNum=1&pageSize=5&statYear=2025" -H "Authorization: Bearer $TOKEN"
102
+curl -s "$BASE/farmer-households?pageNum=1&pageSize=5&statYear=2026" -H "Authorization: Bearer $TOKEN"
99 103
 ```
100 104
 
101 105
 (Windows 环境可用 PowerShell `Invoke-RestMethod`,见技术方案 **§8**。)