浏览代码

Merge remote-tracking branch 'origin/master_exclusive_shop_20250718' into master_exclusive_shop_20250718

yuhongqi 2 周之前
父节点
当前提交
62f80b765e

+ 83 - 0
fs-admin/src/main/java/com/fs/core/config/LoginContextCallback.java

@@ -0,0 +1,83 @@
+// fs-company模块
+package com.fs.core.config;
+
+import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.enums.DataScopeEnum;
+import com.fs.common.utils.StringUtils;
+import com.fs.core.security.LoginUser;
+import com.fs.core.security.SecurityUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 登录上下文回调实现
+ */
+@Component
+public class LoginContextCallback implements com.fs.common.config.LoginContextCallback, ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+    /** 所有权限标识 */
+    private static final String ALL_PERMISSION = "*:*:*";
+    @Override
+    public Long getUserId() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        return loginUser != null ? loginUser.getUser().getUserId() : null;
+    }
+
+    @Override
+    public Long getCompanyUserId() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public String getUsername() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        return loginUser.getUsername();
+    }
+
+    @Override
+    public String getCompanyUserName() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public Long getCompanyId() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public Long getDeptId() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        SysUser user = loginUser.getUser();
+        return user.getDeptId();
+    }
+
+
+    @Override
+    public boolean hasPermission(String permission){
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        Set<String> permissions = loginUser.getPermissions();
+        if(loginUser.getUser().isAdmin()){
+            return true;
+        }
+        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
+    }
+
+
+    @Override
+    public DataScopeEnum getDataScope(){
+        return null;
+    }
+
+
+
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        LoginContextCallback.applicationContext = applicationContext;
+    }
+}

+ 11 - 1
fs-admin/src/main/java/com/fs/core/security/filter/JwtAuthenticationTokenFilter.java

@@ -6,7 +6,9 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.fs.common.config.LoginContextManager;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.core.config.LoginContextCallback;
 import com.fs.core.exception.FSException;
 import com.fs.core.security.LoginUser;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -49,7 +51,15 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
             authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
             SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+
+            // 设置上下文回调
+            LoginContextCallback callback = new LoginContextCallback();
+            LoginContextManager.setContextCallback(callback);
+        }
+        try{
+            chain.doFilter(request, response);
+        }finally {
+            LoginContextManager.clearContext();
         }
-        chain.doFilter(request, response);
     }
 }

+ 13 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveOrderController.java

@@ -2,6 +2,7 @@ package com.fs.live.controller;
 
 import cn.hutool.core.util.StrUtil;
 import com.fs.common.annotation.Log;
+import com.fs.common.config.LoginContextManager;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
@@ -48,6 +49,7 @@ import com.fs.store.service.IFsExpressService;
 import com.fs.store.service.IFsUserService;
 import com.fs.task.LiveTask;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.http.util.Asserts;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -364,6 +366,17 @@ public class LiveOrderController extends BaseController
         }
 
         ErpOrderQueryResponse response = erpOrderService.getOrderLive(request);
+
+        if(CollectionUtils.isNotEmpty(response.getOrders())){
+            ErpOrderQuery erpOrderQuery = response.getOrders().get(0);
+            if(!LoginContextManager.hasPermission("live:liveOrder:queryAddress")){
+                erpOrderQuery.setReceiver_address(ParseUtils.parseAddress(erpOrderQuery.getReceiver_address()));
+            }
+            if(!LoginContextManager.hasPermission("live:liveOrder:queryPhone")){
+                erpOrderQuery.setReceiver_mobile(ParseUtils.parsePhone(erpOrderQuery.getReceiver_mobile()));
+                erpOrderQuery.setReceiver_phone(ParseUtils.parsePhone(erpOrderQuery.getReceiver_phone()));
+            }
+        }
         return R.ok().put("data",response);
     }
 

+ 57 - 0
fs-common/src/main/java/com/fs/common/config/LoginContextCallback.java

@@ -0,0 +1,57 @@
+// fs-service模块
+package com.fs.common.config;
+
+
+import com.fs.common.enums.DataScopeEnum;
+
+/**
+ * 登录上下文回调接口
+ */
+public interface LoginContextCallback {
+
+    /**
+     * 获取当前用户ID
+     */
+    Long getUserId();
+
+    /**
+     * 获取companyUserId
+     * 销售端有值
+     */
+    Long getCompanyUserId();
+
+    /**
+     * 获取用户名
+     */
+    String getUsername();
+
+    /**
+     * 获取当前销售用户名
+     * @return String
+     */
+    String getCompanyUserName();
+
+    /**
+     * 获取租户ID
+     */
+    Long getCompanyId();
+
+    /**
+     * 获取部门ID
+     * @return 部门ID
+     */
+    Long getDeptId();
+
+    /**
+     * 获取当前登录用户数据权限
+     * @return DataScopeEnum
+     */
+    DataScopeEnum getDataScope();
+
+    /**
+     * 当前用户是否拥有某个权限
+     * @param permission 权限字符
+     * @return boolean
+     */
+    boolean hasPermission(String permission);
+}

