Explorar el Código

根据数据权限过滤

wwh hace 1 semana
padre
commit
b64b6adc8f

+ 33 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/diagnosis/service/impl/BizMedicalResourceServiceImpl.java

@@ -60,6 +60,7 @@ public class BizMedicalResourceServiceImpl implements IBizMedicalResourceService
60
     public List<BizMedicalResource> selectBizMedicalResourceList(BizMedicalResource query)
60
     public List<BizMedicalResource> selectBizMedicalResourceList(BizMedicalResource query)
61
     {
61
     {
62
         // 列表入参校验与类型校验须在 Controller 中、startPage() 之前完成(避免 PageHelper 分页错位)
62
         // 列表入参校验与类型校验须在 Controller 中、startPage() 之前完成(避免 PageHelper 分页错位)
63
+        applyTownDataScope(query);
63
         List<BizMedicalResource> list = bizMedicalResourceMapper.selectBizMedicalResourceList(query);
64
         List<BizMedicalResource> list = bizMedicalResourceMapper.selectBizMedicalResourceList(query);
64
         if (list != null)
65
         if (list != null)
65
         {
66
         {
@@ -284,6 +285,38 @@ public class BizMedicalResourceServiceImpl implements IBizMedicalResourceService
284
         return row;
285
         return row;
285
     }
286
     }
286
 
287
 
288
+    /**
289
+     * 乡镇/村登录人:兽医人员库仅看本镇数据(未填所属乡镇的兽医记录对乡镇账号不可见)。
290
+     * 非兽医类型不按乡镇收窄。
291
+     */
292
+    private void applyTownDataScope(BizMedicalResource query)
293
+    {
294
+        if (query == null)
295
+        {
296
+            return;
297
+        }
298
+        query.getParams().put("dataScope", "");
299
+        Long scopedTownId = townDeptSupport.resolveScopedTownDeptId();
300
+        if (scopedTownId == null)
301
+        {
302
+            return;
303
+        }
304
+        String type = query.getResourceType();
305
+        if (StringUtils.isNotEmpty(type) && !MedicalResourceRules.RESOURCE_TYPE_VET.equals(type))
306
+        {
307
+            return;
308
+        }
309
+        if (MedicalResourceRules.RESOURCE_TYPE_VET.equals(type))
310
+        {
311
+            query.getParams().put("dataScope", " AND town_dept_id = " + scopedTownId + " ");
312
+            return;
313
+        }
314
+        // 未传类型时:兽医按镇过滤,其余类型仍可见
315
+        query.getParams().put("dataScope",
316
+                " AND (resource_type <> '" + MedicalResourceRules.RESOURCE_TYPE_VET
317
+                        + "' OR town_dept_id = " + scopedTownId + ") ");
318
+    }
319
+
287
     /**
320
     /**
288
      * 兽医人员可选所属乡镇:校验部门并冗余乡镇名;非兽医清空。
321
      * 兽医人员可选所属乡镇:校验部门并冗余乡镇名;非兽医清空。
289
      */
322
      */

+ 178 - 34
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/TownDeptSupport.java

@@ -9,11 +9,20 @@ import org.springframework.beans.factory.annotation.Autowired;
9
 import org.springframework.stereotype.Component;
9
 import org.springframework.stereotype.Component;
10
 import com.ruoyi.common.core.domain.TreeSelect;
10
 import com.ruoyi.common.core.domain.TreeSelect;
11
 import com.ruoyi.common.core.domain.entity.SysDept;
11
 import com.ruoyi.common.core.domain.entity.SysDept;
12
+import com.ruoyi.common.core.domain.model.LoginUser;
13
+import com.ruoyi.common.utils.SecurityUtils;
12
 import com.ruoyi.common.utils.StringUtils;
14
 import com.ruoyi.common.utils.StringUtils;
13
 import com.ruoyi.system.service.ISysDeptService;
15
 import com.ruoyi.system.service.ISysDeptService;
14
 
16
 
15
 /**
17
 /**
16
- * 填报乡镇 / 村字典(县 → 乡镇 → 村;乡镇为县级下 order_num &gt; 12 节点)
18
+ * 填报乡镇 / 村字典(县 → 乡镇 → 村;乡镇为县级下 order_num &gt; 12 节点)。
19
+ * <p>
20
+ * 下拉范围:
21
+ * <ul>
22
+ *   <li>乡镇人员:仅本镇 + 本镇下各村</li>
23
+ *   <li>村级人员:仅上级乡镇 + 本村(不依赖 selectDeptList 的 @DataScope,避免查不到上级镇)</li>
24
+ *   <li>其余(含超管):按部门数据权限列出填报乡镇/村</li>
25
+ * </ul>
17
  */
26
  */
18
 @Component
27
 @Component
19
 public class TownDeptSupport
28
 public class TownDeptSupport
@@ -23,30 +32,17 @@ public class TownDeptSupport
23
 
32
 
24
     public List<SysDept> listReportTowns()
33
     public List<SysDept> listReportTowns()
25
     {
34
     {
26
-        SysDept query = new SysDept();
27
-        query.setParentId(YakHerdInventoryRules.TOWN_PARENT_ID);
28
-        query.setStatus("0");
29
-        List<SysDept> depts = deptService.selectDeptList(query);
30
-        List<SysDept> towns = new ArrayList<>();
31
-        if (depts == null)
35
+        ReportOrgScope scope = resolveReportOrgScope();
36
+        if (scope.townDeptId != null)
32
         {
37
         {
33
-            return towns;
34
-        }
35
-        for (SysDept dept : depts)
36
-        {
37
-            if (dept == null || !"0".equals(dept.getDelFlag()))
38
-            {
39
-                continue;
40
-            }
41
-            if (dept.getOrderNum() != null && dept.getOrderNum() > YakHerdInventoryRules.TOWN_MIN_ORDER_NUM)
38
+            SysDept town = deptService.selectDeptById(scope.townDeptId);
39
+            if (isActiveReportTown(town))
42
             {
40
             {
43
-                towns.add(dept);
41
+                return Collections.singletonList(town);
44
             }
42
             }
43
+            return Collections.emptyList();
45
         }
44
         }
46
-        towns.sort((a, b) -> Integer.compare(
47
-                a.getOrderNum() == null ? 0 : a.getOrderNum(),
48
-                b.getOrderNum() == null ? 0 : b.getOrderNum()));
49
-        return towns;
45
+        return queryReportTownsByDataScope();
50
     }
46
     }
51
 
47
 
52
     public List<SysDept> listVillagesUnderTown(Long townDeptId)
48
     public List<SysDept> listVillagesUnderTown(Long townDeptId)
@@ -56,25 +52,21 @@ public class TownDeptSupport
56
         {
52
         {
57
             return villages;
53
             return villages;
58
         }
54
         }
59
-        SysDept query = new SysDept();
60
-        query.setParentId(townDeptId);
61
-        query.setStatus("0");
62
-        List<SysDept> depts = deptService.selectDeptList(query);
63
-        if (depts == null)
55
+        ReportOrgScope scope = resolveReportOrgScope();
56
+        if (scope.townDeptId != null && !scope.townDeptId.equals(townDeptId))
64
         {
57
         {
65
             return villages;
58
             return villages;
66
         }
59
         }
67
-        for (SysDept dept : depts)
60
+        if (scope.villageDeptId != null)
68
         {
61
         {
69
-            if (dept != null && "0".equals(dept.getDelFlag()))
62
+            SysDept village = deptService.selectDeptById(scope.villageDeptId);
63
+            if (isActiveDept(village) && townDeptId.equals(village.getParentId()))
70
             {
64
             {
71
-                villages.add(dept);
65
+                villages.add(village);
72
             }
66
             }
67
+            return villages;
73
         }
68
         }
74
-        villages.sort((a, b) -> Integer.compare(
75
-                a.getOrderNum() == null ? 0 : a.getOrderNum(),
76
-                b.getOrderNum() == null ? 0 : b.getOrderNum()));
77
-        return villages;
69
+        return queryVillagesByParent(townDeptId);
78
     }
70
     }
