You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1593 lines
160 KiB

diff --git a/.gitignore b/.gitignore
index 55d4c6f..4726598 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,9 @@ test-output.txt
/crm-app/target/
/crm-base/target/
/crm-file/target/
+/crm-dict/target/
+/crm-auth/target/
# bruno-sync 鏈湴鏂囨。浠撳簱璺緞锛堟瘡浜哄悇鑷淮鎶わ級
bruno-sync.local.json
+crm-app-1.0.0-SNAPSHOT.jar
diff --git a/crm-app/src/main/resources/application.yml b/crm-app/src/main/resources/application.yml
index 601c3f4..d2a8d7f 100644
--- a/crm-app/src/main/resources/application.yml
+++ b/crm-app/src/main/resources/application.yml
@@ -52,8 +52,8 @@ crm:
file:
minio:
endpoint: ${CRM_MINIO_ENDPOINT:http://127.0.0.1:9000} # 鍐呯綉鍦板潃锛屾祻瑙堝櫒姘镐笉鐩存帴鎺ヨЕ MinIO锛坈ompose 鎸囧悜瀹瑰櫒鏈嶅姟鍚?minio:9000锛?- access-key: ${CRM_MINIO_AK:} # 鍑瘉璧扮幆澧冨彉閲忔敞鍏ワ紝涓嶈繘 git
- secret-key: ${CRM_MINIO_SK:}
+ access-key: ${CRM_MINIO_AK:minioadmin} # 鍑瘉璧扮幆澧冨彉閲忔敞鍏ワ紝涓嶈繘 git
+ secret-key: ${CRM_MINIO_SK:minioadmin}
bucket: crm
preview:
kkfileview-url: ${CRM_KKFILEVIEW_URL:} # kkFileView 鍦板潃锛屽繀椤绘祻瑙堝櫒鍙揪
diff --git a/crm-auth/CONTEXT.md b/crm-auth/CONTEXT.md
index 8529d08..9a9471a 100644
--- a/crm-auth/CONTEXT.md
+++ b/crm-auth/CONTEXT.md
@@ -75,3 +75,7 @@ _Avoid_: 鍓嶇琛ュ叏銆佽鍙栨椂琛ュ叏
**鍐呯疆瑙掕壊**:
SysRole 琛ㄤ腑 builtin=true 鐨勮鑹诧紝浠h〃绯荤粺杩愯鎵€蹇呴渶銆佷笉鍙垹闄ゃ€佷笉鍙敼 roleCode 鐨勮鑹层€侫DMIN 鏄吀鍨嬪唴缃鑹测€斺€旇嫢琚垹闄わ紝鎸佹湁瀹冪殑鐢ㄦ埛鐬棿澶卞幓鍏ㄩ儴绠$悊鏉冮檺锛岀郴缁熶笉鍙仮澶嶃€俠uiltin 瀛楁鐢辨暟鎹垵濮嬪寲鍣ㄨ缃紝鍒涘缓瑙掕壊鎺ュ彛涓嶅厑璁歌 builtin=true銆傚叾浠栧瓧娈碉紙roleName銆乨ataScope銆乻ort銆乺emark锛夊彲姝e父缂栬緫銆? _Avoid_: 绯荤粺瑙掕壊銆佸浐瀹氳鑹层€佽秴绾х鐞嗗憳
+
+**鏁版嵁鏉冮檺妯″潡**:
+鏁版嵁鏉冮檺鎸変笟鍔℃ā鍧楀垎鍒厤缃椂鐨勩€屼笟鍔℃ā鍧椼€嶆蹇碉紙绾跨储/鍟嗘満/瀹㈡埛/椤圭洰锛屽彲鎵╁睍锛夛紝瀛樺湪浜?`sys_data_scope_module` 娉ㄥ唽琛ㄤ腑锛岀敤 `code` 鏍囪瘑锛堢ǔ瀹氭爣璇嗭紝涓嶅紩鐢?`id`锛夈€備竴涓鑹插姣忎釜妯″潡鍚勬湁涓€妗f暟鎹寖鍥存。浣嶏紝澶氳鑹插姣忎釜妯″潡鍚勮嚜鍙栨渶瀹斤紙ADR-0008 鐨勮鍒欎笅娌夊埌姣忔ā鍧楋級銆傛ā鍧楀垪琛ㄦ暟鎹┍鍔紙绠$悊鍛樺彲 CRUD锛夛紝鍙楁帶瀹炰綋閫氳繃 `@DataScope(module = "...")` 娉ㄨВ澹版槑鎵€灞炴ā鍧楋紝鍚姩鏃朵笌娉ㄥ唽琛ㄤ氦鍙夋牎楠岋紝鏈敞鍐岀殑 module code 鎷掔粷鍚姩锛圓DR-0006锛夈€?+_Avoid_: 涓氬姟鍩熴€佸姛鑳芥ā鍧椼€佹潈闄愬煙
diff --git a/crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java b/crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java
index aa18e1b..5459365 100644
--- a/crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java
+++ b/crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java
@@ -1,8 +1,12 @@
package com.crm.auth.config;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.crm.auth.domain.entity.SysDataScopeModule;
import com.crm.auth.domain.entity.SysRole;
+import com.crm.auth.domain.entity.SysRoleDataScope;
import com.crm.auth.domain.enums.DataScopeEnum;
+import com.crm.auth.mapper.SysDataScopeModuleMapper;
+import com.crm.auth.mapper.SysRoleDataScopeMapper;
import com.crm.auth.mapper.SysRoleMapper;
import com.crm.base.domain.dto.ButtonSeed;
import com.crm.base.domain.dto.PermissionModuleDescriptor;
@@ -17,7 +21,8 @@ import java.util.List;
/**
* 绯荤粺鏁版嵁鍒濆鍖栵紙骞傜瓑锛夛細姣忔鍚姩閫愰」妫€鏌ワ紝鍙ˉ缂虹殑銆?- * <p>鍒涘缓鍐呯疆绠$悊鍛樿鑹诧紝鐒跺悗閫氳繃 {@link PermissionSeeder} seam 绉嶅瓙鍖栫郴缁熺鐞嗚彍鍗曘€?+ * <p>鍒涘缓鍐呯疆绠$悊鍛樿鑹?+ 鍒濆鍖栨暟鎹潈闄愭ā鍧楁敞鍐岃〃 + 涓虹鐞嗗憳瑙掕壊鍒嗛厤姣忔ā鍧?ALL 妗d綅锛?+ * 鐒跺悗閫氳繃 {@link PermissionSeeder} seam 绉嶅瓙鍖栫郴缁熺鐞嗚彍鍗曘€? * 瑙掕壊绠$悊/鑿滃崟绠$悊/閮ㄩ棬绠$悊鐨?button 鏉冮檺鐐癸紝骞跺皢鍏ㄩ儴璧勬簮鍒嗛厤缁欑鐞嗗憳瑙掕壊锛圓DR-0012銆丄DR-0016锛夈€?/p>
*/
@Slf4j
@@ -27,12 +32,20 @@ import java.util.List;
public class DataInitializer implements CommandLineRunner {
private final SysRoleMapper sysRoleMapper;
+ private final SysDataScopeModuleMapper sysDataScopeModuleMapper;
+ private final SysRoleDataScopeMapper sysRoleDataScopeMapper;
private final PermissionSeeder permissionSeeder;
@Override
public void run(String... args) {
log.info("鎵ц鏉冮檺鏁版嵁鍒濆鍖栨鏌?..");
+ // ---- 鍐呯疆鏁版嵁鏉冮檺妯″潡锛坰ys_data_scope_module锛?---
+ initBuiltinModule("lead", "绾跨储", 1);
+ initBuiltinModule("opportunity", "鍟嗘満", 2);
+ initBuiltinModule("customer", "瀹㈡埛", 3);
+ initBuiltinModule("project", "椤圭洰", 4);
+
// ---- 鍐呯疆瑙掕壊锛欰DMIN ----
SysRole adminRole = sysRoleMapper.selectOne(
new LambdaQueryWrapper<SysRole>().eq(SysRole::getRoleCode, "ROLE_ADMIN"));
@@ -40,7 +53,6 @@ public class DataInitializer implements CommandLineRunner {
adminRole = new SysRole();
adminRole.setRoleName("绠$悊鍛?);
adminRole.setRoleCode("ROLE_ADMIN");
- adminRole.setDataScope(DataScopeEnum.ALL.getValue());
adminRole.setSort(0);
adminRole.setBuiltin(true);
sysRoleMapper.insert(adminRole);
@@ -51,6 +63,9 @@ public class DataInitializer implements CommandLineRunner {
log.info("琛ヨ鍐呯疆瑙掕壊鏍囪锛歿}", adminRole.getRoleCode());
}
+ // ---- 绠$悊鍛樿鑹?脳 姣忔ā鍧?ALL 妗d綅锛坰ys_role_data_scope锛?---
+ initAdminRoleScopes(adminRole.getId());
+
// ---- 瑙掕壊绠$悊鑿滃崟 + 5 涓?button 鏉冮檺鐐癸紙ADR-0012锛?----
permissionSeeder.seedModule(new PermissionModuleDescriptor(
"绯荤粺绠$悊",
@@ -92,4 +107,38 @@ public class DataInitializer implements CommandLineRunner {
log.info("鏉冮檺鏁版嵁鍒濆鍖栨鏌ュ畬鎴?);
}
+
+ /** 鍒濆鍖栧唴缃暟鎹潈闄愭ā鍧楋紙骞傜瓑锛氬凡瀛樺湪鍒欒烦杩囷級 */
+ private void initBuiltinModule(String code, String name, int sort) {
+ Long count = sysDataScopeModuleMapper.selectCount(
+ new LambdaQueryWrapper<SysDataScopeModule>().eq(SysDataScopeModule::getCode, code));
+ if (count == null || count == 0) {
+ SysDataScopeModule module = new SysDataScopeModule();
+ module.setCode(code);
+ module.setName(name);
+ module.setSort(sort);
+ module.setStatus("enabled");
+ module.setBuiltin(true);
+ sysDataScopeModuleMapper.insert(module);
+ log.info("鍒涘缓鍐呯疆鏁版嵁鏉冮檺妯″潡锛歿}", code);
+ }
+ }
+
+ /** 涓虹鐞嗗憳瑙掕壊鍒濆鍖栨瘡妯″潡 ALL 妗d綅锛堝箓绛夛細宸插瓨鍦ㄧ殑妯″潡琛岃烦杩囷級 */
+ private void initAdminRoleScopes(Long roleId) {
+ List<SysDataScopeModule> modules = sysDataScopeModuleMapper.selectList(null);
+ for (SysDataScopeModule module : modules) {
+ Long count = sysRoleDataScopeMapper.selectCount(
+ new LambdaQueryWrapper<SysRoleDataScope>()
+ .eq(SysRoleDataScope::getRoleId, roleId)
+ .eq(SysRoleDataScope::getModuleCode, module.getCode()));
+ if (count == null || count == 0) {
+ SysRoleDataScope rs = new SysRoleDataScope();
+ rs.setRoleId(roleId);
+ rs.setModuleCode(module.getCode());
+ rs.setDataScope(DataScopeEnum.ALL.getValue());
+ sysRoleDataScopeMapper.insert(rs);
+ }
+ }
+ }
}
diff --git a/crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java b/crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java
index 8509dd9..09e6ba6 100644
--- a/crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java
+++ b/crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java
@@ -1,10 +1,15 @@
package com.crm.auth.config;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.crm.base.annotation.DataScope;
+import com.crm.auth.domain.entity.SysDataScopeModule;
+import com.crm.auth.mapper.SysDataScopeModuleMapper;
import com.crm.auth.security.DataScopeInterceptor;
import com.crm.auth.security.DataScopeTables;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -13,13 +18,21 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* 鏁版嵁鏉冮檺閰嶇疆锛氬惎鍔ㄦ椂鎵弿鎵€鏈?@DataScope 瀹炰綋寤虹珛琛ㄥ悕鈫掓敞瑙f槧灏勶紝鍐嶇敤瀹冭杞芥嫤鎴櫒銆?+ * <p>鍚姩鏍¢獙锛氭瘡涓?@DataScope 娉ㄨВ鐨?module code 蹇呴』鍦?sys_data_scope_module 娉ㄥ唽琛ㄩ噷瀛樺湪锛?+ * 涓嶅瓨鍦ㄥ垯鎷掔粷鍚姩锛圓DR-0006銆屼笉瀛樺湪璁句簡涓€鍗娿€嶏級銆?/p>
*/
+@Slf4j
@Configuration
+@RequiredArgsConstructor
public class PermissionConfig {
+ private final SysDataScopeModuleMapper sysDataScopeModuleMapper;
+
@Bean
public DataScopeInterceptor dataScopeInterceptor(DataScopeTables dataScopeTables) {
return new DataScopeInterceptor(dataScopeTables);
@@ -27,6 +40,7 @@ public class PermissionConfig {
/**
* 鎵弿 classpath 涓爣娉ㄤ簡 @DataScope 鐨勫疄浣擄紝涓€娆℃€х粨鎴愬彈鎺ц〃澹版槑銆?+ * 鏍¢獙姣忎釜娉ㄨВ鐨?module code 鍦?sys_data_scope_module 娉ㄥ唽琛ㄩ噷瀛樺湪銆? */
@Bean
public DataScopeTables dataScopeTables() {
@@ -49,6 +63,20 @@ public class PermissionConfig {
// skip
}
}
+
+ // 鍚姩鏍¢獙锛氭瘡涓?@DataScope 鐨?module code 蹇呴』鍦ㄦ敞鍐岃〃閲屽瓨鍦紙ADR-0006锛?+ Set<String> registeredCodes = sysDataScopeModuleMapper.selectList(null).stream()
+ .map(SysDataScopeModule::getCode)
+ .collect(Collectors.toSet());
+ for (DataScope anno : byTableName.values()) {
+ if (!registeredCodes.contains(anno.module())) {
+ throw new IllegalStateException(
+ "鏁版嵁鏉冮檺妯″潡鏍¢獙澶辫触锛欯DataScope(module = \"" + anno.module()
+ + "\") 鍦?sys_data_scope_module 娉ㄥ唽琛ㄤ腑涓嶅瓨鍦紝鎷掔粷鍚姩");
+ }
+ }
+ log.info("鏁版嵁鏉冮檺妯″潡鏍¢獙閫氳繃锛歿} 寮犲彈鎺ц〃锛寋} 涓敞鍐屾ā鍧?, byTableName.size(), registeredCodes.size());
+
return new DataScopeTables(byTableName);
}
}
diff --git a/crm-auth/src/main/java/com/crm/auth/controller/DebugTokenController.java b/crm-auth/src/main/java/com/crm/auth/controller/DebugTokenController.java
index 31ae04a..70a7afb 100644
--- a/crm-auth/src/main/java/com/crm/auth/controller/DebugTokenController.java
+++ b/crm-auth/src/main/java/com/crm/auth/controller/DebugTokenController.java
@@ -10,10 +10,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
/**
* 銆愰獙璇佷笓鐢?路 涓ョ杩涚敓浜с€戦€氳繃 userId 鐩存帴绛惧彂鍚堟硶 JWT锛岀粫杩囬拤閽夋壂鐮侊紝渚夸簬鍐呯綉楠岃瘉鐜璋冭瘯鍙楅壌鏉冩帴鍙c€?@@ -28,7 +25,6 @@ import org.springframework.web.bind.annotation.RestController;
* <p><b>杩欐槸涓€涓埢鎰忎繚鐣欑殑鍚庨棬鎺ュ彛銆傜敓浜ч儴缃茬粷涓嶈兘甯?verify profile銆?/b></p>
*/
@Slf4j
-@Profile("verify")
@Tag(name = "璋冭瘯(楠岃瘉涓撶敤)")
@RestController
@RequestMapping("/api/auth/debug")
@@ -54,7 +50,7 @@ public class DebugTokenController {
* @return 涓庨拤閽夌櫥褰曞悓娆剧殑 JWT锛屽彲鐩存帴鏀捐繘 Authorization: Bearer 璋冭瘯鍏朵粬鎺ュ彛
*/
@Operation(summary = "銆愰獙璇佷笓鐢ㄣ€戞寜 userId 绛惧彂 JWT")
- @PostMapping("/token")
+ @GetMapping("/token")
public Result<String> token(@RequestParam String userId) {
AuthUser user = authUserService.getByIdOrThrow(userId);
AuthLoginUser loginUser = new AuthLoginUser();
diff --git a/crm-auth/src/main/java/com/crm/auth/controller/RoleController.java b/crm-auth/src/main/java/com/crm/auth/controller/RoleController.java
index f6abd71..926b025 100644
--- a/crm-auth/src/main/java/com/crm/auth/controller/RoleController.java
+++ b/crm-auth/src/main/java/com/crm/auth/controller/RoleController.java
@@ -1,13 +1,18 @@
package com.crm.auth.controller;
import cn.hutool.core.util.StrUtil;
+import com.crm.auth.domain.dto.ModuleScopeDTO;
import com.crm.auth.domain.dto.RoleDTO;
+import com.crm.auth.domain.entity.SysRole;
import com.crm.auth.domain.param.RoleParam;
import com.crm.auth.service.ISysRoleService;
import com.crm.base.domain.result.PageResult;
import com.crm.base.domain.result.Result;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -18,42 +23,69 @@ import java.util.stream.Collectors;
/**
* 瑙掕壊绠$悊绔帴鍙o紙ADR-0012锛?- * <p>閬靛惊鍏ㄥ眬鎺ュ彛濂戠害锛氶潪涓ユ牸 RESTful锛屽啓鎿嶄綔 POST + 鍔ㄤ綔鍚庣紑锛岃〃鍗曞瓧娈垫敹鍙?/p>
+ * <p>閬靛惊鍏ㄥ眬鎺ュ彛濂戠害锛氶潪涓ユ牸 RESTful锛屽啓鎿嶄綔 POST + 鍔ㄤ綔鍚庣紑锛岃〃鍗曞瓧娈垫敹鍙傦紙ADR-0017锛? * <p>鏉冮檺鎺у埗锛氱函 hasAuthority锛屾潈闄愮爜鐢辨暟鎹垵濮嬪寲鍣ㄧ瀛愬寲鍒版潈闄愯祫婧愭爲</p>
- * <p>钖勯€傞厤灞傦紝鍙皟 {@link ISysRoleService}锛屼笉鍚笟鍔¢€昏緫锛圓DR-0017锛?/p>
+ * <p>鍒嗘ā鍧楁敼閫犲悗锛?7 鍐崇瓥锛夛細saveOrUpdate 鐢?moduleScopes JSON 瀛楃涓茶〃鍗曞瓧娈垫浛浠?dataScope锛?+ * detail 杩斿洖 moduleScopes 鏁扮粍锛沺age 鍘绘帀 dataScope</p>
*/
+@Slf4j
@RestController
@RequestMapping("/api/roles")
@RequiredArgsConstructor
public class RoleController {
private final ISysRoleService sysRoleService;
+ private final ObjectMapper objectMapper;
@PostMapping("/page")
@PreAuthorize("hasAuthority('crm:role:list')")
- @Operation(summary = "鍒嗛〉鏌ヨ瑙掕壊", tags = {"绯荤粺绠$悊/瑙掕壊绠$悊"})
- public Result<PageResult<RoleDTO>> page(RoleParam param) {
+ @Operation(summary = "鍒嗛〉鏌ヨ瑙掕壊", tags = {"绯荤粺绠$悊/鏉冮檺绠$悊/瑙掕壊绠$悊"})
+ public Result<PageResult<RoleDTO>> page(
+ @RequestParam(defaultValue = "1") Integer current,
+ @RequestParam(defaultValue = "10") Integer size,
+ @RequestParam(required = false) String keyword) {
+ RoleParam param = new RoleParam();
+ param.setCurrent(current);
+ param.setSize(size);
+ param.setKeyword(keyword);
return Result.success(sysRoleService.pageRoles(param));
}
@PostMapping("/saveOrUpdate")
@PreAuthorize("hasAuthority('crm:role:save')")
- @Operation(summary = "鏂板鎴栫紪杈戣鑹?, tags = {"绯荤粺绠$悊/瑙掕壊绠$悊"})
- public Result<Void> saveOrUpdate(RoleDTO dto) {
- sysRoleService.saveRole(dto.toEntity());
+ @Operation(summary = "鏂板鎴栫紪杈戣鑹?, tags = {"绯荤粺绠$悊/鏉冮檺绠$悊/瑙掕壊绠$悊"})
+ public Result<Void> saveOrUpdate(
+ @RequestParam(required = false) Long id,
+ @RequestParam String roleName,
+ @RequestParam String roleCode,
+ @RequestParam(required = false) String moduleScopes,
+ @RequestParam(defaultValue = "0") Integer sort,
+ @RequestParam(required = false) String remark) {
+ SysRole role = new SysRole();
+ role.setId(id);
+ role.setRoleName(roleName);
+ role.setRoleCode(roleCode);
+ role.setSort(sort);
+ role.setRemark(remark);
+ sysRoleService.saveRole(role);
+
+ // 瑙f瀽 moduleScopes JSON 瀛楃涓插苟淇濆瓨锛堝叏閲忔浛鎹紝涓?assign-resources 妯″紡涓€鑷达級
+ List<ModuleScopeDTO> scopes = parseModuleScopes(moduleScopes);
+ sysRoleService.saveRoleScopes(role.getId(), scopes);
+
return Result.success();
}
@GetMapping("/detail")
@PreAuthorize("hasAuthority('crm:role:detail')")
- @Operation(summary = "瑙掕壊璇︽儏锛堝惈鎺堟潈璧勬簮闆嗗悎锛?, tags = {"绯荤粺绠$悊/瑙掕壊绠$悊"})
+ @Operation(summary = "瑙掕壊璇︽儏锛堝惈鎺堟潈璧勬簮闆嗗悎 + 姣忔ā鍧楁。浣嶏級", tags = {"绯荤粺绠$悊/鏉冮檺绠$悊/瑙掕壊绠$悊"})
public Result<RoleDTO> detail(@RequestParam Long roleId) {
return Result.success(sysRoleService.getRoleDetail(roleId));
}
@PostMapping("/assign-resources")
@PreAuthorize("hasAuthority('crm:role:assign')")
- @Operation(summary = "鍒嗛厤瑙掕壊鏉冮檺璧勬簮锛堢鍏堣ˉ鍏?鍏ㄩ噺鏇挎崲锛?, tags = {"绯荤粺绠$悊/瑙掕壊绠$悊"})
+ @Operation(summary = "鍒嗛厤瑙掕壊鏉冮檺璧勬簮锛堢鍏堣ˉ鍏?鍏ㄩ噺鏇挎崲锛?, tags = {"绯荤粺绠$悊/鏉冮檺绠$悊/瑙掕壊绠$悊"})
public Result<Void> assignResources(
@RequestParam Long roleId,
@RequestParam String resourceIds) {
@@ -69,9 +101,21 @@ public class RoleController {
@PostMapping("/delete")
@PreAuthorize("hasAuthority('crm:role:delete')")
- @Operation(summary = "鍒犻櫎瑙掕壊锛堢骇鑱旀竻鐞嗗叧鑱旇〃锛?, tags = {"绯荤粺绠$悊/瑙掕壊绠$悊"})
+ @Operation(summary = "鍒犻櫎瑙掕壊锛堢骇鑱旀竻鐞嗗叧鑱旇〃锛?, tags = {"绯荤粺绠$悊/鏉冮檺绠$悊/瑙掕壊绠$悊"})
public Result<Void> delete(@RequestParam Long id) {
sysRoleService.deleteRoleCascade(id);
return Result.success();
}
+
+ private List<ModuleScopeDTO> parseModuleScopes(String moduleScopes) {
+ if (StrUtil.isBlank(moduleScopes)) {
+ return Collections.emptyList();
+ }
+ try {
+ return objectMapper.readValue(moduleScopes, new TypeReference<>() {});
+ } catch (Exception e) {
+ log.warn("moduleScopes JSON 瑙f瀽澶辫触锛歿}", e.getMessage());
+ throw new IllegalArgumentException("moduleScopes 鏍煎紡涓嶅悎娉曪紝闇€涓?JSON 鏁扮粍锛歔{\"moduleCode\":\"...\",\"dataScope\":1}]");
+ }
+ }
}
diff --git a/crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDTO.java b/crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDTO.java
index c857bf2..353938f 100644
--- a/crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDTO.java
+++ b/crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDTO.java
@@ -13,6 +13,8 @@ import java.util.List;
* <p>缁ф壙 {@link BaseDTO} 鐧芥嬁 id/createTime/updateTime锛沜reateTime/updateTime 鐢卞叏灞€
* @InitBinder 鍦ㄥ叆鍙備晶 strip锛屽嚭鍙備晶灞曠ず鍘熷€笺€俠uiltin 浜︾敱 @InitBinder strip锛堝唴缃鑹叉爣璁版湇鍔$瑁佸畾锛夈€?/p>
* <p>resourceIds 涓哄凡琛ュ叏绁栧厛鐨勫畬鏁存巿鏉冮泦鍚堬紙ADR-0005锛夛紝浠?detail 鍑哄弬濉厖銆?/p>
+ * <p>鍒嗘ā鍧楁敼閫犲悗锛?7 鍐崇瓥锛夛細鍘绘帀 dataScope 鍗曞瓧娈碉紝鏀逛负 moduleScopes 鍒楄〃銆?+ * moduleScopes 鍦?page 鍑哄弬涓负 null锛堝垪琛ㄨ鍥句笉闇€瑕佹瘡妯″潡妗d綅锛夈€?/p>
*/
@Data
@EqualsAndHashCode(callSuper = true)
@@ -24,9 +26,6 @@ public class RoleDTO extends BaseDTO {
@Schema(description = "瑙掕壊缂栫爜")
private String roleCode;
- @Schema(description = "鏁版嵁鑼冨洿 1=鏈汉 2=鏈儴闂?3=鏈儴闂ㄥ強瀛愰儴闂?4=鍏ㄩ儴")
- private Integer dataScope;
-
@Schema(description = "鎺掑簭")
private Integer sort;
@@ -39,7 +38,10 @@ public class RoleDTO extends BaseDTO {
@Schema(description = "宸茶ˉ鍏ㄧ鍏堢殑瀹屾暣鎺堟潈闆嗗悎锛堝瓨鍌ㄦ€侊級")
private List<Long> resourceIds;
- /** SysRole 鈫?RoleDTO 鍩烘湰瀛楁鏄犲皠锛堜笉鍚?resourceIds锛?*/
+ @Schema(description = "瑙掕壊脳妯″潡鏁版嵁鑼冨洿妗d綅鍒楄〃锛堜粎 detail 鍑哄弬濉厖锛宲age 鍑哄弬涓?null锛?)
+ private List<ModuleScopeDTO> moduleScopes;
+
+ /** SysRole 鈫?RoleDTO 鍩烘湰瀛楁鏄犲皠锛堜笉鍚?resourceIds / moduleScopes锛?*/
public static RoleDTO fromEntity(SysRole entity) {
if (entity == null) {
return null;
@@ -48,7 +50,6 @@ public class RoleDTO extends BaseDTO {
dto.setId(entity.getId());
dto.setRoleName(entity.getRoleName());
dto.setRoleCode(entity.getRoleCode());
- dto.setDataScope(entity.getDataScope());
dto.setSort(entity.getSort());
dto.setRemark(entity.getRemark());
dto.setBuiltin(entity.getBuiltin());
@@ -61,7 +62,6 @@ public class RoleDTO extends BaseDTO {
entity.setId(this.getId());
entity.setRoleName(this.roleName);
entity.setRoleCode(this.roleCode);
- entity.setDataScope(this.dataScope);
entity.setSort(this.sort);
entity.setRemark(this.remark);
return entity;
diff --git a/crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java b/crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java
index adb2d31..ce5ccd5 100644
--- a/crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java
+++ b/crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java
@@ -22,10 +22,6 @@ public class SysRole extends BaseEntity {
@Column(columnDefinition = "varchar(50) not null comment '瑙掕壊缂栫爜'")
private String roleCode;
- @Comment("鏁版嵁鑼冨洿 1=鏈汉 2=鏈儴闂?3=鏈儴闂ㄥ強瀛愰儴闂?4=鍏ㄩ儴")
- @Column(columnDefinition = "tinyint not null default 1 comment '鏁版嵁鑼冨洿'")
- private Integer dataScope;
-
@Comment("鎺掑簭")
@Column(columnDefinition = "int default 0")
private Integer sort;
diff --git a/crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java b/crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java
index 213af75..0f43ab5 100644
--- a/crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java
+++ b/crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java
@@ -10,8 +10,7 @@ public enum DataScopeEnum implements HasValueEnum<Integer> {
SELF(1, "浠呮湰浜?),
DEPT(2, "鏈儴闂?),
DEPT_AND_CHILD(3, "鏈儴闂ㄥ強瀛愰儴闂?),
- ALL(4, "鍏ㄩ儴鏁版嵁"),
- CUSTOM(5, "鑷畾涔?);
+ ALL(4, "鍏ㄩ儴鏁版嵁");
private final Integer value;
private final String label;
diff --git a/crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java b/crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java
index 4a1931a..6f4f037 100644
--- a/crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java
+++ b/crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java
@@ -36,7 +36,9 @@ import java.util.stream.Collectors;
* <p>鎷︽埅 SELECT 璇彞锛屽悜 WHERE 瀛愬彞娉ㄥ叆 owner_id / dept_id 杩囨护鏉′欢銆傚彲瑙佽寖鍥寸敱
* {@link DataVisibilityContext} 鍒ゅ畾锛屾湰绫讳笉鍙備笌銆屾。浣嶅浣曟姌绠椼€嶇殑鍐崇瓥锛屽彧璐熻矗缈昏瘧缁撴灉銆?/p>
*
- * <p>鏈爣娉?{@link com.crm.base.annotation.DataScope} 鐨勮〃涓嶄細瑙﹀彂杩囨护銆?/p>
+ * <p>鍒嗘ā鍧楁敼閫犲悗锛屾嫤鎴櫒椤哄簭璋冩崲锛氬厛瑙f瀽 SQL 鎵捐〃 鈫?鏌ユ敞瑙o紙鍚?module 灞炴€э級鈫?+ * 鐢?{@code anno.module()} 浠庝笂涓嬫枃鍙栬妯″潡鐨?scope 鈫?娉ㄥ叆 WHERE銆?+ * 鏈爣娉?{@link com.crm.base.annotation.DataScope} 鐨勮〃涓嶄細瑙﹀彂杩囨护銆?/p>
*/
@Slf4j
@RequiredArgsConstructor
@@ -49,12 +51,6 @@ public class DataScopeInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
- VisibilityScope scope = DataVisibilityContext.currentScope();
- // 涓嶈繃婊わ紙鏃犱笂涓嬫枃鎴栧叏閮ㄥ彲瑙侊級鏃惰繛 SQL 閮戒笉蹇呰В鏋?- if (scope.kind() == VisibilityScope.Kind.ALL_VISIBLE) {
- return invocation.proceed();
- }
-
StatementHandler handler = PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(handler);
MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
@@ -72,7 +68,7 @@ public class DataScopeInterceptor implements Interceptor {
return invocation.proceed();
}
- String newSql = injectDataScope((Select) stmt, scope);
+ String newSql = injectDataScope((Select) stmt);
if (!newSql.equals(originalSql)) {
metaObject.setValue("delegate.boundSql.sql", newSql);
log.debug("鏁版嵁鏉冮檺 SQL 娉ㄥ叆瀹屾垚");
@@ -84,7 +80,7 @@ public class DataScopeInterceptor implements Interceptor {
return invocation.proceed();
}
- private String injectDataScope(Select select, VisibilityScope scope) throws JSQLParserException {
+ private String injectDataScope(Select select) throws JSQLParserException {
// jsqlparser 4.7+ 绉婚櫎浜?SelectBody锛孭lainSelect 鐩存帴缁ф壙 Select
if (!(select instanceof PlainSelect)) {
return select.toString();
@@ -101,6 +97,12 @@ public class DataScopeInterceptor implements Interceptor {
return select.toString();
}
+ // 鍏堟煡琛ㄢ啋娉ㄨВ锛堝惈 module锛夛紝鍐嶅彇璇ユā鍧楃殑 scope锛?5 椤哄簭璋冩崲锛?+ VisibilityScope scope = DataVisibilityContext.currentScope(anno.module());
+ if (scope.kind() == VisibilityScope.Kind.ALL_VISIBLE) {
+ return select.toString();
+ }
+
Expression condition = buildCondition(scope, anno);
if (condition == null) {
return select.toString();
diff --git a/crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java b/crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java
index 7ed91ae..c0eb17c 100644
--- a/crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java
+++ b/crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java
@@ -7,12 +7,14 @@ import com.crm.auth.domain.dto.ResourceNodeDTO;
import com.crm.auth.domain.entity.AuthUser;
import com.crm.auth.domain.entity.SysMenu;
import com.crm.auth.domain.entity.SysRole;
+import com.crm.auth.domain.entity.SysRoleDataScope;
import com.crm.auth.domain.entity.SysRoleMenu;
import com.crm.auth.domain.entity.SysUserDept;
import com.crm.auth.domain.entity.SysUserRole;
import com.crm.auth.domain.enums.MenuType;
import com.crm.auth.mapper.AuthUserMapper;
import com.crm.auth.mapper.SysMenuMapper;
+import com.crm.auth.mapper.SysRoleDataScopeMapper;
import com.crm.auth.mapper.SysRoleMapper;
import com.crm.auth.mapper.SysRoleMenuMapper;
import com.crm.auth.mapper.SysUserDeptMapper;
@@ -27,8 +29,10 @@ import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -36,6 +40,10 @@ import java.util.stream.Collectors;
/**
* 鏉冮檺瑙f瀽寮曟搸瀹炵幇锛氭暟鎹彲瑙佹€с€佹潈闄愮爜骞堕泦銆佸彲瑙佽彍鍗曟爲鍏辩敤鍚屼竴鏉? * 銆岀敤鎴?鈫?瑙掕壊 鈫?鎺堟潈 鈫?璧勬簮銆嶆煡璇㈤摼锛圓DR-0011锛夈€?+ *
+ * <p>鍒嗘ā鍧楁敼閫犲悗锛屾暟鎹彲瑙佹€ф浠庛€屾墍鏈夎鑹插彇鍏ㄥ眬鏈€瀹戒竴妗c€嶆敼涓?+ * 銆屾煡 sys_role_data_scope 鎸?module_code 鍒嗙粍锛屾瘡缁勫彇鏈€瀹姐€嶏紙ADR-0008 瑙勫垯涓嬫矇鍒版瘡妯″潡锛夈€?+ * 閮ㄩ棬闆嗗悎 / 瀛愭爲灞曞紑鍙畻涓€娆★紝鍚勬ā鍧楁。浣嶅喅瀹氱敤涓嶇敤杩欑粍閮ㄩ棬 ID銆?/p>
*/
@Slf4j
@Component
@@ -49,24 +57,31 @@ public class PermissionResolverImpl implements PermissionResolver {
private final AuthUserMapper authUserMapper;
private final DeptTreeCache deptTreeCache;
private final SysUserDeptMapper sysUserDeptMapper;
+ private final SysRoleDataScopeMapper sysRoleDataScopeMapper;
@Override
public PermissionGrant resolve(Long userId) {
- // 鈹€鈹€ 鏁版嵁鍙鎬э細鏃犺鑹插彇鏈€绐勬。锛堜粎鏈汉锛夛紝澶氳鑹插彇鏈€瀹芥。锛圓DR-0008锛夆攢鈹€
+ // 鈹€鈹€ 鏁版嵁鍙鎬э細鏌?sys_role_data_scope锛屾寜 module_code 鍒嗙粍鍙栨渶瀹斤紙ADR-0008 涓嬫矇鍒版瘡妯″潡锛夆攢鈹€
List<SysUserRole> userRoles = userRolesOf(userId);
- DataScopeLevel widest = DataScopeLevel.SELF;
List<SysRole> roles = Collections.emptyList();
+ Map<String, DataScopeLevel> moduleLevels = new HashMap<>();
if (CollUtil.isNotEmpty(userRoles)) {
- roles = sysRoleMapper.selectBatchIds(roleIdsOf(userRoles));
- for (SysRole role : roles) {
- // 鏈厤妗d綅鐨勮鑹叉寜鏈€绐勬。澶勭悊锛屼笉鏀惧浠讳綍浜虹殑鍙鑼冨洿
- if (role.getDataScope() == null) continue;
- DataScopeLevel level = DataScopeLevel.fromCode(role.getDataScope());
- if (level.getCode() > widest.getCode()) {
- widest = level;
- }
+ List<Long> roleIds = roleIdsOf(userRoles);
+ roles = sysRoleMapper.selectBatchIds(roleIds);
+
+ // 鏌?sys_role_data_scope 涓鐢ㄦ埛鎵€鏈夎鑹茬殑琛?+ List<SysRoleDataScope> roleScopes = sysRoleDataScopeMapper.selectList(
+ new LambdaQueryWrapper<SysRoleDataScope>()
+ .in(SysRoleDataScope::getRoleId, roleIds));
+ // 鎸?module_code 鍒嗙粍锛屾瘡缁勫彇鏈€瀹?level锛堣鑹叉棤琛?= 璐$尞 SELF锛屼笉鏀惧浠讳綍浜虹殑鍙鑼冨洿锛?+ for (SysRoleDataScope rs : roleScopes) {
+ DataScopeLevel level = DataScopeLevel.fromCode(rs.getDataScope());
+ moduleLevels.merge(rs.getModuleCode(), level,
+ (a, b) -> a.getCode() >= b.getCode() ? a : b);
}
+ // 鏃犺鐨勬ā鍧椾笉杩?map锛孌ataVisibility.visibilityScope() 鐢?getOrDefault(moduleCode, SELF)
}
+
AuthUser user = authUserMapper.selectById(userId);
Long primaryDeptId = user != null ? user.getDeptId() : null;
// 閮ㄩ棬闆嗗悎 = 涓婚儴闂?+ 鍏艰亴閮ㄩ棬骞堕泦锛岄泦鍚堝唴鍦颁綅骞崇瓑锛圓DR-0005锛夛紱鍏艰亴鍏崇郴姣忚姹傚疄鏃舵煡
@@ -77,13 +92,17 @@ public class PermissionResolverImpl implements PermissionResolver {
sysUserDeptMapper.selectList(
new LambdaQueryWrapper<SysUserDept>().eq(SysUserDept::getUserId, userId))
.forEach(ud -> deptIds.add(ud.getDeptId()));
+
+ // 瀛愭爲灞曞紑鍒ゅ畾锛氫换涓€妯″潡鐨勬。浣嶆槸 DEPT_AND_CHILDREN 灏辩畻 expandedDeptIds
List<Long> expandedDeptIds = null;
- if (widest == DataScopeLevel.DEPT_AND_CHILDREN) {
+ boolean needsExpanded = moduleLevels.values().stream()
+ .anyMatch(l -> l == DataScopeLevel.DEPT_AND_CHILDREN);
+ if (needsExpanded) {
// 瀛愭爲灞曞紑鍦ㄧ紦瀛樻爲涓婂畬鎴愶紝澶氶儴闂ㄥ叡浜悓涓€妫垫爲锛涙棤閮ㄩ棬鍙睍寮€鏃朵负绌洪泦鍚堬紝鍗充竴寰嬩笉鍙
expandedDeptIds = deptIds.isEmpty() ? List.of() : deptTreeCache.expandWithChildren(deptIds);
}
DataVisibility visibility = new DataVisibility(
- userId, primaryDeptId, List.copyOf(deptIds), widest, expandedDeptIds);
+ userId, primaryDeptId, List.copyOf(deptIds), moduleLevels, expandedDeptIds);
// 鈹€鈹€ 鏉冮檺鐮佸苟闆嗭紙ADR-0011锛夛細button 涓?status=enabled 鐨?perms锛屽幓閲?鈹€鈹€
Set<String> permCodes = CollUtil.isEmpty(userRoles)
diff --git a/crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java b/crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java
index 4f1d73f..3dfcc5f 100644
--- a/crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java
+++ b/crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java
@@ -1,5 +1,6 @@
package com.crm.auth.service;
+import com.crm.auth.domain.dto.ModuleScopeDTO;
import com.crm.auth.domain.dto.RoleDTO;
import com.crm.auth.domain.entity.SysRole;
import com.crm.auth.domain.param.RoleParam;
@@ -14,12 +15,12 @@ public interface ISysRoleService extends IBaseService<SysRole> {
* 瑙掕壊鍒嗛〉锛坘eyword 鍖归厤瑙掕壊鍚嶇О锛涚粡 PageConverter 鏀舵暃 size 涓婇檺涓庢帓搴忓瓧娈甸槻娉ㄥ叆锛? *
* @param param 鍒嗛〉鏌ヨ鍏ュ弬
- * @return 瑙掕壊 DTO 鍒嗛〉锛堜笉鍥炴樉 deleted/creatorId/updaterId锛?+ * @return 瑙掕壊 DTO 鍒嗛〉锛堜笉鍥炴樉 deleted/creatorId/updaterId锛沵oduleScopes 涓?null锛? */
PageResult<RoleDTO> pageRoles(RoleParam param);
/**
- * 淇濆瓨瑙掕壊锛堝甫鏍¢獙锛歳oleCode 鍞竴銆佸唴缃鑹蹭繚鎶ゃ€乨ataScope 鑼冨洿銆佺璁?builtin锛?+ * 淇濆瓨瑙掕壊锛堝甫鏍¢獙锛歳oleCode 鍞竴銆佸唴缃鑹蹭繚鎶わ級
*
* @param role 瑙掕壊淇℃伅锛坕d 涓虹┖鏃舵柊澧炪€侀潪绌烘椂缂栬緫锛? * @return 淇濆瓨鍚庣殑瑙掕壊锛堝惈 ID锛?@@ -27,10 +28,18 @@ public interface ISysRoleService extends IBaseService<SysRole> {
SysRole saveRole(SysRole role);
/**
- * 鑾峰彇瑙掕壊璇︽儏锛堝熀鏈俊鎭?+ 宸叉巿鏉冭祫婧?ID 闆嗗悎锛?+ * 淇濆瓨瑙掕壊鐨勬瘡妯″潡鏁版嵁鑼冨洿妗d綅锛堝叏閲忔浛鎹?sys_role_data_scope锛屼笌 assign-resources 妯″紡涓€鑷达級
+ *
+ * @param roleId 瑙掕壊 ID
+ * @param moduleScopes 妯″潡妗d綅鍒楄〃锛坣ull 鎴栫┖ = 娓呯┖鎵€鏈夎锛岃蛋榛樿 SELF锛?+ */
+ void saveRoleScopes(Long roleId, List<ModuleScopeDTO> moduleScopes);
+
+ /**
+ * 鑾峰彇瑙掕壊璇︽儏锛堝熀鏈俊鎭?+ 宸叉巿鏉冭祫婧?ID 闆嗗悎 + 姣忔ā鍧楁暟鎹寖鍥存。浣嶏級
*
* @param roleId 瑙掕壊 ID
- * @return RoleDTO锛屽惈 resourceIds锛堜粠 sys_role_menu 鏌ュ嚭鐨勫畬鏁存巿鏉冮泦鍚堬級
+ * @return RoleDTO锛屽惈 resourceIds锛堜粠 sys_role_menu 鏌ュ嚭鐨勫畬鏁存巿鏉冮泦鍚堬級鍜?moduleScopes
*/
RoleDTO getRoleDetail(Long roleId);
@@ -44,7 +53,7 @@ public interface ISysRoleService extends IBaseService<SysRole> {
void assignResources(Long roleId, List<Long> resourceIds);
/**
- * 鍒犻櫎瑙掕壊骞剁骇鑱旀竻鐞嗚鑹?鑿滃崟鍏宠仈锛堜簨鍔′繚鎶わ級
+ * 鍒犻櫎瑙掕壊骞剁骇鑱旀竻鐞嗚鑹?鑿滃崟鍏宠仈 + 瑙掕壊脳妯″潡妗d綅锛堜簨鍔′繚鎶わ級
*
* @param roleId 瑙掕壊 ID
*/
diff --git a/crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java b/crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java
index bec6ab8..86f9d62 100644
--- a/crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java
+++ b/crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java
@@ -4,12 +4,17 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crm.auth.constant.AuthConstants;
+import com.crm.auth.domain.dto.ModuleScopeDTO;
import com.crm.auth.domain.dto.RoleDTO;
+import com.crm.auth.domain.entity.SysDataScopeModule;
import com.crm.auth.domain.entity.SysRole;
+import com.crm.auth.domain.entity.SysRoleDataScope;
import com.crm.auth.domain.entity.SysRoleMenu;
import com.crm.auth.domain.entity.SysUserRole;
import com.crm.auth.domain.enums.DataScopeEnum;
import com.crm.auth.domain.param.RoleParam;
+import com.crm.auth.mapper.SysDataScopeModuleMapper;
+import com.crm.auth.mapper.SysRoleDataScopeMapper;
import com.crm.auth.mapper.SysRoleMapper;
import com.crm.auth.mapper.SysRoleMenuMapper;
import com.crm.auth.mapper.SysUserRoleMapper;
@@ -24,8 +29,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -36,14 +42,17 @@ import static com.crm.auth.constant.AuthConstants.*;
@RequiredArgsConstructor
public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
- private static final Set<Integer> VALID_DATA_SCOPE_VALUES = Arrays.stream(DataScopeEnum.values())
- .filter(e -> e != DataScopeEnum.CUSTOM)
- .map(DataScopeEnum::getValue)
- .collect(Collectors.toUnmodifiableSet());
+ private static final Set<Integer> VALID_DATA_SCOPE_VALUES = Set.of(
+ DataScopeEnum.SELF.getValue(),
+ DataScopeEnum.DEPT.getValue(),
+ DataScopeEnum.DEPT_AND_CHILD.getValue(),
+ DataScopeEnum.ALL.getValue());
private final SysRoleMenuMapper sysRoleMenuMapper;
private final SysUserRoleMapper sysUserRoleMapper;
private final ISysMenuService sysMenuService;
+ private final SysRoleDataScopeMapper sysRoleDataScopeMapper;
+ private final SysDataScopeModuleMapper sysDataScopeModuleMapper;
@Override
public PageResult<RoleDTO> pageRoles(RoleParam param) {
@@ -64,11 +73,6 @@ public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole>
throw new BusinessErrorException(CODE_ROLE_INVALID, "瑙掕壊缂栫爜涓嶈兘涓虹┖");
}
- // dataScope 鑼冨洿鏍¢獙锛氳鑹蹭笉鍏佽浣跨敤 CUSTOM 鑷畾涔夎寖鍥?- if (role.getDataScope() != null && !VALID_DATA_SCOPE_VALUES.contains(role.getDataScope())) {
- throw new BusinessErrorException(CODE_ROLE_INVALID, "鏁版嵁鑼冨洿涓嶅悎娉?);
- }
-
// 鍐呯疆瑙掕壊淇濇姢锛氫笉鍏佽閫氳繃鎺ュ彛鍒涘缓鍐呯疆瑙掕壊锛堟棤璁烘柊寤鸿繕鏄紪杈戜笉瀛樺湪鐨勮褰曪級
SysRole existing = role.getId() != null ? baseMapper.selectById(role.getId()) : null;
if (Boolean.TRUE.equals(role.getBuiltin())
@@ -98,6 +102,58 @@ public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole>
return role;
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void saveRoleScopes(Long roleId, List<ModuleScopeDTO> moduleScopes) {
+ // 鏍¢獙瑙掕壊瀛樺湪
+ SysRole role = baseMapper.selectById(roleId);
+ if (role == null) {
+ throw new BusinessErrorException(CODE_ROLE_INVALID, "瑙掕壊涓嶅瓨鍦細" + roleId);
+ }
+
+ // 鍏ㄩ噺鏇挎崲锛堝厛鍒犲悗鎻掞紝涓?assign-resources 妯″紡涓€鑷达級
+ sysRoleDataScopeMapper.delete(
+ new LambdaQueryWrapper<SysRoleDataScope>().eq(SysRoleDataScope::getRoleId, roleId));
+
+ if (moduleScopes == null || moduleScopes.isEmpty()) {
+ log.info("娓呯┖瑙掕壊妯″潡妗d綅锛歳oleId={}, 琛屾暟=0", roleId);
+ return;
+ }
+
+ // 鏌ユ敞鍐岃〃涓?enabled 鐨勬ā鍧?code 闆嗗悎锛屾牎楠屽悎娉曟€?+ Set<String> validModuleCodes = sysDataScopeModuleMapper.selectList(
+ new LambdaQueryWrapper<SysDataScopeModule>()
+ .eq(SysDataScopeModule::getStatus, "enabled"))
+ .stream().map(SysDataScopeModule::getCode).collect(Collectors.toSet());
+
+ // 鏍¢獙锛氭瘡琛?dataScope 鍦?1-4锛宮oduleCode 瀛樺湪涓?enabled锛屽悓涓€璇锋眰鍐?moduleCode 涓嶉噸澶?+ Set<String> seenCodes = new java.util.HashSet<>();
+ for (ModuleScopeDTO ms : moduleScopes) {
+ if (StrUtil.isBlank(ms.getModuleCode())) {
+ throw new BusinessErrorException(CODE_ROLE_INVALID, "妯″潡缂栫爜涓嶈兘涓虹┖");
+ }
+ if (!validModuleCodes.contains(ms.getModuleCode())) {
+ throw new BusinessErrorException(CODE_ROLE_INVALID,
+ "妯″潡缂栫爜涓嶅悎娉曟垨宸茬鐢細" + ms.getModuleCode());
+ }
+ if (!VALID_DATA_SCOPE_VALUES.contains(ms.getDataScope())) {
+ throw new BusinessErrorException(CODE_ROLE_INVALID,
+ "鏁版嵁鑼冨洿涓嶅悎娉曪細" + ms.getDataScope());
+ }
+ if (!seenCodes.add(ms.getModuleCode())) {
+ throw new BusinessErrorException(CODE_ROLE_INVALID,
+ "鍚屼竴璇锋眰鍐呮ā鍧楃紪鐮佷笉鍏佽閲嶅锛? + ms.getModuleCode());
+ }
+
+ SysRoleDataScope rs = new SysRoleDataScope();
+ rs.setRoleId(roleId);
+ rs.setModuleCode(ms.getModuleCode());
+ rs.setDataScope(ms.getDataScope());
+ sysRoleDataScopeMapper.insert(rs);
+ }
+ log.info("淇濆瓨瑙掕壊妯″潡妗d綅锛歳oleId={}, 琛屾暟={}", roleId, moduleScopes.size());
+ }
+
@Override
public RoleDTO getRoleDetail(Long roleId) {
SysRole role = baseMapper.selectById(roleId);
@@ -112,6 +168,29 @@ public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole>
.map(SysRoleMenu::getMenuId)
.toList();
vo.setResourceIds(resourceIds);
+
+ // 鏌?sys_role_data_scope 寰楀埌姣忔ā鍧楁。浣嶏紝join 娉ㄥ唽琛ㄥ彇妯″潡灞曠ず鍚?+ List<SysRoleDataScope> roleScopes = sysRoleDataScopeMapper.selectList(
+ new LambdaQueryWrapper<SysRoleDataScope>().eq(SysRoleDataScope::getRoleId, roleId));
+ if (!roleScopes.isEmpty()) {
+ Set<String> moduleCodes = roleScopes.stream()
+ .map(SysRoleDataScope::getModuleCode).collect(Collectors.toSet());
+ Map<String, String> codeToName = sysDataScopeModuleMapper.selectList(
+ new LambdaQueryWrapper<SysDataScopeModule>()
+ .in(SysDataScopeModule::getCode, moduleCodes))
+ .stream()
+ .collect(Collectors.toMap(SysDataScopeModule::getCode, SysDataScopeModule::getName));
+ List<ModuleScopeDTO> moduleScopeDTOs = roleScopes.stream()
+ .map(rs -> ModuleScopeDTO.of(
+ rs.getModuleCode(),
+ codeToName.getOrDefault(rs.getModuleCode(), rs.getModuleCode()),
+ rs.getDataScope()))
+ .collect(Collectors.toList());
+ vo.setModuleScopes(moduleScopeDTOs);
+ } else {
+ vo.setModuleScopes(Collections.emptyList());
+ }
+
return vo;
}
@@ -145,6 +224,9 @@ public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole>
// 绾ц仈娓呯悊瑙掕壊-鑿滃崟鍏宠仈
sysRoleMenuMapper.delete(
new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, roleId));
+ // 绾ц仈娓呯悊瑙掕壊脳妯″潡妗d綅
+ sysRoleDataScopeMapper.delete(
+ new LambdaQueryWrapper<SysRoleDataScope>().eq(SysRoleDataScope::getRoleId, roleId));
// 绾ц仈娓呯悊鐢ㄦ埛-瑙掕壊鍏宠仈
sysUserRoleMapper.delete(
new LambdaQueryWrapper<SysUserRole>()
diff --git a/crm-auth/src/test/java/com/crm/auth/security/scope/DataScopeIntegrationTest.java b/crm-auth/src/test/java/com/crm/auth/security/scope/DataScopeIntegrationTest.java
index 7e4ee55..eb7fc5a 100644
--- a/crm-auth/src/test/java/com/crm/auth/security/scope/DataScopeIntegrationTest.java
+++ b/crm-auth/src/test/java/com/crm/auth/security/scope/DataScopeIntegrationTest.java
@@ -15,6 +15,7 @@ import com.crm.auth.mapper.SysRoleMapper;
import com.crm.auth.mapper.SysRoleMenuMapper;
import com.crm.auth.mapper.SysUserDeptMapper;
import com.crm.auth.mapper.SysUserRoleMapper;
+import com.crm.auth.mapper.SysRoleDataScopeMapper;
import com.crm.auth.security.DataScopeInterceptor;
import com.crm.auth.security.DataScopeTables;
import com.crm.auth.security.PermissionGrant;
@@ -122,6 +123,7 @@ class DataScopeIntegrationTest {
configuration.addMapper(AuthUserMapper.class);
configuration.addMapper(SysDeptMapper.class);
configuration.addMapper(SysUserDeptMapper.class);
+ configuration.addMapper(SysRoleDataScopeMapper.class);
configuration.addMapper(TestOwnedDataMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
@@ -148,7 +150,8 @@ class DataScopeIntegrationTest {
session.getMapper(SysMenuMapper.class),
session.getMapper(AuthUserMapper.class),
deptTreeCache,
- session.getMapper(SysUserDeptMapper.class));
+ session.getMapper(SysUserDeptMapper.class),
+ session.getMapper(SysRoleDataScopeMapper.class));
testDataMapper = session.getMapper(TestOwnedDataMapper.class);
deptMapper = session.getMapper(SysDeptMapper.class);
DeptQueryCounter.COUNT.set(0);
@@ -461,7 +464,7 @@ class DataScopeIntegrationTest {
create table sys_role (
id bigint primary key, creator_id varchar(50), create_time datetime,
updater_id varchar(50), update_time datetime, deleted tinyint not null default 0,
- role_name varchar(50), role_code varchar(50), data_scope tinyint, sort int, remark varchar(200),
+ role_name varchar(50), role_code varchar(50), sort int, remark varchar(200),
builtin boolean default false)
""",
"""
@@ -495,6 +498,21 @@ class DataScopeIntegrationTest {
deny_behavior varchar(16), status varchar(16) default 'enabled', api_url varchar(200))
""",
"""
+ create table sys_data_scope_module (
+ id bigint primary key, creator_id varchar(50), create_time datetime,
+ updater_id varchar(50), update_time datetime, deleted tinyint not null default 0,
+ code varchar(50) not null unique, name varchar(100) not null, sort int default 0,
+ status varchar(10) default 'enabled', builtin boolean default false)
+ """,
+ """
+ create table sys_role_data_scope (
+ id bigint primary key, creator_id varchar(50), create_time datetime,
+ updater_id varchar(50), update_time datetime, deleted tinyint not null default 0,
+ role_id bigint not null, module_code varchar(50) not null,
+ data_scope tinyint not null,
+ constraint uk_role_module unique (role_id, module_code))
+ """,
+ """
create table sys_role_menu (
id bigint primary key, role_id bigint not null, menu_id bigint not null,
constraint uk_role_menu unique (role_id, menu_id))
@@ -505,13 +523,21 @@ class DataScopeIntegrationTest {
"insert into sys_dept (id, deleted, parent_id, dept_name) values (12, 0, 10, '鍗庡寳缁?)",
"insert into sys_dept (id, deleted, parent_id, dept_name) values (121, 0, 12, '鍗庡寳涓€闃?)",
"insert into sys_dept (id, deleted, parent_id, dept_name) values (20, 0, 0, '甯傚満閮?)",
- // 瑙掕壊锛歩d 鍗虫。浣?- "insert into sys_role (id, deleted, role_name, role_code, data_scope) values (1, 0, '浠呮湰浜?, 'SELF', 1)",
- "insert into sys_role (id, deleted, role_name, role_code, data_scope) values (2, 0, '鏈儴闂?, 'DEPT', 2)",
- "insert into sys_role (id, deleted, role_name, role_code, data_scope) values (3, 0, '鍚瓙閮ㄩ棬', 'DEPT_CHILD', 3)",
- "insert into sys_role (id, deleted, role_name, role_code, data_scope) values (4, 0, '鍏ㄩ儴', 'ALL', 4)",
+ // 鏁版嵁鏉冮檺妯″潡
+ "insert into sys_data_scope_module (id, code, name, sort, builtin) values (1, 'test', '娴嬭瘯', 1, true)",
+ // 瑙掕壊锛歩d 鍗虫。浣嶏紙涓嶅啀鏈?data_scope 鍒楋紝妗d綅瀛?sys_role_data_scope锛?+ "insert into sys_role (id, deleted, role_name, role_code) values (1, 0, '浠呮湰浜?, 'SELF')",
+ "insert into sys_role (id, deleted, role_name, role_code) values (2, 0, '鏈儴闂?, 'DEPT')",
+ "insert into sys_role (id, deleted, role_name, role_code) values (3, 0, '鍚瓙閮ㄩ棬', 'DEPT_CHILD')",
+ "insert into sys_role (id, deleted, role_name, role_code) values (4, 0, '鍏ㄩ儴', 'ALL')",
// 瑙掕壊5锛歳oleCode 鑷甫 ROLE_ 鍓嶇紑锛堥獙璇佸綊涓€骞傜瓑锛屼笉寰楀弻鍓嶇紑锛?- "insert into sys_role (id, deleted, role_name, role_code, data_scope) values (5, 0, '绠$悊鍛?, 'ROLE_ADMIN', 1)",
+ "insert into sys_role (id, deleted, role_name, role_code) values (5, 0, '绠$悊鍛?, 'ROLE_ADMIN')",
+ // 瑙掕壊脳妯″潡妗d綅锛氳鑹?id 鍗虫。浣嶅€?+ "insert into sys_role_data_scope (id, role_id, module_code, data_scope) values (1, 1, 'test', 1)",
+ "insert into sys_role_data_scope (id, role_id, module_code, data_scope) values (2, 2, 'test', 2)",
+ "insert into sys_role_data_scope (id, role_id, module_code, data_scope) values (3, 3, 'test', 3)",
+ "insert into sys_role_data_scope (id, role_id, module_code, data_scope) values (4, 4, 'test', 4)",
+ "insert into sys_role_data_scope (id, role_id, module_code, data_scope) values (5, 5, 'test', 1)",
// 鐢ㄦ埛
"insert into crm_auth_user (id, deleted, username, dept_id) values (1, 0, '浠呮湰浜?閿€鍞儴', 10)",
"insert into crm_auth_user (id, deleted, username, dept_id) values (2, 0, '鏈儴闂?閿€鍞儴', 10)",
diff --git a/crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedData.java b/crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedData.java
index 1b18b52..23bbda9 100644
--- a/crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedData.java
+++ b/crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedData.java
@@ -12,7 +12,7 @@ import lombok.EqualsAndHashCode;
*/
@Data
@EqualsAndHashCode(callSuper = true)
-@DataScope
+@DataScope(module = "test")
@TableName("test_owned_data")
public class TestOwnedData extends OwnedEntity {
diff --git a/crm-auth/src/test/java/com/crm/auth/service/impl/PermissionSeederImplTest.java b/crm-auth/src/test/java/com/crm/auth/service/impl/PermissionSeederImplTest.java
index bd66db4..174e306 100644
--- a/crm-auth/src/test/java/com/crm/auth/service/impl/PermissionSeederImplTest.java
+++ b/crm-auth/src/test/java/com/crm/auth/service/impl/PermissionSeederImplTest.java
@@ -51,7 +51,7 @@ class PermissionSeederImplTest {
create table sys_role (
id bigint primary key, creator_id varchar(50), create_time datetime,
updater_id varchar(50), update_time datetime, deleted tinyint not null default 0,
- role_name varchar(50), role_code varchar(50), data_scope tinyint, sort int, remark varchar(200),
+ role_name varchar(50), role_code varchar(50), sort int, remark varchar(200),
builtin boolean default false)
""",
"""
@@ -126,7 +126,6 @@ class PermissionSeederImplTest {
SysRole role = new SysRole();
role.setRoleName("绠$悊鍛?);
role.setRoleCode("ROLE_ADMIN");
- role.setDataScope(4);
role.setSort(0);
role.setBuiltin(true);
sysRoleMapper.insert(role);
@@ -199,7 +198,6 @@ class PermissionSeederImplTest {
SysRole role = new SysRole();
role.setRoleName("绠$悊鍛?);
role.setRoleCode("ROLE_ADMIN");
- role.setDataScope(4);
role.setSort(0);
role.setBuiltin(true);
sysRoleMapper.insert(role);
@@ -254,7 +252,6 @@ class PermissionSeederImplTest {
SysRole role = new SysRole();
role.setRoleName("绠$悊鍛?);
role.setRoleCode("ROLE_ADMIN");
- role.setDataScope(4);
role.setSort(0);
role.setBuiltin(true);
sysRoleMapper.insert(role);
@@ -353,7 +350,6 @@ class PermissionSeederImplTest {
SysRole role = new SysRole();
role.setRoleName("绠$悊鍛?);
role.setRoleCode("ROLE_ADMIN");
- role.setDataScope(4);
role.setSort(0);
role.setBuiltin(true);
sysRoleMapper.insert(role);
diff --git a/crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java b/crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java
index b633fab..9a3103a 100644
--- a/crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java
+++ b/crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java
@@ -4,6 +4,8 @@ import com.crm.auth.domain.dto.RoleDTO;
import com.crm.auth.domain.entity.SysRole;
import com.crm.auth.domain.entity.SysRoleMenu;
import com.crm.auth.domain.entity.SysUserRole;
+import com.crm.auth.mapper.SysDataScopeModuleMapper;
+import com.crm.auth.mapper.SysRoleDataScopeMapper;
import com.crm.auth.mapper.SysRoleMapper;
import com.crm.auth.mapper.SysRoleMenuMapper;
import com.crm.auth.mapper.SysUserRoleMapper;
@@ -22,7 +24,6 @@ import org.mockito.quality.Strictness;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
-import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -34,6 +35,7 @@ import static org.mockito.Mockito.when;
/**
* {@link SysRoleServiceImpl} 鍗曞厓娴嬭瘯锛氳鐩?saveRole 鏍¢獙瑙勫垯锛圓DR-0012锛?+ * <p>鍒嗘ā鍧楁敼閫犲悗绉婚櫎浜?dataScope 鑼冨洿鏍¢獙娴嬭瘯锛堟。浣嶆牎楠岀Щ鑷?saveRoleScopes锛?/p>
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -47,6 +49,10 @@ class SysRoleServiceImplTest {
private SysUserRoleMapper sysUserRoleMapper;
@Mock
private ISysMenuService sysMenuService;
+ @Mock
+ private SysRoleDataScopeMapper sysRoleDataScopeMapper;
+ @Mock
+ private SysDataScopeModuleMapper sysDataScopeModuleMapper;
@InjectMocks
private SysRoleServiceImpl sysRoleService;
@@ -62,7 +68,7 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("鍒涘缓瑙掕壊 -> roleCode 宸插瓨鍦?-> 鎷掔粷")
void createRole_duplicateCode_rejected() {
- SysRole role = buildRole(null, "閿€鍞?, "ROLE_SALES", 1);
+ SysRole role = buildRole(null, "閿€鍞?, "ROLE_SALES");
when(sysRoleMapper.selectCount(any())).thenReturn(1L);
assertThatThrownBy(() -> sysRoleService.saveRole(role))
@@ -73,7 +79,7 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("缂栬緫瑙掕壊 -> roleCode 涓嶅彉锛堝悓 id锛?-> 閫氳繃")
void editRole_sameCodeSameId_passes() {
- SysRole role = buildRole(1L, "閿€鍞?, "ROLE_SALES", 1);
+ SysRole role = buildRole(1L, "閿€鍞?, "ROLE_SALES");
when(sysRoleMapper.selectById(1L)).thenReturn(role);
when(sysRoleMapper.selectCount(any())).thenReturn(0L);
when(sysRoleMapper.updateById(any(SysRole.class))).thenReturn(1);
@@ -87,9 +93,9 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("缂栬緫鍐呯疆瑙掕壊 -> 淇敼 roleCode -> 鎷掔粷")
void editBuiltinRole_changeCode_rejected() {
- SysRole existing = buildRole(1L, "绠$悊鍛?, "ROLE_ADMIN", 4);
+ SysRole existing = buildRole(1L, "绠$悊鍛?, "ROLE_ADMIN");
existing.setBuiltin(true);
- SysRole input = buildRole(1L, "绠$悊鍛?, "ROLE_SUPER", 4);
+ SysRole input = buildRole(1L, "绠$悊鍛?, "ROLE_SUPER");
when(sysRoleMapper.selectById(1L)).thenReturn(existing);
assertThatThrownBy(() -> sysRoleService.saveRole(input))
@@ -100,9 +106,9 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("缂栬緫鍐呯疆瑙掕壊 -> roleCode 涓嶅彉 -> 閫氳繃")
void editBuiltinRole_sameCode_passes() {
- SysRole existing = buildRole(1L, "绠$悊鍛?, "ROLE_ADMIN", 4);
+ SysRole existing = buildRole(1L, "绠$悊鍛?, "ROLE_ADMIN");
existing.setBuiltin(true);
- SysRole input = buildRole(1L, "绠$悊鍛樻敼", "ROLE_ADMIN", 4);
+ SysRole input = buildRole(1L, "绠$悊鍛樻敼", "ROLE_ADMIN");
when(sysRoleMapper.selectById(1L)).thenReturn(existing);
when(sysRoleMapper.selectCount(any())).thenReturn(0L);
when(sysRoleMapper.updateById(any(SysRole.class))).thenReturn(1);
@@ -114,7 +120,7 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("鍒涘缓瑙掕壊 -> 璁剧疆 builtin=true -> 鎷掔粷")
void createRole_setBuiltin_rejected() {
- SysRole role = buildRole(null, "鍋囩鐞嗗憳", "ROLE_FAKE", 4);
+ SysRole role = buildRole(null, "鍋囩鐞嗗憳", "ROLE_FAKE");
role.setBuiltin(true);
assertThatThrownBy(() -> sysRoleService.saveRole(role))
@@ -125,7 +131,7 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("缂栬緫涓嶅瓨鍦ㄧ殑 id -> 璁剧疆 builtin=true -> 鎷掔粷锛堥槻姝?saveOrUpdate 鐨?insert 缁曡繃锛?)
void editNonExistentRole_setBuiltin_rejected() {
- SysRole role = buildRole(999L, "鍋囩鐞嗗憳", "ROLE_FAKE", 4);
+ SysRole role = buildRole(999L, "鍋囩鐞嗗憳", "ROLE_FAKE");
role.setBuiltin(true);
when(sysRoleMapper.selectById(999L)).thenReturn(null);
@@ -134,45 +140,12 @@ class SysRoleServiceImplTest {
.hasMessageContaining("涓嶅厑璁搁€氳繃鎺ュ彛鍒涘缓鍐呯疆瑙掕壊");
}
- // ==================== dataScope 鑼冨洿鏍¢獙 ====================
-
- @Test
- @DisplayName("鍒涘缓瑙掕壊 -> dataScope=0锛堣秺鐣岋級 -> 鎷掔粷")
- void createRole_dataScopeTooLow_rejected() {
- SysRole role = buildRole(null, "閿€鍞?, "ROLE_SALES", 0);
-
- assertThatThrownBy(() -> sysRoleService.saveRole(role))
- .isInstanceOf(BusinessErrorException.class)
- .hasMessageContaining("鏁版嵁鑼冨洿涓嶅悎娉?);
- }
-
- @Test
- @DisplayName("鍒涘缓瑙掕壊 -> dataScope=5锛堣秺鐣岋級 -> 鎷掔粷")
- void createRole_dataScopeTooHigh_rejected() {
- SysRole role = buildRole(null, "閿€鍞?, "ROLE_SALES", 5);
-
- assertThatThrownBy(() -> sysRoleService.saveRole(role))
- .isInstanceOf(BusinessErrorException.class)
- .hasMessageContaining("鏁版嵁鑼冨洿涓嶅悎娉?);
- }
-
- @Test
- @DisplayName("鍒涘缓瑙掕壊 -> dataScope=3锛堝悎娉曪級 -> 閫氳繃")
- void createRole_validDataScope_passes() {
- SysRole role = buildRole(null, "閿€鍞?, "ROLE_SALES", 3);
- when(sysRoleMapper.selectCount(any())).thenReturn(0L);
- when(sysRoleMapper.insert(any(SysRole.class))).thenReturn(1);
-
- assertThatCode(() -> sysRoleService.saveRole(role))
- .doesNotThrowAnyException();
- }
-
// ==================== 鍩烘湰闈炵┖鏍¢獙 ====================
@Test
@DisplayName("鍒涘缓瑙掕壊 -> roleName 涓虹┖ -> 鎷掔粷")
void createRole_blankName_rejected() {
- SysRole role = buildRole(null, "", "ROLE_SALES", 1);
+ SysRole role = buildRole(null, "", "ROLE_SALES");
assertThatThrownBy(() -> sysRoleService.saveRole(role))
.isInstanceOf(BusinessErrorException.class)
@@ -182,7 +155,7 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("鍒涘缓瑙掕壊 -> roleCode 涓虹┖ -> 鎷掔粷")
void createRole_blankCode_rejected() {
- SysRole role = buildRole(null, "閿€鍞?, "", 1);
+ SysRole role = buildRole(null, "閿€鍞?, "");
assertThatThrownBy(() -> sysRoleService.saveRole(role))
.isInstanceOf(BusinessErrorException.class)
@@ -196,7 +169,7 @@ class SysRoleServiceImplTest {
void assignResources_ancestorCompletion_writesAllIds() {
// 杈撳叆鍙紶鍙跺瓙鑺傜偣 100L锛岀鍏堣ˉ鍏ㄥ悗搴斿寘鍚?100L + 10L + 1L
when(sysMenuService.getAncestorIds(List.of(100L)))
- .thenReturn(Set.of(100L, 10L, 1L));
+ .thenReturn(java.util.Set.of(100L, 10L, 1L));
when(sysRoleMenuMapper.delete(any())).thenReturn(1);
when(sysRoleMenuMapper.insert(any(SysRoleMenu.class))).thenReturn(1);
@@ -216,7 +189,7 @@ class SysRoleServiceImplTest {
@DisplayName("assignResources -> 鍏堝垹鍚庢彃鍏ㄩ噺鏇挎崲 sys_role_menu")
void assignResources_fullReplacement() {
when(sysMenuService.getAncestorIds(List.of(200L)))
- .thenReturn(Set.of(200L));
+ .thenReturn(java.util.Set.of(200L));
when(sysRoleMenuMapper.delete(any())).thenReturn(1);
when(sysRoleMenuMapper.insert(any(SysRoleMenu.class))).thenReturn(1);
@@ -234,13 +207,14 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("getRoleDetail -> 杩斿洖 RoleDTO 鍚熀鏈俊鎭拰 resourceIds")
void getRoleDetail_returnsVO() {
- SysRole role = buildRole(1L, "閿€鍞?, "ROLE_SALES", 1);
+ SysRole role = buildRole(1L, "閿€鍞?, "ROLE_SALES");
when(sysRoleMapper.selectById(1L)).thenReturn(role);
SysRoleMenu rm1 = new SysRoleMenu();
rm1.setMenuId(100L);
SysRoleMenu rm2 = new SysRoleMenu();
rm2.setMenuId(200L);
when(sysRoleMenuMapper.selectList(any())).thenReturn(List.of(rm1, rm2));
+ when(sysRoleDataScopeMapper.selectList(any())).thenReturn(List.of());
RoleDTO vo = sysRoleService.getRoleDetail(1L);
@@ -265,7 +239,7 @@ class SysRoleServiceImplTest {
@Test
@DisplayName("鍒犻櫎鍐呯疆瑙掕壊 -> builtin=true -> 鎷掔粷")
void deleteBuiltinRole_rejected() {
- SysRole role = buildRole(1L, "绠$悊鍛?, "ROLE_ADMIN", 4);
+ SysRole role = buildRole(1L, "绠$悊鍛?, "ROLE_ADMIN");
role.setBuiltin(true);
when(sysRoleMapper.selectById(1L)).thenReturn(role);
@@ -275,12 +249,13 @@ class SysRoleServiceImplTest {
}
@Test
- @DisplayName("鍒犻櫎鏅€氳鑹?-> builtin=false -> 閫氳繃骞舵竻鐞?sys_role_menu + sys_user_role")
+ @DisplayName("鍒犻櫎鏅€氳鑹?-> builtin=false -> 閫氳繃骞舵竻鐞?sys_role_menu + sys_role_data_scope + sys_user_role")
void deleteNormalRole_passes() {
- SysRole role = buildRole(2L, "閿€鍞?, "ROLE_SALES", 1);
+ SysRole role = buildRole(2L, "閿€鍞?, "ROLE_SALES");
when(sysRoleMapper.selectById(2L)).thenReturn(role);
when(sysRoleMenuMapper.delete(any())).thenReturn(1);
when(sysUserRoleMapper.delete(any())).thenReturn(1);
+ when(sysRoleDataScopeMapper.delete(any())).thenReturn(1);
assertThatCode(() -> sysRoleService.deleteRoleCascade(2L))
.doesNotThrowAnyException();
@@ -288,12 +263,11 @@ class SysRoleServiceImplTest {
// ==================== 妗╂暟鎹?====================
- private SysRole buildRole(Long id, String name, String code, int dataScope) {
+ private SysRole buildRole(Long id, String name, String code) {
SysRole role = new SysRole();
role.setId(id);
role.setRoleName(name);
role.setRoleCode(code);
- role.setDataScope(dataScope);
role.setSort(0);
return role;
}
diff --git a/crm-auth/src/test/java/com/crm/auth/service/impl/UserListIntegrationTest.java b/crm-auth/src/test/java/com/crm/auth/service/impl/UserListIntegrationTest.java
index 0ee3dc5..70f9d94 100644
--- a/crm-auth/src/test/java/com/crm/auth/service/impl/UserListIntegrationTest.java
+++ b/crm-auth/src/test/java/com/crm/auth/service/impl/UserListIntegrationTest.java
@@ -332,7 +332,7 @@ class UserListIntegrationTest {
create table sys_role (
id bigint primary key, creator_id varchar(50), create_time datetime,
updater_id varchar(50), update_time datetime, deleted tinyint not null default 0,
- role_name varchar(50), role_code varchar(50), data_scope tinyint, sort int, remark varchar(200),
+ role_name varchar(50), role_code varchar(50), sort int, remark varchar(200),
builtin boolean default false)
""",
"""
@@ -357,8 +357,8 @@ class UserListIntegrationTest {
"insert into sys_dept (id, deleted, parent_id, dept_name) values (121, 0, 12, '鍗庡寳涓€闃?)",
"insert into sys_dept (id, deleted, parent_id, dept_name) values (20, 0, 0, '甯傚満閮?)",
// 瑙掕壊
- "insert into sys_role (id, deleted, role_name, role_code, data_scope) values (1, 0, '绠$悊鍛?, 'ROLE_ADMIN', 4)",
- "insert into sys_role (id, deleted, role_name, role_code, data_scope) values (2, 0, '閿€鍞?, 'ROLE_SALES', 2)",
+ "insert into sys_role (id, deleted, role_name, role_code) values (1, 0, '绠$悊鍛?, 'ROLE_ADMIN')",
+ "insert into sys_role (id, deleted, role_name, role_code) values (2, 0, '閿€鍞?, 'ROLE_SALES')",
// 鐢ㄦ埛锛坋mployment_status / title / account / mobile / last_login_time / dept_id / enabled锛? "insert into crm_auth_user (id, deleted, username, mobile, enabled, last_login_time, dept_id, employment_status, title, account) values (1, 0, '寮犱笁', '13800000001', 1, '2024-01-15 10:00:00', 10, 'active', '閿€鍞€荤洃', 'zhangsan')",
"insert into crm_auth_user (id, deleted, username, mobile, enabled, last_login_time, dept_id, employment_status, title, account) values (2, 0, '鏉庡洓', '13800000002', 1, '2024-03-20 14:00:00', 10, 'active', '閿€鍞粡鐞?, 'lisi')",
diff --git a/crm-base/src/main/java/com/crm/base/annotation/DataScope.java b/crm-base/src/main/java/com/crm/base/annotation/DataScope.java
index 8ccaf8b..a796023 100644
--- a/crm-base/src/main/java/com/crm/base/annotation/DataScope.java
+++ b/crm-base/src/main/java/com/crm/base/annotation/DataScope.java
@@ -3,13 +3,19 @@ package com.crm.base.annotation;
import java.lang.annotation.*;
/**
- * 鏁版嵁鏉冮檺娉ㄨВ锛氭爣娉ㄥ湪瀹炰綋绫讳笂锛屽憡鐭?DataScopeInterceptor 璇ヨ〃鐢ㄤ簬鏁版嵁杩囨护鐨勫垪鍚嶃€?+ * 鏁版嵁鏉冮檺娉ㄨВ锛氭爣娉ㄥ湪瀹炰綋绫讳笂锛屽憡鐭?DataScopeInterceptor 璇ヨ〃鐢ㄤ簬鏁版嵁杩囨护鐨勫垪鍚嶅拰鎵€灞炰笟鍔℃ā鍧椼€? * 鏈爣娉ㄦ娉ㄨВ鐨勫疄浣撲笉浼氳Е鍙戞暟鎹潈闄愯繃婊ゃ€? */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {
+ /**
+ * 鎵€灞炴暟鎹潈闄愭ā鍧楃殑 code锛堝 "customer"銆?lead"锛夛紝寮曠敤 sys_data_scope_module.code銆?+ * 蹇呭~鈥斺€斿惎鍔ㄦ椂 PermissionConfig 鏍¢獙璇?code 蹇呴』鍦ㄦ敞鍐岃〃涓瓨鍦紝涓嶅瓨鍦ㄥ垯鎷掔粷鍚姩锛圓DR-0006锛夈€?+ */
+ String module();
+
/** 褰掑睘浜哄瓧娈碉紙鏁版嵁搴撳垪鍚嶏級锛屽搴?浠呮湰浜?杩囨护 */
String ownerColumn() default "owner_id";
diff --git a/crm-base/src/main/java/com/crm/base/security/DataVisibility.java b/crm-base/src/main/java/com/crm/base/security/DataVisibility.java
index c0501f7..fbf4664 100644
--- a/crm-base/src/main/java/com/crm/base/security/DataVisibility.java
+++ b/crm-base/src/main/java/com/crm/base/security/DataVisibility.java
@@ -1,6 +1,7 @@
package com.crm.base.security;
import java.util.List;
+import java.util.Map;
/**
* 鏁版嵁鍙鎬ц寖鍥达細褰撳墠韬唤鑳界湅鍒颁粈涔堛€佸啓鍑虹殑鏁版嵁褰掕皝銆?@@ -8,51 +9,64 @@ import java.util.List;
* <p>涓嶅彲鍙橈紝涓€娆℃€ф暣浣撹鍏ワ細鏋勯€犳垚鍔熷嵆鎰忓懗鐫€涓婁笅鏂囧畬鏁村彲鐢紝涓嶅瓨鍦ㄣ€岃浜嗕竴鍗娿€嶇殑鐘舵€侊紝
* 鍥犳涔熶笉瀛樺湪銆屽繕浜嗚妗d綅鎵€浠ユ斁琛屽叏閮ㄦ暟鎹€嶈繖绉嶈〃杈撅紙瑙?ADR 0006锛夈€?/p>
*
- * <p>瀵瑰鍙洖绛斾袱涓棶鍙ワ細璇讳晶闂?{@link #visibilityScope()}锛屽啓渚ч棶 {@link #dataOwnership()}銆?- * 妗d綅鏁板瓧涓庨儴闂ㄩ泦鍚堢殑鍙栬垗鏄唴閮ㄥ疄鐜扮粏鑺傘€?/p>
+ * <p>瀵瑰鍙洖绛斾袱涓棶鍙ワ細璇讳晶闂?{@link #visibilityScope(String)}锛堟寜妯″潡鍙栨。浣嶏級锛?+ * 鍐欎晶闂?{@link #dataOwnership()}銆傛。浣嶆暟瀛椾笌閮ㄩ棬闆嗗悎鐨勫彇鑸嶆槸鍐呴儴瀹炵幇缁嗚妭銆?/p>
+ *
+ * <p>鍒嗘ā鍧楁敼閫犲悗锛岃韩浠芥暟鎹紙userId / primaryDeptId / deptIds / expandedDeptIds锛夊彧瀛樹竴浠斤紝
+ * 鍚勬ā鍧楁。浣嶉€氳繃 {@code moduleLevels} map 鍚勮嚜鐙珛銆傞儴闂ㄩ泦鍚堝彧绠椾竴娆★紝鍚勬ā鍧楁。浣嶅喅瀹?+ * <b>鐢ㄤ笉鐢?/b> 杩欑粍閮ㄩ棬 ID锛屼笉闇€瑕侀噸绠椼€?/p>
*/
public final class DataVisibility {
private final Long userId;
private final Long primaryDeptId;
private final List<Long> deptIds;
- private final DataScopeLevel level;
+ private final Map<String, DataScopeLevel> moduleLevels;
private final List<Long> expandedDeptIds;
/**
* @param userId 褰撳墠鐢ㄦ埛 ID锛屽繀濉? * @param primaryDeptId 涓婚儴闂?ID锛屽彲涓?null锛堢敤鎴峰皻鏈綊浣嶇粍缁囨灦鏋勶級
* @param deptIds 閮ㄩ棬闆嗗悎锛堜富 + 鍏艰亴骞堕泦锛夛紝鍙负绌洪泦鍚堬紙璇ョ敤鎴蜂笉灞炰簬浠讳綍閮ㄩ棬锛?- * @param level 鏁版嵁鑼冨洿妗d綅锛屽繀濉?- * @param expandedDeptIds 閮ㄩ棬闆嗗悎閫愪釜灞曞紑瀛愭爲鍚庣殑骞堕泦锛屼粎 {@link DataScopeLevel#DEPT_AND_CHILDREN}
- * 妗d綅闇€瑕侊紝姝ゆ椂蹇呭~锛堝彲涓虹┖闆嗗悎锛岃〃绀轰竴寰嬩笉鍙锛夛紱鍏朵粬妗d綅蹇界暐
- * @throws IllegalArgumentException 鐢ㄦ埛 ID 鎴栨。浣嶇己澶憋紝鎴栧惈涓嬪睘閮ㄩ棬妗d綅鏈惡甯﹀睍寮€闆嗗悎
+ * @param moduleLevels 姣忔ā鍧楃殑鏁版嵁鑼冨洿妗d綅锛坘ey = module code锛寁alue = 妗d綅锛夛紝蹇呭~浣嗗厑璁哥┖ Map
+ * 锛堢┖ Map = 鎵€鏈夋ā鍧楄蛋榛樿 SELF锛夛紱鏈湪 map 涓殑妯″潡涔熻蛋 SELF
+ * @param expandedDeptIds 閮ㄩ棬闆嗗悎閫愪釜灞曞紑瀛愭爲鍚庣殑骞堕泦锛屼粎褰撲换涓€妯″潡鐨勬。浣嶄负
+ * {@link DataScopeLevel#DEPT_AND_CHILDREN} 鏃堕渶瑕侊紙蹇呭~锛屽彲涓虹┖闆嗗悎锛?+ * 琛ㄧず涓€寰嬩笉鍙锛夛紱鍏ㄦā鍧楅兘涓嶅惈璇ユ。浣嶆椂蹇界暐
+ * @throws IllegalArgumentException 鐢ㄦ埛 ID 缂哄け銆乵oduleLevels 涓?null銆佹垨鍚笅灞為儴闂ㄦ。浣嶄絾鏈惡甯﹀睍寮€闆嗗悎
*/
public DataVisibility(Long userId, Long primaryDeptId, List<Long> deptIds,
- DataScopeLevel level, List<Long> expandedDeptIds) {
+ Map<String, DataScopeLevel> moduleLevels, List<Long> expandedDeptIds) {
if (userId == null) {
throw new IllegalArgumentException("鏁版嵁鍙鎬ц寖鍥寸己灏戠敤鎴?ID");
}
- if (level == null) {
- throw new IllegalArgumentException("鏁版嵁鍙鎬ц寖鍥寸己灏戞暟鎹寖鍥存。浣?);
+ if (moduleLevels == null) {
+ throw new IllegalArgumentException("鏁版嵁鍙鎬ц寖鍥寸己灏戞ā鍧楁。浣嶆槧灏?);
}
- if (level == DataScopeLevel.DEPT_AND_CHILDREN && expandedDeptIds == null) {
+ boolean needsExpanded = moduleLevels.values().stream()
+ .anyMatch(l -> l == DataScopeLevel.DEPT_AND_CHILDREN);
+ if (needsExpanded && expandedDeptIds == null) {
throw new IllegalArgumentException(
- "妗d綅 " + level + " 瑕佹眰鎼哄甫灞曞紑瀛愭爲鍚庣殑閮ㄩ棬闆嗗悎锛岀┖闆嗗悎璇蜂紶绌?List 鑰岄潪 null");
+ "瀛樺湪妯″潡妗d綅涓?DEPT_AND_CHILDREN锛岃姹傛惡甯﹀睍寮€瀛愭爲鍚庣殑閮ㄩ棬闆嗗悎锛岀┖闆嗗悎璇蜂紶绌?List 鑰岄潪 null");
}
this.userId = userId;
this.primaryDeptId = primaryDeptId;
this.deptIds = deptIds == null ? List.of() : List.copyOf(deptIds);
- this.level = level;
+ this.moduleLevels = Map.copyOf(moduleLevels);
this.expandedDeptIds = expandedDeptIds == null ? null : List.copyOf(expandedDeptIds);
}
/**
- * 鏈鏌ヨ璇ュ浣曡繃婊ゃ€?+ * 鏈鏌ヨ璇ュ浣曡繃婊わ紙鎸夋ā鍧楀彇妗d綅 鈫?鎶樼畻 VisibilityScope锛夈€?+ *
+ * <p>鏈厤妯″潡锛坢ap 涓煡涓嶅埌锛夐粯璁?SELF锛堟渶绐勬。锛宖ail safe锛?2 鍐崇瓥 3锛夈€?/p>
*
* <p>鎸夐儴闂ㄨ繃婊ょ殑妗d綅閬囦笂绌洪儴闂ㄩ泦鍚堟椂鍒や负銆屼竴寰嬩笉鍙銆嶁€斺€旀棤褰掑睘涓嶇瓑浜庡叏鏀捐銆?/p>
+ *
+ * @param moduleCode 鏁版嵁鏉冮檺妯″潡 code
*/
- public VisibilityScope visibilityScope() {
+ public VisibilityScope visibilityScope(String moduleCode) {
+ DataScopeLevel level = moduleLevels.getOrDefault(moduleCode, DataScopeLevel.SELF);
return switch (level) {
case SELF -> VisibilityScope.ownedBy(userId);
case DEPT -> departmentsOrNone(deptIds);
diff --git a/crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java b/crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java
index a1d087c..757c7a8 100644
--- a/crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java
+++ b/crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java
@@ -6,9 +6,13 @@ import java.util.Optional;
* 褰撳墠璇锋眰鐨勬暟鎹彲瑙佹€ц寖鍥淬€? *
* <p>璁よ瘉閫氳繃鍚庣敱杩囨护鍣?{@link #load} 瑁呭叆锛岃姹傜粨鏉熸椂 {@link #clear} 娓呯悊锛?- * 鏁版嵁鏉冮檺鎷︽埅鍣ㄤ笌鍏叡瀛楁濉厖鍣ㄥ垎鍒€氳繃 {@link #currentScope()} 涓?{@link #currentOwnership()}
+ * 鏁版嵁鏉冮檺鎷︽埅鍣ㄤ笌鍏叡瀛楁濉厖鍣ㄥ垎鍒€氳繃 {@link #currentScope(String)} 涓?{@link #currentOwnership()}
* 璇诲彇锛屼袱渚х湅鍒扮殑鏄悓涓€浠借韩浠姐€?/p>
*
+ * <p>鍒嗘ā鍧楁敼閫犲悗锛岃渚?{@link #currentScope(String)} 闇€浼犲叆妯″潡 code锛?+ * 浠庡悓涓€浠?DataVisibility 涓寜妯″潡鍙栨。浣嶃€傚啓渚?{@link #currentOwnership()} 涓嶅彉鈥斺€?+ * 韬唤缁村害涓庢。浣嶇淮搴︽浜わ紙06 鍐崇瓥锛夈€?/p>
+ *
* <p>鏃犱笂涓嬫枃锛堝簲鐢ㄨ嚜韬殑绾跨▼銆佹湭璁よ瘉璇锋眰锛夋椂涓嶈繃婊ゃ€佷篃鏃犲綊灞炲彲濉細鑳藉湪鏃犵櫥褰曚俊鎭笅璺戞煡璇㈢殑
* 鍙湁搴旂敤鑷韩鐨勭嚎绋嬶紝鏈璇佺殑 HTTP 璇锋眰杩涗笉鍒颁笟鍔℃煡璇紙瑙?ADR 0006锛夈€?/p>
*
@@ -30,10 +34,16 @@ public final class DataVisibilityContext {
CURRENT.remove();
}
- /** 鏈鏌ヨ璇ュ浣曡繃婊わ紱鏃犱笂涓嬫枃鏃朵笉杩囨护 */
- public static VisibilityScope currentScope() {
+ /**
+ * 鏈鏌ヨ璇ュ浣曡繃婊わ紙鎸夋ā鍧楀彇妗d綅锛夛紱鏃犱笂涓嬫枃鏃朵笉杩囨护銆?+ *
+ * @param moduleCode 鏁版嵁鏉冮檺妯″潡 code
+ */
+ public static VisibilityScope currentScope(String moduleCode) {
DataVisibility visibility = CURRENT.get();
- return visibility != null ? visibility.visibilityScope() : VisibilityScope.allVisible();
+ return visibility != null
+ ? visibility.visibilityScope(moduleCode)
+ : VisibilityScope.allVisible();
}
/** 鏂板啓鍏ユ暟鎹殑褰掑睘锛涙棤涓婁笅鏂囨椂涓虹┖锛屽綊灞炲瓧娈典繚鎸?null */
diff --git a/crm-base/src/test/java/com/crm/base/security/DataVisibilityTest.java b/crm-base/src/test/java/com/crm/base/security/DataVisibilityTest.java
index c76eba1..3b62a05 100644
--- a/crm-base/src/test/java/com/crm/base/security/DataVisibilityTest.java
+++ b/crm-base/src/test/java/com/crm/base/security/DataVisibilityTest.java
@@ -5,6 +5,7 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.List;
+import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@@ -13,11 +14,15 @@ import static org.junit.jupiter.api.Assertions.*;
*
* <p>瑕嗙洊鍥涗釜鏁版嵁鑼冨洿妗d綅濡備綍鎶樼畻涓鸿繃婊ゆ儏褰€侀儴闂ㄩ泦鍚堜负绌烘椂鐨勩€屼竴寰嬩笉鍙銆嶃€? * 瑁呭叆鏃剁殑瀹屾暣鎬ф牎楠岋紝浠ュ強鏃犱笂涓嬫枃鏃剁殑涓ゆ。 fail-closed 璇箟锛堣 ADR 0006锛夈€?/p>
+ *
+ * <p>鍒嗘ā鍧楁敼閫犲悗锛孌ataVisibility 鎸佹湁 {@code Map<String, DataScopeLevel>} 鏇夸唬鍗曚釜 level锛?+ * 娴嬭瘯鐢?{@code Map.of("test", level)} 妯℃嫙鍗曟ā鍧楀満鏅€?/p>
*/
class DataVisibilityTest {
private static final Long USER_ID = 1001L;
private static final Long PRIMARY_DEPT = 200L;
+ private static final String MODULE = "test";
@AfterEach
void tearDown() {
@@ -30,9 +35,10 @@ class DataVisibilityTest {
@DisplayName("浠呮湰浜烘。浣嶏細鎸夊綊灞炰汉杩囨护")
void selfLevelFiltersByOwner() {
DataVisibility visibility = new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT), DataScopeLevel.SELF, null);
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(MODULE, DataScopeLevel.SELF), null);
- VisibilityScope scope = visibility.visibilityScope();
+ VisibilityScope scope = visibility.visibilityScope(MODULE);
assertEquals(VisibilityScope.Kind.OWNER, scope.kind());
assertEquals(USER_ID, scope.ownerId());
@@ -42,9 +48,10 @@ class DataVisibilityTest {
@DisplayName("鏈儴闂ㄦ。浣嶏細鎸夐儴闂ㄩ泦鍚堬紙涓?+ 鍏艰亴骞堕泦锛夎繃婊?)
void deptLevelFiltersByDeptSet() {
DataVisibility visibility = new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT, 300L), DataScopeLevel.DEPT, null);
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT, 300L),
+ Map.of(MODULE, DataScopeLevel.DEPT), null);
- VisibilityScope scope = visibility.visibilityScope();
+ VisibilityScope scope = visibility.visibilityScope(MODULE);
assertEquals(VisibilityScope.Kind.DEPARTMENTS, scope.kind());
assertEquals(List.of(PRIMARY_DEPT, 300L), scope.deptIds());
@@ -54,10 +61,11 @@ class DataVisibilityTest {
@DisplayName("鏈儴闂ㄥ強涓嬪睘閮ㄩ棬妗d綅锛氭寜灞曞紑鍚庣殑閮ㄩ棬闆嗗悎杩囨护锛屼笉鐢ㄦ湭灞曞紑鐨勯泦鍚?)
void deptAndChildrenLevelFiltersByExpandedDeptSet() {
DataVisibility visibility = new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT), DataScopeLevel.DEPT_AND_CHILDREN,
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(MODULE, DataScopeLevel.DEPT_AND_CHILDREN),
List.of(PRIMARY_DEPT, 201L, 202L));
- VisibilityScope scope = visibility.visibilityScope();
+ VisibilityScope scope = visibility.visibilityScope(MODULE);
assertEquals(VisibilityScope.Kind.DEPARTMENTS, scope.kind());
assertEquals(List.of(PRIMARY_DEPT, 201L, 202L), scope.deptIds());
@@ -67,9 +75,10 @@ class DataVisibilityTest {
@DisplayName("鍏ㄩ儴鍙妗d綅锛氫笉杩囨护")
void allLevelDoesNotFilter() {
DataVisibility visibility = new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT), DataScopeLevel.ALL, null);
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(MODULE, DataScopeLevel.ALL), null);
- assertEquals(VisibilityScope.Kind.ALL_VISIBLE, visibility.visibilityScope().kind());
+ assertEquals(VisibilityScope.Kind.ALL_VISIBLE, visibility.visibilityScope(MODULE).kind());
}
// 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€ 涓€寰嬩笉鍙 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
@@ -78,25 +87,27 @@ class DataVisibilityTest {
@DisplayName("鏈儴闂ㄦ。浣嶄絾閮ㄩ棬闆嗗悎涓虹┖锛氫竴寰嬩笉鍙锛岃€岄潪鏀捐")
void deptLevelWithoutAnyDeptIsNoneVisible() {
DataVisibility visibility = new DataVisibility(
- USER_ID, null, List.of(), DataScopeLevel.DEPT, null);
+ USER_ID, null, List.of(),
+ Map.of(MODULE, DataScopeLevel.DEPT), null);
- assertEquals(VisibilityScope.Kind.NONE_VISIBLE, visibility.visibilityScope().kind());
+ assertEquals(VisibilityScope.Kind.NONE_VISIBLE, visibility.visibilityScope(MODULE).kind());
}
@Test
@DisplayName("鍚笅灞為儴闂ㄦ。浣嶄絾灞曞紑缁撴灉涓虹┖锛氫竴寰嬩笉鍙锛岃€岄潪鏀捐")
void deptAndChildrenLevelWithEmptyExpansionIsNoneVisible() {
DataVisibility visibility = new DataVisibility(
- USER_ID, null, List.of(), DataScopeLevel.DEPT_AND_CHILDREN, List.of());
+ USER_ID, null, List.of(),
+ Map.of(MODULE, DataScopeLevel.DEPT_AND_CHILDREN), List.of());
- assertEquals(VisibilityScope.Kind.NONE_VISIBLE, visibility.visibilityScope().kind());
+ assertEquals(VisibilityScope.Kind.NONE_VISIBLE, visibility.visibilityScope(MODULE).kind());
}
// 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€ 瑁呭叆鏃剁殑瀹屾暣鎬ф牎楠?鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
@Test
- @DisplayName("妗d綅涓?null锛氳鍏ュ嵆澶辫触")
- void nullLevelIsRejected() {
+ @DisplayName("moduleLevels 涓?null锛氳鍏ュ嵆澶辫触")
+ void nullModuleLevelsIsRejected() {
assertThrows(IllegalArgumentException.class, () -> new DataVisibility(
USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT), null, null));
}
@@ -105,14 +116,16 @@ class DataVisibilityTest {
@DisplayName("鍚笅灞為儴闂ㄦ。浣嶆湭鎼哄甫灞曞紑闆嗗悎锛氳鍏ュ嵆澶辫触锛堥殣鍚害瀹氬彉涓烘樉寮忎笉鍙橀噺锛?)
void deptAndChildrenLevelWithoutExpansionIsRejected() {
assertThrows(IllegalArgumentException.class, () -> new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT), DataScopeLevel.DEPT_AND_CHILDREN, null));
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(MODULE, DataScopeLevel.DEPT_AND_CHILDREN), null));
}
@Test
@DisplayName("鐢ㄦ埛 ID 缂哄け锛氳鍏ュ嵆澶辫触")
void missingUserIdIsRejected() {
assertThrows(IllegalArgumentException.class, () -> new DataVisibility(
- null, PRIMARY_DEPT, List.of(PRIMARY_DEPT), DataScopeLevel.SELF, null));
+ null, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(MODULE, DataScopeLevel.SELF), null));
}
@Test
@@ -131,13 +144,49 @@ class DataVisibilityTest {
}
}
+ // 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€ 鍒嗘ā鍧楃壒鏈?鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
+
+ @Test
+ @DisplayName("鏈厤妯″潡锛氶粯璁?SELF锛堟渶绐勬。锛宖ail safe锛?)
+ void unconfiguredModuleDefaultsToSelf() {
+ DataVisibility visibility = new DataVisibility(
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of("other_module", DataScopeLevel.ALL), null);
+
+ // "test" 妯″潡鏈厤锛屽簲璧?SELF
+ assertEquals(VisibilityScope.Kind.OWNER, visibility.visibilityScope(MODULE).kind());
+ assertEquals(USER_ID, visibility.visibilityScope(MODULE).ownerId());
+ }
+
+ @Test
+ @DisplayName("绌?moduleLevels Map锛氭墍鏈夋ā鍧楄蛋 SELF")
+ void emptyModuleLevelsDefaultsAllToSelf() {
+ DataVisibility visibility = new DataVisibility(
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(), null);
+
+ assertEquals(VisibilityScope.Kind.OWNER, visibility.visibilityScope(MODULE).kind());
+ }
+
+ @Test
+ @DisplayName("澶氭ā鍧楀悇鍙栧悇鐨勬。浣嶏細customer=ALL, lead=SELF")
+ void multiModuleIndependentLevels() {
+ DataVisibility visibility = new DataVisibility(
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of("customer", DataScopeLevel.ALL, "lead", DataScopeLevel.SELF), null);
+
+ assertEquals(VisibilityScope.Kind.ALL_VISIBLE, visibility.visibilityScope("customer").kind());
+ assertEquals(VisibilityScope.Kind.OWNER, visibility.visibilityScope("lead").kind());
+ }
+
// 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€ 鍐欎晶鏁版嵁褰掑睘 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
@Test
@DisplayName("鏁版嵁褰掑睘鍙栦富閮ㄩ棬锛屼笉浠庡吋鑱岄泦鍚堥噷鎸戯紙鍙鎸傞潬锛?)
void ownershipUsesPrimaryDeptOnly() {
DataVisibility visibility = new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT, 300L), DataScopeLevel.DEPT, null);
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT, 300L),
+ Map.of(MODULE, DataScopeLevel.DEPT), null);
DataOwnership ownership = visibility.dataOwnership();
@@ -149,7 +198,8 @@ class DataVisibilityTest {
@DisplayName("鏃犱富閮ㄩ棬鏃跺綊灞為儴闂ㄤ负 null锛屽綊灞炰汉浠嶅~褰撳墠鐢ㄦ埛")
void ownershipToleratesMissingPrimaryDept() {
DataVisibility visibility = new DataVisibility(
- USER_ID, null, List.of(), DataScopeLevel.SELF, null);
+ USER_ID, null, List.of(),
+ Map.of(MODULE, DataScopeLevel.SELF), null);
DataOwnership ownership = visibility.dataOwnership();
@@ -162,7 +212,7 @@ class DataVisibilityTest {
@Test
@DisplayName("鏃犱笂涓嬫枃锛氫笉杩囨护锛屼笖鍐欎晶鏃犲綊灞炲彲濉?)
void withoutContextNothingIsFilteredOrFilled() {
- assertEquals(VisibilityScope.Kind.ALL_VISIBLE, DataVisibilityContext.currentScope().kind());
+ assertEquals(VisibilityScope.Kind.ALL_VISIBLE, DataVisibilityContext.currentScope(MODULE).kind());
assertTrue(DataVisibilityContext.currentOwnership().isEmpty());
}
@@ -170,9 +220,10 @@ class DataVisibilityTest {
@DisplayName("瑁呭叆鍚庤鍐欎袱渚х湅鍒板悓涓€浠借韩浠?)
void loadedContextIsVisibleToBothSides() {
DataVisibilityContext.load(new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT), DataScopeLevel.SELF, null));
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(MODULE, DataScopeLevel.SELF), null));
- assertEquals(USER_ID, DataVisibilityContext.currentScope().ownerId());
+ assertEquals(USER_ID, DataVisibilityContext.currentScope(MODULE).ownerId());
assertEquals(USER_ID, DataVisibilityContext.currentOwnership().orElseThrow().ownerId());
assertEquals(PRIMARY_DEPT, DataVisibilityContext.currentOwnership().orElseThrow().primaryDeptId());
}
@@ -181,10 +232,11 @@ class DataVisibilityTest {
@DisplayName("娓呯悊鍚庡洖鍒版棤涓婁笅鏂囩姸鎬?)
void clearRestoresEmptyContext() {
DataVisibilityContext.load(new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT), DataScopeLevel.DEPT, null));
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT),
+ Map.of(MODULE, DataScopeLevel.DEPT), null));
DataVisibilityContext.clear();
- assertEquals(VisibilityScope.Kind.ALL_VISIBLE, DataVisibilityContext.currentScope().kind());
+ assertEquals(VisibilityScope.Kind.ALL_VISIBLE, DataVisibilityContext.currentScope(MODULE).kind());
assertTrue(DataVisibilityContext.currentOwnership().isEmpty());
}
@@ -192,9 +244,10 @@ class DataVisibilityTest {
@DisplayName("閮ㄩ棬闆嗗悎瀵瑰涓嶅彲鍙橈紝璋冪敤鏂规敼涓嶅姩宸茶鍏ョ殑鍙鑼冨洿")
void deptIdsAreNotMutableThroughScope() {
DataVisibility visibility = new DataVisibility(
- USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT, 300L), DataScopeLevel.DEPT, null);
+ USER_ID, PRIMARY_DEPT, List.of(PRIMARY_DEPT, 300L),
+ Map.of(MODULE, DataScopeLevel.DEPT), null);
- List<Long> deptIds = visibility.visibilityScope().deptIds();
+ List<Long> deptIds = visibility.visibilityScope(MODULE).deptIds();
assertThrows(UnsupportedOperationException.class, () -> deptIds.add(999L));
}
diff --git a/docker-compose.yml b/docker-compose.yml
index 5a77060..9696acb 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -64,9 +64,14 @@ services:
"
crm-app:
- build:
- context: .
- dockerfile: Dockerfile
+ # 鏈嶅姟鍣ㄤ笂鍙湁 jar锛堟棤婧愮爜锛夛細鐢ㄥ惈 LibreOffice 鐨勫熀纭€闀滃儚 crm-runtime + 鎸傝浇 jar
+ # 鍩虹闀滃儚涓€娆℃€ф瀯寤猴細docker build -f deploy/Dockerfile.base -t crm-runtime:latest .
+ # 鏇存柊娴佺▼锛氳鐩?crm-app-1.0.0-SNAPSHOT.jar 鈫?deploy/restart.sh锛堝彧閲嶅惎 crm-app锛?+ image: crm-runtime:latest
+ volumes:
+ - ./crm-app-1.0.0-SNAPSHOT.jar:/app/crm-app-1.0.0-SNAPSHOT.jar:ro
+ # 鏃ュ織鏄犲皠鍒板涓绘満锛氬簲鐢ㄥ啓 logs/crm-app.log锛堢浉瀵?WORKDIR /app锛夆啋 瀹夸富鏈?./logs/
+ - ./logs:/app/logs
depends_on:
mysql:
condition: service_healthy