+ 97 - 0
fs-common/src/main/java/com/fs/common/config/LoginContextManager.java

@@ -0,0 +1,97 @@
+// fs-service模块
+package com.fs.common.config;
+
+import com.fs.common.enums.DataScopeEnum;
+
+/**
+ * 登录上下文管理器
+ * 线程安全的上下文管理
+ */
+public class LoginContextManager {
+
+    private static final ThreadLocal<LoginContextCallback> callbackHolder = new ThreadLocal<>();
+
+    /**
+     * 设置上下文回调函数
+     */
+    public static void setContextCallback(LoginContextCallback callback) {
+        callbackHolder.set(callback);
+    }
+
+    /**
+     * 获取当前用户ID
+     */
+    public static Long getCurrentUserId() {
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null ? callback.getUserId() : null;
+    }
+
+    /**
+     * 获取companyUserId
+     */
+    public static Long getCurrentCompanyUserId() {
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null ? callback.getCompanyUserId() : null;
+    }
+
+    /**
+     * 获取用户名
+     */
+    public static String getCurrentUsername() {
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null ? callback.getUsername() : null;
+    }
+
+    /**
+     * 获取租户ID
+     */
+    public static Long getCurrentCompanyId() {
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null ? callback.getCompanyId() : null;
+    }
+
+    /**
+     * 获取当前部门ID
+     * @return 部门ID
+     */
+    public static Long getCurrentDeptId(){
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null ? callback.getDeptId() : null;
+    }
+
+    /**
+     * 获取当前用户的数据权限
+     * @return DataScopeEnum
+     */
+    public static DataScopeEnum getDataScope(){
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null ? callback.getDataScope() : null;
+    }
+
+
+
+    /**
+     * 清理上下文
+     */
+    public static void clearContext() {
+        callbackHolder.remove();
+    }
+
+    /**
+     * 检查是否有有效的上下文
+     */
+    public static boolean hasContext() {
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null && callback.getUserId() != null;
+    }
+
+    /**
+     * 当前用户是否有指定资源的权限
+     * @param permission 权限支付
+     * @return boolean
+     */
+    public static boolean hasPermission(String permission) {
+        LoginContextCallback callback = callbackHolder.get();
+        return callback != null && callback.hasPermission(permission);
+    }
+}

+ 174 - 0
fs-common/src/main/java/com/fs/common/enums/DataScopeEnum.java

