mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-03-22 02:37:16 +08:00
【升级】大数据下的优化
This commit is contained in:
@@ -123,8 +123,8 @@
|
||||
</s-table>
|
||||
</template>
|
||||
</XnResizablePanel>
|
||||
<Form ref="formRef" @successful="tableRef.refresh()" />
|
||||
<CopyForm ref="copyFormRef" @successful="tableRef.clearRefreshSelected()" />
|
||||
<Form ref="formRef" @successful="tableRef.refresh(); refreshTreeData()" />
|
||||
<CopyForm ref="copyFormRef" @successful="tableRef.clearRefreshSelected(); refreshTreeData()" />
|
||||
</template>
|
||||
|
||||
<script setup name="bizOrg">
|
||||
@@ -282,6 +282,21 @@
|
||||
})
|
||||
}
|
||||
loadTreeData()
|
||||
// 刷新树数据(增删改后调用,使用全量树接口保留展开状态)
|
||||
const refreshTreeData = () => {
|
||||
treeLoading.value = true
|
||||
treeData.value = []
|
||||
bizOrgApi
|
||||
.orgTree()
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 懒加载子节点
|
||||
const onLoadData = (treeNode) => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -323,12 +338,14 @@
|
||||
]
|
||||
bizOrgApi.orgDelete(params).then(() => {
|
||||
tableRef.value.refresh(true)
|
||||
refreshTreeData()
|
||||
})
|
||||
}
|
||||
// 批量删除
|
||||
const deleteBatchOrg = (params) => {
|
||||
bizOrgApi.orgDelete(params).then(() => {
|
||||
tableRef.value.clearRefreshSelected()
|
||||
refreshTreeData()
|
||||
})
|
||||
}
|
||||
// 批量复制
|
||||
|
||||
@@ -122,8 +122,8 @@
|
||||
</s-table>
|
||||
</template>
|
||||
</XnResizablePanel>
|
||||
<Form ref="formRef" @successful="tableRef.refresh()" />
|
||||
<CopyForm ref="copyFormRef" @successful="tableRef.clearRefreshSelected()" />
|
||||
<Form ref="formRef" @successful="tableRef.refresh(); refreshTreeData()" />
|
||||
<CopyForm ref="copyFormRef" @successful="tableRef.clearRefreshSelected(); refreshTreeData()" />
|
||||
</template>
|
||||
|
||||
<script setup name="sysOrg">
|
||||
@@ -281,6 +281,21 @@
|
||||
})
|
||||
}
|
||||
loadTreeData()
|
||||
// 刷新树数据(增删改后调用,使用全量树接口保留展开状态)
|
||||
const refreshTreeData = () => {
|
||||
treeLoading.value = true
|
||||
treeData.value = []
|
||||
orgApi
|
||||
.orgTree()
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 懒加载子节点
|
||||
const onLoadData = (treeNode) => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -322,12 +337,14 @@
|
||||
]
|
||||
orgApi.orgDelete(params).then(() => {
|
||||
tableRef.value.refresh(true)
|
||||
refreshTreeData()
|
||||
})
|
||||
}
|
||||
// 批量删除
|
||||
const deleteBatchOrg = (params) => {
|
||||
orgApi.orgDelete(params).then(() => {
|
||||
tableRef.value.clearRefreshSelected()
|
||||
refreshTreeData()
|
||||
})
|
||||
}
|
||||
// 批量复制
|
||||
|
||||
@@ -62,4 +62,29 @@ public class CommonSqlUtil {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用预计算表的子查询替代IN查询,适用于大数据量场景
|
||||
* 通过MAP表查找SCOPE_KEY,再从SCOPE表获取orgId列表:
|
||||
* column IN (SELECT ORG_ID FROM SYS_USER_DATA_SCOPE WHERE USER_ID = '{userId}'
|
||||
* AND SCOPE_KEY = (SELECT SCOPE_KEY FROM SYS_USER_DATA_SCOPE_MAP WHERE USER_ID = '{userId}' AND API_URL = '{apiUrl}'))
|
||||
* <p>
|
||||
* 按API维度精确过滤,相同orgId集合的API共享SCOPE_KEY,大幅减少预计算表数据量。
|
||||
* SQL固定长度,不受数据量影响,数据库可缓存执行计划,走索引高效查询。
|
||||
* </p>
|
||||
*
|
||||
* @param wrapper MyBatis-Plus LambdaQueryWrapper
|
||||
* @param column 查询列
|
||||
* @param userId 当前登录用户ID
|
||||
* @param apiUrl 当前请求的API地址
|
||||
* @param <T> 实体类型
|
||||
*/
|
||||
public static <T> void scopeIn(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> column, String userId, String apiUrl) {
|
||||
// 防御性处理:移除单引号防止SQL注入
|
||||
String safeUserId = userId.replace("'", "");
|
||||
String safeApiUrl = apiUrl.replace("'", "");
|
||||
wrapper.inSql(column, "SELECT ORG_ID FROM SYS_USER_DATA_SCOPE WHERE USER_ID = '" + safeUserId
|
||||
+ "' AND SCOPE_KEY = (SELECT SCOPE_KEY FROM SYS_USER_DATA_SCOPE_MAP WHERE USER_ID = '"
|
||||
+ safeUserId + "' AND API_URL = '" + safeApiUrl + "')");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,4 +177,14 @@ public interface SaBaseLoginUserApi {
|
||||
* @date 2022/3/10 16:14
|
||||
**/
|
||||
void doRegister(String account, String password);
|
||||
|
||||
/**
|
||||
* 刷新用户数据范围预计算表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param dataScopeList 用户的数据范围集合(per-API维度)
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
*/
|
||||
void refreshUserDataScope(String userId, List<SaBaseLoginUser.DataScope> dataScopeList);
|
||||
}
|
||||
|
||||
@@ -81,4 +81,12 @@ public interface SysOrgApi {
|
||||
* @date 2025/01/10 14:45
|
||||
**/
|
||||
List<JSONObject> getOrgListByIdListWithoutException(List<String> orgIdList);
|
||||
|
||||
/**
|
||||
* 清除组织缓存
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
void clearOrgCache();
|
||||
}
|
||||
|
||||
@@ -727,6 +727,8 @@ public class AuthServiceImpl implements AuthService {
|
||||
saBaseLoginUser.setRoleCodeList(roleCodeList);
|
||||
// 缓存用户信息,此处使用TokenSession为了指定时间内无操作则自动下线
|
||||
StpUtil.getTokenSession().set("loginUser", saBaseLoginUser);
|
||||
// 刷新用户数据范围预计算表
|
||||
loginUserApi.refreshUserDataScope(saBaseLoginUser.getId(), saBaseLoginUser.getDataScopeList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.biz.core.listener;
|
||||
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import vip.xiaonuo.biz.core.enums.BizDataTypeEnum;
|
||||
import vip.xiaonuo.biz.modular.org.service.BizOrgService;
|
||||
import vip.xiaonuo.common.listener.CommonDataChangeListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 业务模块数据变化侦听器
|
||||
* 监听其他模块(如sys)对共享表的变更,同步清除biz模块缓存
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
@Component
|
||||
public class BizDataChangeListener implements CommonDataChangeListener {
|
||||
|
||||
@Resource
|
||||
private BizOrgService bizOrgService;
|
||||
|
||||
@Override
|
||||
public void doAddWithDataId(String dataType, String dataId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAddWithDataIdList(String dataType, List<String> dataIdList) {
|
||||
if(dataType.equals(BizDataTypeEnum.ORG.getValue())) {
|
||||
bizOrgService.clearOrgCache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAddWithData(String dataType, JSONObject jsonObject) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAddWithDataList(String dataType, JSONArray jsonArray) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doUpdateWithDataId(String dataType, String dataId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doUpdateWithDataIdList(String dataType, List<String> dataIdList) {
|
||||
if(dataType.equals(BizDataTypeEnum.ORG.getValue())) {
|
||||
bizOrgService.clearOrgCache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doUpdateWithData(String dataType, JSONObject jsonObject) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doUpdateWithDataList(String dataType, JSONArray jsonArray) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDeleteWithDataId(String dataType, String dataId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDeleteWithDataIdList(String dataType, List<String> dataIdList) {
|
||||
if(dataType.equals(BizDataTypeEnum.ORG.getValue())) {
|
||||
bizOrgService.clearOrgCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,4 +197,13 @@ public interface BizOrgService extends IService<BizOrg> {
|
||||
* @date 2025/12/24 01:30
|
||||
*/
|
||||
void copy(BizOrgCopyParam bizOrgCopyParam);
|
||||
|
||||
/**
|
||||
* 清除机构缓存(Redis缓存 + 版本号递增)
|
||||
* 当其他模块修改了SYS_ORG表数据时,需要调用此方法同步清除缓存
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
*/
|
||||
void clearOrgCache();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -44,11 +45,13 @@ import vip.xiaonuo.biz.modular.position.service.BizPositionService;
|
||||
import vip.xiaonuo.biz.modular.user.entity.BizUser;
|
||||
import vip.xiaonuo.biz.modular.user.service.BizUserService;
|
||||
import vip.xiaonuo.common.cache.CommonCacheOperator;
|
||||
import vip.xiaonuo.common.util.CommonServletUtil;
|
||||
import vip.xiaonuo.common.util.CommonSqlUtil;
|
||||
import vip.xiaonuo.common.enums.CommonSortOrderEnum;
|
||||
import vip.xiaonuo.common.exception.CommonException;
|
||||
import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
|
||||
import vip.xiaonuo.common.page.CommonPageRequest;
|
||||
import vip.xiaonuo.sys.api.SysOrgApi;
|
||||
import vip.xiaonuo.sys.api.SysRoleApi;
|
||||
|
||||
import java.util.*;
|
||||
@@ -77,6 +80,9 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
@Resource
|
||||
private CommonCacheOperator commonCacheOperator;
|
||||
|
||||
@Resource
|
||||
private SysOrgApi sysOrgApi;
|
||||
|
||||
@Resource
|
||||
private SysRoleApi sysRoleApi;
|
||||
|
||||
@@ -114,7 +120,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizOrg::getId, loginUserDataScope);
|
||||
CommonSqlUtil.scopeIn(queryWrapper.lambda(), BizOrg::getId, StpUtil.getLoginIdAsString(), CommonServletUtil.getRequest().getServletPath());
|
||||
}
|
||||
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
|
||||
}
|
||||
@@ -273,6 +279,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
CommonDataChangeEventCenter.doAddWithData(BizDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(bizOrg));
|
||||
// 清除缓存
|
||||
this.invalidateOrgCaches();
|
||||
sysOrgApi.clearOrgCache();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -311,6 +318,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
CommonDataChangeEventCenter.doUpdateWithData(BizDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(bizOrg));
|
||||
// 清除缓存
|
||||
this.invalidateOrgCaches();
|
||||
sysOrgApi.clearOrgCache();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -367,6 +375,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
CommonDataChangeEventCenter.doDeleteWithDataIdList(BizDataTypeEnum.ORG.getValue(), toDeleteOrgIdList);
|
||||
// 清除缓存
|
||||
this.invalidateOrgCaches();
|
||||
sysOrgApi.clearOrgCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,6 +413,11 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
commonCacheOperator.put(ORG_CACHE_VERSION_KEY, String.valueOf(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearOrgCache() {
|
||||
this.invalidateOrgCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户可见的机构ID集合(带版本化缓存)
|
||||
* 缓存命中时直接返回,无需加载全量机构数据;缓存未命中时计算并缓存
|
||||
@@ -502,6 +516,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
CommonDataChangeEventCenter.doAddWithData(BizDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(bizOrg));
|
||||
// 清除缓存
|
||||
this.invalidateOrgCaches();
|
||||
sysOrgApi.clearOrgCache();
|
||||
return bizOrg.getId();
|
||||
}
|
||||
|
||||
@@ -543,7 +558,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizOrg::getId, loginUserDataScope);
|
||||
CommonSqlUtil.scopeIn(queryWrapper.lambda(), BizOrg::getId, StpUtil.getLoginIdAsString(), CommonServletUtil.getRequest().getServletPath());
|
||||
}
|
||||
// 查询部分字段
|
||||
queryWrapper.lambda().select(BizOrg::getId, BizOrg::getParentId, BizOrg::getName,
|
||||
@@ -567,7 +582,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizUser::getOrgId, loginUserDataScope);
|
||||
CommonSqlUtil.scopeIn(queryWrapper.lambda(), BizUser::getOrgId, StpUtil.getLoginIdAsString(), CommonServletUtil.getRequest().getServletPath());
|
||||
}
|
||||
// 只查询部分字段
|
||||
queryWrapper.lambda().select(BizUser::getId, BizUser::getAvatar, BizUser::getOrgId, BizUser::getPositionId, BizUser::getAccount,
|
||||
@@ -646,6 +661,7 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
});
|
||||
// 清除缓存
|
||||
this.invalidateOrgCaches();
|
||||
sysOrgApi.clearOrgCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -212,4 +212,15 @@ public class ClientLoginUserApiProvider implements SaBaseLoginUserApi {
|
||||
public void doRegister(String account, String password) {
|
||||
clientUserService.doRegister(account, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* C端用户无数据范围,不实现
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
@Override
|
||||
public void refreshUserDataScope(String userId, List<SaBaseLoginUser.DataScope> dataScopeList) {
|
||||
// C端用户无数据范围,无需刷新预计算表
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@ package vip.xiaonuo.sys.core.listener;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
|
||||
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
|
||||
import vip.xiaonuo.common.listener.CommonDataChangeListener;
|
||||
import vip.xiaonuo.sys.core.enums.SysDataTypeEnum;
|
||||
import vip.xiaonuo.sys.modular.org.service.SysUserDataScopeService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -32,6 +34,9 @@ import java.util.List;
|
||||
@Component
|
||||
public class SysDataChangeListener implements CommonDataChangeListener {
|
||||
|
||||
@Resource
|
||||
private SysUserDataScopeService sysUserDataScopeService;
|
||||
|
||||
@Override
|
||||
public void doAddWithDataId(String dataType, String dataId) {
|
||||
// 此处可做额外处理
|
||||
@@ -46,6 +51,8 @@ public class SysDataChangeListener implements CommonDataChangeListener {
|
||||
saBaseLoginUser.setDataScopeList(saBaseLoginUser.getDataScopeList());
|
||||
// 重新缓存当前登录用户信息
|
||||
StpUtil.getTokenSession().set("loginUser", saBaseLoginUser);
|
||||
// 同步更新预计算表:为当前用户的所有API追加新机构
|
||||
sysUserDataScopeService.appendOrgIdsForUser(saBaseLoginUser.getId(), dataIdList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +93,9 @@ public class SysDataChangeListener implements CommonDataChangeListener {
|
||||
|
||||
@Override
|
||||
public void doDeleteWithDataIdList(String dataType, List<String> dataIdList) {
|
||||
// 此处可做额外处理
|
||||
// 组织删除时,精准删除预计算表中对应的机构记录
|
||||
if(dataType.equals(SysDataTypeEnum.ORG.getValue())) {
|
||||
sysUserDataScopeService.deleteByOrgIds(dataIdList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.org.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户数据范围预计算实体
|
||||
* <p>
|
||||
* 通过SCOPE_KEY将相同orgId集合的API分组,避免重复存储,大幅减少数据量。
|
||||
* </p>
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("SYS_USER_DATA_SCOPE")
|
||||
public class SysUserDataScope implements Serializable {
|
||||
|
||||
/** 用户ID */
|
||||
@Schema(description = "用户ID")
|
||||
private String userId;
|
||||
|
||||
/** 作用域KEY(orgId集合的MD5摘要) */
|
||||
@Schema(description = "作用域KEY")
|
||||
private String scopeKey;
|
||||
|
||||
/** 机构ID */
|
||||
@Schema(description = "机构ID")
|
||||
private String orgId;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.org.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户数据范围API映射实体
|
||||
* <p>
|
||||
* 记录每个API对应的作用域KEY,多个API共享相同orgId集合时复用同一个SCOPE_KEY,
|
||||
* 大幅减少预计算表的数据量。
|
||||
* </p>
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("SYS_USER_DATA_SCOPE_MAP")
|
||||
public class SysUserDataScopeMap implements Serializable {
|
||||
|
||||
/** 用户ID */
|
||||
@Schema(description = "用户ID")
|
||||
private String userId;
|
||||
|
||||
/** API接口地址 */
|
||||
@Schema(description = "API接口地址")
|
||||
private String apiUrl;
|
||||
|
||||
/** 作用域KEY(orgId集合的MD5摘要) */
|
||||
@Schema(description = "作用域KEY")
|
||||
private String scopeKey;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.org.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import vip.xiaonuo.sys.modular.org.entity.SysUserDataScopeMap;
|
||||
|
||||
/**
|
||||
* 用户数据范围API映射Mapper
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
@Mapper
|
||||
public interface SysUserDataScopeMapMapper extends BaseMapper<SysUserDataScopeMap> {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.org.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import vip.xiaonuo.sys.modular.org.entity.SysUserDataScope;
|
||||
|
||||
/**
|
||||
* 用户数据范围预计算Mapper
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
@Mapper
|
||||
public interface SysUserDataScopeMapper extends BaseMapper<SysUserDataScope> {
|
||||
|
||||
}
|
||||
@@ -84,4 +84,9 @@ public class SysOrgApiProvider implements SysOrgApi {
|
||||
public List<JSONObject> getOrgListByIdListWithoutException(List<String> orgIdList) {
|
||||
return sysOrgService.listByIds(orgIdList).stream().map(JSONUtil::parseObj).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearOrgCache() {
|
||||
sysOrgService.clearOrgCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,4 +213,13 @@ public interface SysOrgService extends IService<SysOrg> {
|
||||
* @date 2025/5/10 12:13
|
||||
*/
|
||||
List<String> getParentIdListByOrgId(String orgId);
|
||||
|
||||
/**
|
||||
* 清除组织缓存(本地内存缓存 + Redis缓存)
|
||||
* 当其他模块修改了SYS_ORG表数据时,需要调用此方法同步清除缓存
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
*/
|
||||
void clearOrgCache();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.org.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
|
||||
import vip.xiaonuo.sys.modular.org.entity.SysUserDataScope;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户数据范围预计算Service接口
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
public interface SysUserDataScopeService extends IService<SysUserDataScope> {
|
||||
|
||||
/**
|
||||
* 刷新指定用户的数据范围预计算(登录时调用)
|
||||
* 按API维度存储,每个API的数据范围独立
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param dataScopeList 用户的数据范围集合(per-API)
|
||||
*/
|
||||
void refreshByUserId(String userId, List<SaBaseLoginUser.DataScope> dataScopeList);
|
||||
|
||||
/**
|
||||
* 删除指定用户的数据范围预计算
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
void deleteByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 删除指定机构ID的预计算记录(机构删除时调用)
|
||||
* 精准删除,不影响其他机构的数据
|
||||
*
|
||||
* @param orgIds 被删除的机构ID列表
|
||||
*/
|
||||
void deleteByOrgIds(List<String> orgIds);
|
||||
|
||||
/**
|
||||
* 为指定用户的所有API追加机构ID(机构新增时调用)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param orgIds 新增的机构ID列表
|
||||
*/
|
||||
void appendOrgIdsForUser(String userId, List<String> orgIds);
|
||||
}
|
||||
@@ -401,7 +401,8 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
/**
|
||||
* 清除本地内存缓存和Redis缓存
|
||||
*/
|
||||
private void clearOrgCache() {
|
||||
@Override
|
||||
public void clearOrgCache() {
|
||||
localOrgListCache = null;
|
||||
localParentChildrenMap = null;
|
||||
localAllOrgIdList = null;
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.org.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
|
||||
import vip.xiaonuo.sys.modular.org.entity.SysUserDataScope;
|
||||
import vip.xiaonuo.sys.modular.org.entity.SysUserDataScopeMap;
|
||||
import vip.xiaonuo.sys.modular.org.mapper.SysUserDataScopeMapper;
|
||||
import vip.xiaonuo.sys.modular.org.mapper.SysUserDataScopeMapMapper;
|
||||
import vip.xiaonuo.sys.modular.org.service.SysUserDataScopeService;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户数据范围预计算Service实现类
|
||||
* <p>
|
||||
* 优化策略:
|
||||
* 1. 作用域去重:将orgId集合相同的API分组,共享同一个SCOPE_KEY,避免重复存储
|
||||
* 2. 增量刷新:对比新旧数据,只执行必要的增删操作,减少数据库IO
|
||||
* </p>
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SysUserDataScopeServiceImpl extends ServiceImpl<SysUserDataScopeMapper, SysUserDataScope>
|
||||
implements SysUserDataScopeService {
|
||||
|
||||
@Resource
|
||||
private SysUserDataScopeMapMapper sysUserDataScopeMapMapper;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void refreshByUserId(String userId, List<SaBaseLoginUser.DataScope> dataScopeList) {
|
||||
if (ObjectUtil.isEmpty(dataScopeList)) {
|
||||
// 无数据范围,清空该用户所有记录
|
||||
this.deleteByUserId(userId);
|
||||
return;
|
||||
}
|
||||
// 1. 按orgId集合分组,计算SCOPE_KEY
|
||||
// key=scopeKey, value=orgId集合
|
||||
Map<String, List<String>> scopeKeyToOrgIds = new LinkedHashMap<>();
|
||||
// key=apiUrl, value=scopeKey
|
||||
Map<String, String> apiToScopeKey = new LinkedHashMap<>();
|
||||
for (SaBaseLoginUser.DataScope dataScope : dataScopeList) {
|
||||
if (dataScope.isScopeAll() || ObjectUtil.isEmpty(dataScope.getDataScope())) {
|
||||
continue;
|
||||
}
|
||||
List<String> sortedOrgIds = dataScope.getDataScope().stream()
|
||||
.distinct().sorted().collect(Collectors.toList());
|
||||
String scopeKey = SecureUtil.md5(String.join(",", sortedOrgIds));
|
||||
apiToScopeKey.put(dataScope.getApiUrl(), scopeKey);
|
||||
scopeKeyToOrgIds.putIfAbsent(scopeKey, sortedOrgIds);
|
||||
}
|
||||
if (apiToScopeKey.isEmpty()) {
|
||||
this.deleteByUserId(userId);
|
||||
return;
|
||||
}
|
||||
// 2. 增量刷新MAP表
|
||||
Map<String, String> existingMap = sysUserDataScopeMapMapper.selectList(
|
||||
new LambdaQueryWrapper<SysUserDataScopeMap>()
|
||||
.eq(SysUserDataScopeMap::getUserId, userId)
|
||||
).stream().collect(Collectors.toMap(SysUserDataScopeMap::getApiUrl, SysUserDataScopeMap::getScopeKey));
|
||||
|
||||
// 需要删除的MAP记录(旧的有,新的没有)
|
||||
Set<String> apiToDelete = new HashSet<>(existingMap.keySet());
|
||||
apiToDelete.removeAll(apiToScopeKey.keySet());
|
||||
if (!apiToDelete.isEmpty()) {
|
||||
sysUserDataScopeMapMapper.delete(new LambdaQueryWrapper<SysUserDataScopeMap>()
|
||||
.eq(SysUserDataScopeMap::getUserId, userId)
|
||||
.in(SysUserDataScopeMap::getApiUrl, apiToDelete));
|
||||
}
|
||||
// 需要新增或更新的MAP记录
|
||||
List<SysUserDataScopeMap> mapToInsert = new ArrayList<>();
|
||||
List<SysUserDataScopeMap> mapToUpdate = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : apiToScopeKey.entrySet()) {
|
||||
String apiUrl = entry.getKey();
|
||||
String newScopeKey = entry.getValue();
|
||||
String oldScopeKey = existingMap.get(apiUrl);
|
||||
if (oldScopeKey == null) {
|
||||
// 新增
|
||||
SysUserDataScopeMap map = new SysUserDataScopeMap();
|
||||
map.setUserId(userId);
|
||||
map.setApiUrl(apiUrl);
|
||||
map.setScopeKey(newScopeKey);
|
||||
mapToInsert.add(map);
|
||||
} else if (!oldScopeKey.equals(newScopeKey)) {
|
||||
// scope变了,更新
|
||||
SysUserDataScopeMap map = new SysUserDataScopeMap();
|
||||
map.setUserId(userId);
|
||||
map.setApiUrl(apiUrl);
|
||||
map.setScopeKey(newScopeKey);
|
||||
mapToUpdate.add(map);
|
||||
}
|
||||
// scope没变的跳过
|
||||
}
|
||||
for (SysUserDataScopeMap map : mapToInsert) {
|
||||
sysUserDataScopeMapMapper.insert(map);
|
||||
}
|
||||
for (SysUserDataScopeMap map : mapToUpdate) {
|
||||
SysUserDataScopeMap updateEntity = new SysUserDataScopeMap();
|
||||
updateEntity.setScopeKey(map.getScopeKey());
|
||||
sysUserDataScopeMapMapper.update(updateEntity, new LambdaQueryWrapper<SysUserDataScopeMap>()
|
||||
.eq(SysUserDataScopeMap::getUserId, map.getUserId())
|
||||
.eq(SysUserDataScopeMap::getApiUrl, map.getApiUrl()));
|
||||
}
|
||||
|
||||
// 3. 增量刷新SCOPE表
|
||||
// 查询该用户当前所有scope记录
|
||||
Set<String> existingScopeKeys = this.list(
|
||||
new LambdaQueryWrapper<SysUserDataScope>()
|
||||
.select(SysUserDataScope::getScopeKey)
|
||||
.eq(SysUserDataScope::getUserId, userId)
|
||||
.groupBy(SysUserDataScope::getScopeKey)
|
||||
).stream().map(SysUserDataScope::getScopeKey).collect(Collectors.toSet());
|
||||
|
||||
// 新的需要的scopeKey集合
|
||||
Set<String> neededScopeKeys = new HashSet<>(scopeKeyToOrgIds.keySet());
|
||||
|
||||
// 删除不再需要的scope记录
|
||||
Set<String> scopeKeysToDelete = new HashSet<>(existingScopeKeys);
|
||||
scopeKeysToDelete.removeAll(neededScopeKeys);
|
||||
if (!scopeKeysToDelete.isEmpty()) {
|
||||
this.remove(new LambdaQueryWrapper<SysUserDataScope>()
|
||||
.eq(SysUserDataScope::getUserId, userId)
|
||||
.in(SysUserDataScope::getScopeKey, scopeKeysToDelete));
|
||||
}
|
||||
|
||||
// 新增缺失的scope记录
|
||||
Set<String> scopeKeysToAdd = new HashSet<>(neededScopeKeys);
|
||||
scopeKeysToAdd.removeAll(existingScopeKeys);
|
||||
if (!scopeKeysToAdd.isEmpty()) {
|
||||
List<SysUserDataScope> newRecords = new ArrayList<>();
|
||||
for (String scopeKey : scopeKeysToAdd) {
|
||||
for (String orgId : scopeKeyToOrgIds.get(scopeKey)) {
|
||||
SysUserDataScope record = new SysUserDataScope();
|
||||
record.setUserId(userId);
|
||||
record.setScopeKey(scopeKey);
|
||||
record.setOrgId(orgId);
|
||||
newRecords.add(record);
|
||||
}
|
||||
}
|
||||
if (!newRecords.isEmpty()) {
|
||||
this.saveBatch(newRecords);
|
||||
}
|
||||
}
|
||||
|
||||
int totalScopeRecords = scopeKeyToOrgIds.values().stream().mapToInt(List::size).sum();
|
||||
log.debug(">>> 刷新用户数据范围预计算,userId={},API数={},去重后scope组={},scope记录数={}",
|
||||
userId, apiToScopeKey.size(), scopeKeyToOrgIds.size(), totalScopeRecords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByUserId(String userId) {
|
||||
sysUserDataScopeMapMapper.delete(new LambdaQueryWrapper<SysUserDataScopeMap>()
|
||||
.eq(SysUserDataScopeMap::getUserId, userId));
|
||||
this.remove(new LambdaQueryWrapper<SysUserDataScope>()
|
||||
.eq(SysUserDataScope::getUserId, userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByOrgIds(List<String> orgIds) {
|
||||
if (ObjectUtil.isEmpty(orgIds)) {
|
||||
return;
|
||||
}
|
||||
this.remove(new LambdaQueryWrapper<SysUserDataScope>()
|
||||
.in(SysUserDataScope::getOrgId, orgIds));
|
||||
log.info(">>> 删除机构数据范围预计算,orgIds数量={}", orgIds.size());
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void appendOrgIdsForUser(String userId, List<String> orgIds) {
|
||||
if (ObjectUtil.isEmpty(orgIds)) {
|
||||
return;
|
||||
}
|
||||
// 查询该用户所有的scopeKey(去重)
|
||||
Set<String> existingScopeKeys = this.list(
|
||||
new LambdaQueryWrapper<SysUserDataScope>()
|
||||
.select(SysUserDataScope::getScopeKey)
|
||||
.eq(SysUserDataScope::getUserId, userId)
|
||||
.groupBy(SysUserDataScope::getScopeKey)
|
||||
).stream().map(SysUserDataScope::getScopeKey).collect(Collectors.toSet());
|
||||
|
||||
if (ObjectUtil.isEmpty(existingScopeKeys)) {
|
||||
return;
|
||||
}
|
||||
// 查询已存在的(scopeKey, orgId)组合,避免重复插入
|
||||
Set<String> existingKeys = this.list(
|
||||
new LambdaQueryWrapper<SysUserDataScope>()
|
||||
.select(SysUserDataScope::getScopeKey, SysUserDataScope::getOrgId)
|
||||
.eq(SysUserDataScope::getUserId, userId)
|
||||
.in(SysUserDataScope::getOrgId, orgIds)
|
||||
).stream().map(r -> r.getScopeKey() + "|" + r.getOrgId()).collect(Collectors.toSet());
|
||||
|
||||
// 为每个scopeKey追加新的机构ID
|
||||
List<SysUserDataScope> newRecords = new ArrayList<>();
|
||||
for (String scopeKey : existingScopeKeys) {
|
||||
for (String orgId : orgIds) {
|
||||
if (!existingKeys.contains(scopeKey + "|" + orgId)) {
|
||||
SysUserDataScope record = new SysUserDataScope();
|
||||
record.setUserId(userId);
|
||||
record.setScopeKey(scopeKey);
|
||||
record.setOrgId(orgId);
|
||||
newRecords.add(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(newRecords)) {
|
||||
this.saveBatch(newRecords);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import org.springframework.stereotype.Service;
|
||||
import vip.xiaonuo.auth.api.SaBaseLoginUserApi;
|
||||
import vip.xiaonuo.auth.core.pojo.SaBaseClientLoginUser;
|
||||
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
|
||||
import vip.xiaonuo.sys.modular.org.service.SysUserDataScopeService;
|
||||
import vip.xiaonuo.sys.modular.user.entity.SysUser;
|
||||
import vip.xiaonuo.sys.modular.user.result.SysLoginUser;
|
||||
import vip.xiaonuo.sys.modular.user.service.SysUserService;
|
||||
@@ -39,6 +40,9 @@ public class SysLoginUserApiProvider implements SaBaseLoginUserApi {
|
||||
@Resource
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@Resource
|
||||
private SysUserDataScopeService sysUserDataScopeService;
|
||||
|
||||
/**
|
||||
* 根据id获取B端用户信息,查不到则返回null
|
||||
*
|
||||
@@ -207,4 +211,9 @@ public class SysLoginUserApiProvider implements SaBaseLoginUserApi {
|
||||
public void doRegister(String account, String password) {
|
||||
sysUserService.doRegister(account, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshUserDataScope(String userId, List<SaBaseLoginUser.DataScope> dataScopeList) {
|
||||
sysUserDataScopeService.refreshByUserId(userId, dataScopeList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1413,4 +1413,37 @@ CREATE TABLE `SYS_USER_PASSWORD` (
|
||||
-- Records of SYS_USER_PASSWORD
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for SYS_USER_DATA_SCOPE
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `SYS_USER_DATA_SCOPE`;
|
||||
CREATE TABLE `SYS_USER_DATA_SCOPE` (
|
||||
`USER_ID` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
|
||||
`SCOPE_KEY` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '作用域KEY(orgId集合的MD5摘要)',
|
||||
`ORG_ID` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '机构ID',
|
||||
PRIMARY KEY (`USER_ID`, `SCOPE_KEY`, `ORG_ID`) USING BTREE,
|
||||
INDEX `IDX_USER_SCOPE`(`USER_ID`, `SCOPE_KEY`) USING BTREE,
|
||||
INDEX `IDX_ORG_ID`(`ORG_ID`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户数据范围预计算' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of SYS_USER_DATA_SCOPE
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for SYS_USER_DATA_SCOPE_MAP
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `SYS_USER_DATA_SCOPE_MAP`;
|
||||
CREATE TABLE `SYS_USER_DATA_SCOPE_MAP` (
|
||||
`USER_ID` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
|
||||
`API_URL` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'API接口地址',
|
||||
`SCOPE_KEY` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '作用域KEY(orgId集合的MD5摘要)',
|
||||
PRIMARY KEY (`USER_ID`, `API_URL`) USING BTREE,
|
||||
INDEX `IDX_USER_SCOPE_KEY`(`USER_ID`, `SCOPE_KEY`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户数据范围API映射' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of SYS_USER_DATA_SCOPE_MAP
|
||||
-- ----------------------------
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
Reference in New Issue
Block a user