79
 
71
 
80
     /**
72
     /**
@@ -152,7 +144,7 @@ public class TownDeptSupport
152
             return null;
144
             return null;
153
         }
145
         }
154
         SysDept village = deptService.selectDeptById(villageDeptId);
146
         SysDept village = deptService.selectDeptById(villageDeptId);
155
-        if (village == null || !"0".equals(village.getDelFlag()) || !"0".equals(village.getStatus()))
147
+        if (!isActiveDept(village))
156
         {
148
         {
157
             return null;
149
             return null;
158
         }
150
         }
@@ -237,4 +229,156 @@ public class TownDeptSupport
237
         }
229
         }
238
         return Collections.unmodifiableList(options);
230
         return Collections.unmodifiableList(options);
239
     }
231
     }
232
+
233
+    /**
234
+     * 当前登录人锁定的填报乡镇 id(乡镇/村人员);非此类人员返回 null(不限制)。
235
+     */
236
+    public Long resolveScopedTownDeptId()
237
+    {
238
+        return resolveReportOrgScope().townDeptId;
239
+    }
240
+
241
+    /**
242
+     * 当前登录人是否落在填报乡镇/村:返回应锁定的乡镇、村;否则不限制(走 @DataScope 列表)。
243
+     */
244
+    ReportOrgScope resolveReportOrgScope()
245
+    {
246
+        LoginUser loginUser = tryGetLoginUser();
247
+        if (loginUser == null || SecurityUtils.isAdmin(loginUser.getUserId()))
248
+        {
249
+            return ReportOrgScope.unrestricted();
250
+        }
251
+        Long deptId = loginUser.getDeptId();
252
+        if (deptId == null && loginUser.getUser() != null)
253
+        {
254
+            deptId = loginUser.getUser().getDeptId();
255
+        }
256
+        if (deptId == null)
257
+        {
258
+            return ReportOrgScope.unrestricted();
259
+        }
260
+        SysDept self = deptService.selectDeptById(deptId);
261
+        if (!isActiveDept(self))
262
+        {
263
+            return ReportOrgScope.unrestricted();
264
+        }
265
+        if (isActiveReportTown(self))
266
+        {
267
+            return ReportOrgScope.townOnly(self.getDeptId());
268
+        }
269
+        if (self.getParentId() != null)
270
+        {
271
+            SysDept parent = deptService.selectDeptById(self.getParentId());
272
+            if (isActiveReportTown(parent))
273
+            {
274
+                return ReportOrgScope.villageOnly(parent.getDeptId(), self.getDeptId());
275
+            }
276
+        }
277
+        return ReportOrgScope.unrestricted();
278
+    }
279
+
280
+    private List<SysDept> queryReportTownsByDataScope()
281
+    {
282
+        SysDept query = new SysDept();
283
+        query.setParentId(YakHerdInventoryRules.TOWN_PARENT_ID);
284
+        query.setStatus("0");
285
+        List<SysDept> depts = deptService.selectDeptList(query);
286
+        List<SysDept> towns = new ArrayList<>();
287
+        if (depts == null)
288
+        {
289
+            return towns;
290
+        }
291
+        for (SysDept dept : depts)
292
+        {
293
+            if (isActiveReportTown(dept))
294
+            {
295
+                towns.add(dept);
296
+            }
297
+        }
298
+        towns.sort((a, b) -> Integer.compare(
299
+                a.getOrderNum() == null ? 0 : a.getOrderNum(),
300
+                b.getOrderNum() == null ? 0 : b.getOrderNum()));
301
+        return towns;
302
+    }
303
+
304
+    private List<SysDept> queryVillagesByParent(Long townDeptId)
305
+    {
306
+        List<SysDept> villages = new ArrayList<>();
307
+        SysDept query = new SysDept();
308
+        query.setParentId(townDeptId);
309
+        query.setStatus("0");
310
+        List<SysDept> depts = deptService.selectDeptList(query);
311
+        if (depts == null)
312
+        {
313
+            return villages;
314
+        }
315
+        for (SysDept dept : depts)
316
+        {
317
+            if (isActiveDept(dept))
318
+            {
319
+                villages.add(dept);
320
+            }
321
+        }
322
+        villages.sort((a, b) -> Integer.compare(
323
+                a.getOrderNum() == null ? 0 : a.getOrderNum(),
324
+                b.getOrderNum() == null ? 0 : b.getOrderNum()));
325
+        return villages;
326
+    }
327
+
328
+    static boolean isActiveReportTown(SysDept dept)
329
+    {
330
+        if (!isActiveDept(dept))
331
+        {
332
+            return false;
333
+        }
334
+        if (!Long.valueOf(YakHerdInventoryRules.TOWN_PARENT_ID).equals(dept.getParentId()))
335
+        {
336
+            return false;
337
+        }
338
+        return dept.getOrderNum() != null && dept.getOrderNum() > YakHerdInventoryRules.TOWN_MIN_ORDER_NUM;
339
+    }
340
+
341
+    static boolean isActiveDept(SysDept dept)
342
+    {
343
+        return dept != null && "0".equals(dept.getDelFlag()) && "0".equals(dept.getStatus());
344
+    }
345
+
346
+    private static LoginUser tryGetLoginUser()
347
+    {
348
+        try
349
+        {
350
+            return SecurityUtils.getLoginUser();
351
+        }
352
+        catch (Exception e)
353
+        {
354
+            return null;
355
+        }
356
+    }
357
+
358
+    static final class ReportOrgScope
359
+    {
360
+        final Long townDeptId;
361
+        final Long villageDeptId;
362
+
363
+        private ReportOrgScope(Long townDeptId, Long villageDeptId)
364
+        {
365
+            this.townDeptId = townDeptId;
366
+            this.villageDeptId = villageDeptId;
367
+        }
368
+
369
+        static ReportOrgScope unrestricted()
370
+        {
371
+            return new ReportOrgScope(null, null);
372
+        }
373
+
374
+        static ReportOrgScope townOnly(Long townDeptId)
375
+        {
376
+            return new ReportOrgScope(townDeptId, null);
377
+        }
378
+
379
+        static ReportOrgScope villageOnly(Long townDeptId, Long villageDeptId)
380
+        {
381
+            return new ReportOrgScope(townDeptId, villageDeptId);
382
+        }
383
+    }
240
 }