@@ -0,0 +1,174 @@
+package com.fs.common.enums;
+
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 数据权限范围枚举
+ * 定义系统中不同层级的数据访问权限
+ *
+ * @author System
+ * @date 2024年
+ */
+@Getter
+public enum DataScopeEnum {
+
+    /**
+     * 全部数据权限 - 最高权限级别,可以访问所有数据
+     */
+    ALL("1", "全部数据权限"),
+
+    /**
+     * 自定义数据权限 - 根据用户自定义的权限规则访问数据
+     */
+    CUSTOM("2", "自定数据权限"),
+
+    /**
+     * 本部门数据权限 - 只能访问用户所属部门的数据
+     */
+    DEPARTMENT("3", "本部门数据权限"),
+
+    /**
+     * 本部门及以下数据权限 - 可以访问用户所属部门及其下级部门的数据
+     */
+    DEPARTMENT_AND_BELOW("4", "本部门及以下数据权限"),
+    /**
+     * 仅本人数据权限
+     */
+    SELF_ONLY("5", "仅本人数据权限");
+
+    /**
+     * 权限类型代码
+     * -- GETTER --
+     *  获取权限代码
+     *
+     * @return 权限代码
+
+     */
+    private final String code;
+
+    /**
+     * 权限描述信息
+     * -- GETTER --
+     *  获取权限描述
+     *
+     * @return 权限描述
+
+     */
+    private final String description;
+
+    /**
+     * 代码到枚举值的映射缓存,用于快速查找
+     */
+    private static final Map<String, DataScopeEnum> CODE_MAP = Arrays.stream(values())
+            .collect(Collectors.toMap(DataScopeEnum::getCode, Function.identity()));
+
+    /**
+     * 构造函数
+     *
+     * @param code 权限代码
+     * @param description 权限描述
+     */
+    DataScopeEnum(String code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    /**
+     * 根据代码获取对应的枚举值
+     *
+     * @param code 权限代码
+     * @return 对应的枚举值,如果不存在则返回null
+     */
+    public static DataScopeEnum getByCode(String code) {
+        return CODE_MAP.get(code);
+    }
+
+    /**
+     * 根据代码获取对应的枚举值,如果不存在则抛出异常
+     *
+     * @param code 权限代码
+     * @return 对应的枚举值
+     * @throws IllegalArgumentException 如果代码对应的枚举值不存在
+     */
+    public static DataScopeEnum getByCodeOrThrow(String code) {
+        DataScopeEnum enumValue = CODE_MAP.get(code);
+        if (enumValue == null) {
+            throw new IllegalArgumentException("无效的数据权限代码: " + code);
+        }
+        return enumValue;
+    }
+
+    /**
+     * 验证代码是否有效
+     *
+     * @param code 待验证的权限代码
+     * @return 如果代码有效返回true,否则返回false
+     */
+    public static boolean isValidCode(String code) {
+        return CODE_MAP.containsKey(code);
+    }
+
+    /**
+     * 获取所有有效的权限代码数组
+     *
+     * @return 权限代码数组
+     */
+    public static String[] getAllCodes() {
+        return CODE_MAP.keySet().toArray(new String[0]);
+    }
+
+    /**
+     * 检查当前权限是否包含另一个权限
+     * 权限层级关系:ALL > DEPARTMENT_AND_BELOW > DEPARTMENT > CUSTOM
+     *
+     * @param other 另一个权限
+     * @return 如果当前权限包含或等于另一个权限返回true
+     */
+    public boolean contains(DataScopeEnum other) {
+        if (this == ALL) {
+            return true; // 全部权限包含所有权限
+        }
+        if (this == DEPARTMENT_AND_BELOW) {
+            return other == DEPARTMENT_AND_BELOW || other == DEPARTMENT || other == CUSTOM;
+        }
+        if (this == DEPARTMENT) {
+            return other == DEPARTMENT || other == CUSTOM;
+        }
+        return this == other; // CUSTOM 只包含自己
+    }
+
+    /**
+     * 获取权限的层级值(数值越小权限越大)
+     *
+     * @return 层级值
+     */
+    public int getLevel() {
+        switch (this) {
+            case ALL: return 1;
+            case DEPARTMENT_AND_BELOW: return 2;
+            case DEPARTMENT: return 3;
+            case CUSTOM: return 4;
+            default: return 5;
+        }
+    }
+
+    /**
+     * 比较两个权限的层级
+     *
+     * @param other 另一个权限
+     * @return 如果当前权限层级更高返回正数,相同返回0,更低返回负数
+     */
+    public int compareLevel(DataScopeEnum other) {
+        return Integer.compare(other.getLevel(), this.getLevel());
+    }
+
+    @Override
+    public String toString() {
+        return code + ":" + description;
+    }
+}

+ 110 - 0
fs-company/src/main/java/com/fs/core/config/LoginContextCallback.java

@@ -0,0 +1,110 @@
+// fs-company模块
+package com.fs.core.config;
+
+import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.enums.DataScopeEnum;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyRoleMapper;
+import com.fs.core.security.LoginUser;
+import com.fs.core.security.SecurityUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * 登录上下文回调实现
+ */
+@Component
+public class LoginContextCallback implements com.fs.common.config.LoginContextCallback, ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+    /** 所有权限标识 */
+    private static final String ALL_PERMISSION = "*:*:*";
+    @Override
+    public Long getUserId() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        return loginUser != null ? loginUser.getUser().getUserId() : null;
+    }
+
+    @Override
+    public Long getCompanyUserId() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        CompanyUser user = loginUser.getUser();
+        return user != null ? user.getUserId() : null;
+    }
+
+    @Override
+    public String getUsername() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public String getCompanyUserName() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        return loginUser.getUsername();
+    }
+
+    @Override
+    public Long getCompanyId() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        return loginUser.getUser().getCompanyId();
+    }
+
+    @Override
+    public Long getDeptId() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        CompanyUser user = loginUser.getUser();
+        return user.getDeptId();
+    }
+
+
+    @Override
+    public boolean hasPermission(String permission){
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        Set<String> permissions = loginUser.getPermissions();
+        if(loginUser.getUser().isAdmin()){
+            return true;
+        }
+        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
+    }
+
+
+    @Override
+    public DataScopeEnum getDataScope(){
+        CompanyRoleMapper companyRoleMapper = applicationContext.getBean(CompanyRoleMapper.class);
+        List<String> dataScope = companyRoleMapper.queryCompanyUserDataScope(getCompanyUserId());
+        if (dataScope == null || dataScope.isEmpty()) {
+            return null;
+        }
+        Set<String> dataScopeSet = new TreeSet<>(dataScope);
+
+        // 按优先级顺序检查
+        for (DataScopeEnum scope : Arrays.asList(
+                DataScopeEnum.ALL,
+                DataScopeEnum.CUSTOM,
+                DataScopeEnum.DEPARTMENT,
+                DataScopeEnum.DEPARTMENT_AND_BELOW,
+                DataScopeEnum.SELF_ONLY
+        )) {
+            if (dataScopeSet.contains(scope.getCode())) {
+                return scope;
+            }
+        }
+        return null;
+    }
+
+
+
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        LoginContextCallback.applicationContext = applicationContext;
+    }
+}