384
 }

+ 1 - 0
baqing-admin/src/main/resources/mapper/diagnosis/BizMedicalResourceMapper.xml

@@ -112,6 +112,7 @@
112
             <if test="queryPublishStatus != null and queryPublishStatus == 2">
112
             <if test="queryPublishStatus != null and queryPublishStatus == 2">
113
                 and publish_status = 0 and offline_time is not null
113
                 and publish_status = 0 and offline_time is not null
114
             </if>
114
             </if>
115
+            ${params.dataScope}
115
         </where>
116
         </where>
116
         order by create_time desc, id desc
117
         order by create_time desc, id desc
117
     </select>
118
     </select>

+ 27 - 0
baqing-admin/src/test/java/com/ruoyi/web/modules/diagnosis/service/impl/BizMedicalResourceServiceImplTest.java

@@ -11,6 +11,7 @@ import static org.mockito.Mockito.times;
11
 import static org.mockito.Mockito.verify;
11
 import static org.mockito.Mockito.verify;
12
 import static org.mockito.Mockito.when;
12
 import static org.mockito.Mockito.when;
13
 
13
 
14
+import java.util.Collections;
14
 import com.ruoyi.common.core.domain.entity.SysUser;
15
 import com.ruoyi.common.core.domain.entity.SysUser;
15
 import com.ruoyi.common.exception.ServiceException;
16
 import com.ruoyi.common.exception.ServiceException;
16
 import com.ruoyi.web.modules.diagnosis.MedicalResourceTestSamples;
17
 import com.ruoyi.web.modules.diagnosis.MedicalResourceTestSamples;
@@ -250,4 +251,30 @@ class BizMedicalResourceServiceImplTest
250
         verify(accountAssigner, never()).createAccount(any(), any());
251
         verify(accountAssigner, never()).createAccount(any(), any());
251
         verify(mapper, never()).updateBizMedicalResource(any());
252
         verify(mapper, never()).updateBizMedicalResource(any());
252
     }
253
     }
254
+
255
+    @Test
256
+    @DisplayName("乡镇人员列表仅本镇兽医")
257
+    void listAppliesTownScopeForVet()
258
+    {
259
+        when(townDeptSupport.resolveScopedTownDeptId()).thenReturn(200L);
260
+        when(mapper.selectBizMedicalResourceList(any())).thenReturn(Collections.emptyList());
261
+        BizMedicalResource query = new BizMedicalResource();
262
+        query.setResourceType(MedicalResourceRules.RESOURCE_TYPE_VET);
263
+        service.selectBizMedicalResourceList(query);
264
+        verify(mapper).selectBizMedicalResourceList(argThat(q ->
265
+                String.valueOf(q.getParams().get("dataScope")).contains("town_dept_id = 200")));
266
+    }
267
+
268
+    @Test
269
+    @DisplayName("非兽医类型不按乡镇收窄")
270
+    void listSkipsTownScopeForNonVet()
271
+    {
272
+        when(townDeptSupport.resolveScopedTownDeptId()).thenReturn(200L);
273
+        when(mapper.selectBizMedicalResourceList(any())).thenReturn(Collections.emptyList());
274
+        BizMedicalResource query = new BizMedicalResource();
275
+        query.setResourceType(MedicalResourceRules.RESOURCE_TYPE_ORG);
276
+        service.selectBizMedicalResourceList(query);
277
+        verify(mapper).selectBizMedicalResourceList(argThat(q ->
278
+                "".equals(q.getParams().get("dataScope"))));
279
+    }
253
 }