+ 14 - 3
fs-company/src/main/java/com/fs/core/security/filter/JwtAuthenticationTokenFilter.java

@@ -6,7 +6,9 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.fs.common.config.LoginContextManager;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.core.config.LoginContextCallback;
 import com.fs.core.exception.FSException;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
@@ -22,8 +24,8 @@ import com.fs.common.utils.StringUtils;
 
 /**
  * token过滤器 验证token有效性
- * 
- 
+ *
+
  */
 @Component
 public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
@@ -43,7 +45,16 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
             authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
             SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+
+            // 设置上下文回调
+            LoginContextCallback callback = new LoginContextCallback();
+            LoginContextManager.setContextCallback(callback);
+
+        }
+        try{
+            chain.doFilter(request, response);
+        }finally {
+            LoginContextManager.clearContext();
         }
-        chain.doFilter(request, response);
     }
 }

+ 15 - 8
fs-service-system/src/main/java/com/fs/company/mapper/CompanyRoleMapper.java

@@ -1,20 +1,22 @@
 package com.fs.company.mapper;
 
 import com.fs.company.domain.CompanyRole;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 import java.util.List;
 
 /**
  * 角色信息Mapper接口
- * 
+ *
  * @author fs
  * @date 2021-05-25
  */
-public interface CompanyRoleMapper 
+public interface CompanyRoleMapper
 {
     /**
      * 查询角色信息
-     * 
+     *
      * @param roleId 角色信息ID
      * @return 角色信息
      */
@@ -22,7 +24,7 @@ public interface CompanyRoleMapper
 
     /**
      * 查询角色信息列表
-     * 
+     *
      * @param companyRole 角色信息
      * @return 角色信息集合
      */
@@ -30,7 +32,7 @@ public interface CompanyRoleMapper
 
     /**
      * 新增角色信息
-     * 
+     *
      * @param companyRole 角色信息
      * @return 结果
      */
@@ -38,7 +40,7 @@ public interface CompanyRoleMapper
 
     /**
      * 修改角色信息
-     * 
+     *
      * @param companyRole 角色信息
      * @return 结果
      */
@@ -46,7 +48,7 @@ public interface CompanyRoleMapper
 
     /**
      * 删除角色信息
-     * 
+     *
      * @param roleId 角色信息ID
      * @return 结果
      */
@@ -54,7 +56,7 @@ public interface CompanyRoleMapper
 
     /**
      * 批量删除角色信息
-     * 
+     *
      * @param roleIds 需要删除的数据ID
      * @return 结果
      */
@@ -69,4 +71,9 @@ public interface CompanyRoleMapper
     CompanyRole checkRoleKeyUnique(String roleKey);
 
     List<Integer> selectRoleListByUserId(Long userId);
+
+    @Select("select distinct cr.data_scope from company_user_role cur\n" +
+            "          inner join company_role cr on cur.role_id=cr.role_id\n" +
+            "        where cur.user_id = #{companyUserId}")
+    List<String> queryCompanyUserDataScope(@Param("companyUserId") Long companyUserId);
 }

+ 14 - 2
fs-service-system/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -24,6 +24,7 @@ import com.alibaba.fastjson.JSON;
 
 import com.alibaba.fastjson.JSONObject;
 import com.fs.common.config.FSSysConfig;
+import com.fs.common.config.LoginContextManager;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.event.TemplateBean;
@@ -215,6 +216,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
     @Autowired
     private LiveAfterSalesMapper liveAfterSalesMapper;
 
+    @Autowired
+    private ILiveOrderService liveOrderService;
+
 
     public LiveOrderServiceImpl(RedisCache redisCache) {
         this.redisCache = redisCache;
@@ -683,11 +687,19 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             liveOrder.setDeliveryStatus(null);
             liveOrder.setDeliveryType(null);
         }
+
+        // 判断当前用户是否有指定数据权限
+        if(!LoginContextManager.hasPermission("live:liveOrder:queryAddress")){
+            liveOrder.setUserAddress(ParseUtils.parseAddress(liveOrder.getUserAddress()));
+        }
+        if(!LoginContextManager.hasPermission("live:liveOrder:queryPhone")){
+            liveOrder.setUserPhone(ParseUtils.parsePhone(liveOrder.getUserPhone()));
+        }
+
         return liveOrder;
     }
 
-    @Autowired
-    private ILiveOrderService liveOrderService;
+
     @Override
     @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
     public void editLiveOrderDeliveryId(FsStoreDelivers deliveryDTO) {