280
 }

+ 136 - 0
baqing-admin/src/test/java/com/ruoyi/web/modules/industryservice/support/TownDeptSupportScopeTest.java

@@ -0,0 +1,136 @@
1
+package com.ruoyi.web.modules.industryservice.support;
2
+
3
+import static org.junit.jupiter.api.Assertions.assertEquals;
4
+import static org.junit.jupiter.api.Assertions.assertTrue;
5
+import static org.mockito.ArgumentMatchers.any;
6
+import static org.mockito.Mockito.when;
7
+
8
+import java.util.Collections;
9
+import java.util.List;
10
+import org.junit.jupiter.api.AfterEach;
11
+import org.junit.jupiter.api.DisplayName;
12
+import org.junit.jupiter.api.Test;
13
+import org.junit.jupiter.api.extension.ExtendWith;
14
+import org.mockito.InjectMocks;
15
+import org.mockito.Mock;
16
+import org.mockito.junit.jupiter.MockitoExtension;
17
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
18
+import org.springframework.security.core.context.SecurityContextHolder;
19
+import com.ruoyi.common.core.domain.TreeSelect;
20
+import com.ruoyi.common.core.domain.entity.SysDept;
21
+import com.ruoyi.common.core.domain.entity.SysUser;
22
+import com.ruoyi.common.core.domain.model.LoginUser;
23
+import com.ruoyi.system.service.ISysDeptService;
24
+
25
+@ExtendWith(MockitoExtension.class)
26
+@DisplayName("TownDeptSupport 乡镇/村范围")
27
+class TownDeptSupportScopeTest
28
+{
29
+    @Mock
30
+    private ISysDeptService deptService;
31
+
32
+    @InjectMocks
33
+    private TownDeptSupport townDeptSupport;
34
+
35
+    @AfterEach
36
+    void clearSecurity()
37
+    {
38
+        SecurityContextHolder.clearContext();
39
+    }
40
+
41
+    @Test
42
+    @DisplayName("村级人员:乡镇下拉仅上级镇,村树仅本村")
43
+    void villageUserSeesParentTownAndOwnVillage()
44
+    {
45
+        SysDept town = town(200L, "雅安镇", 20);
46
+        SysDept village = village(300L, "雅安镇荣嘎村", 200L);
47
+        loginAs(300L, 9L);
48
+
49
+        when(deptService.selectDeptById(300L)).thenReturn(village);
50
+        when(deptService.selectDeptById(200L)).thenReturn(town);
51
+        when(deptService.selectDeptById(YakHerdInventoryRules.TOWN_PARENT_ID)).thenReturn(county());
52
+
53
+        List<SysDept> towns = townDeptSupport.listReportTowns();
54
+        assertEquals(1, towns.size());
55
+        assertEquals(200L, towns.get(0).getDeptId());
56
+
57
+        List<SysDept> villages = townDeptSupport.listVillagesUnderTown(200L);
58
+        assertEquals(1, villages.size());
59
+        assertEquals(300L, villages.get(0).getDeptId());
60
+
61
+        assertTrue(townDeptSupport.listVillagesUnderTown(999L).isEmpty());
62
+
63
+        List<TreeSelect> tree = townDeptSupport.buildVillageTree();
64
+        assertEquals(1, tree.size());
65
+        assertEquals(1, tree.get(0).getChildren().size());
66
+        assertEquals(200L, tree.get(0).getChildren().get(0).getId());
67
+        assertEquals(1, tree.get(0).getChildren().get(0).getChildren().size());
68
+        assertEquals(300L, tree.get(0).getChildren().get(0).getChildren().get(0).getId());
69
+    }
70
+
71
+    @Test
72
+    @DisplayName("乡镇人员:仅本镇")
73
+    void townUserSeesOwnTown()
74
+    {
75
+        SysDept town = town(200L, "雅安镇", 20);
76
+        loginAs(200L, 8L);
77
+        when(deptService.selectDeptById(200L)).thenReturn(town);
78
+
79
+        List<SysDept> towns = townDeptSupport.listReportTowns();
80
+        assertEquals(1, towns.size());
81
+        assertEquals(200L, towns.get(0).getDeptId());
82
+    }
83
+
84
+    @Test
85
+    @DisplayName("无登录上下文时仍走部门列表查询")
86
+    void noLoginFallsBackToDeptList()
87
+    {
88
+        SysDept town = town(200L, "雅安镇", 20);
89
+        when(deptService.selectDeptList(any())).thenReturn(Collections.singletonList(town));
90
+        assertEquals(1, townDeptSupport.listReportTowns().size());
91
+    }
92
+
93
+    private static void loginAs(Long deptId, Long userId)
94
+    {
95
+        SysUser user = new SysUser();
96
+        user.setUserId(userId);
97
+        user.setDeptId(deptId);
98
+        LoginUser loginUser = new LoginUser(userId, deptId, user, Collections.emptySet());
99
+        SecurityContextHolder.getContext().setAuthentication(
100
+                new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList()));
101
+    }
102
+
103
+    private static SysDept county()
104
+    {
105
+        SysDept d = new SysDept();
106
+        d.setDeptId(YakHerdInventoryRules.TOWN_PARENT_ID);
107
+        d.setDeptName("巴青县");
108
+        d.setStatus("0");
109
+        d.setDelFlag("0");
110
+        return d;
111
+    }
112
+
113
+    private static SysDept town(Long id, String name, int orderNum)
114
+    {
115
+        SysDept d = new SysDept();
116
+        d.setDeptId(id);
117
+        d.setDeptName(name);
118
+        d.setParentId(YakHerdInventoryRules.TOWN_PARENT_ID);
119
+        d.setOrderNum(orderNum);
120
+        d.setStatus("0");
121
+        d.setDelFlag("0");
122
+        return d;
123
+    }
124
+
125
+    private static SysDept village(Long id, String name, Long townId)
126
+    {
127
+        SysDept d = new SysDept();
128
+        d.setDeptId(id);
129
+        d.setDeptName(name);
130
+        d.setParentId(townId);
131
+        d.setOrderNum(1);
132
+        d.setStatus("0");
133
+        d.setDelFlag("0");
134
+        return d;
135
+    }
136
+}