负载均衡深度改造,增加分布式锁,表唯一约束等
This commit is contained in:
@@ -9,6 +9,7 @@ import com.easyagents.flow.core.chain.listener.ChainEventListener;
|
||||
import com.easyagents.flow.core.chain.repository.NodeStateField;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.entity.WorkflowExecResult;
|
||||
@@ -63,6 +64,10 @@ public class ChainEventListenerForSave implements ChainEventListener {
|
||||
ChainState state = chain.getState();
|
||||
Workflow workflow = workflowService.getById(definition.getId());
|
||||
String instanceId = state.getInstanceId();
|
||||
WorkflowExecResult existed = workflowExecResultService.getByExecKey(instanceId);
|
||||
if (existed != null) {
|
||||
return;
|
||||
}
|
||||
WorkflowExecResult record = new WorkflowExecResult();
|
||||
record.setExecKey(instanceId);
|
||||
record.setWorkflowId(workflow.getId());
|
||||
@@ -74,7 +79,12 @@ public class ChainEventListenerForSave implements ChainEventListener {
|
||||
record.setStatus(state.getStatus().getValue());
|
||||
record.setCreatedKey(WorkFlowUtil.USER_KEY);
|
||||
record.setCreatedBy(WorkFlowUtil.getOperator(chain).getId().toString());
|
||||
workflowExecResultService.save(record);
|
||||
try {
|
||||
workflowExecResultService.save(record);
|
||||
} catch (DuplicateKeyException e) {
|
||||
// 多节点重试时可能并发写同一 exec_key,按幂等处理。
|
||||
log.debug("exec result already exists, execKey={}", instanceId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleChainEndEvent(ChainEndEvent event, Chain chain) {
|
||||
|
||||
@@ -5,10 +5,16 @@ import tech.easyflow.ai.mapper.BotDocumentCollectionMapper;
|
||||
import tech.easyflow.ai.service.BotDocumentCollectionService;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.time.Duration;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
|
||||
/**
|
||||
@@ -20,6 +26,13 @@ import com.mybatisflex.core.query.QueryWrapper;
|
||||
@Service
|
||||
public class BotDocumentCollectionServiceImpl extends ServiceImpl<BotDocumentCollectionMapper, BotDocumentCollection> implements BotDocumentCollectionService {
|
||||
|
||||
private static final String BOT_BINDING_LOCK_KEY_PREFIX = "easyflow:lock:bot:binding:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
public List<BotDocumentCollection> listByBotId(BigInteger botId) {
|
||||
|
||||
@@ -30,15 +43,30 @@ public class BotDocumentCollectionServiceImpl extends ServiceImpl<BotDocumentCol
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveBotAndKnowledge(BigInteger botId, BigInteger[] knowledgeIds) {
|
||||
this.remove(QueryWrapper.create().eq(BotDocumentCollection::getBotId, botId));
|
||||
List<BotDocumentCollection> list = new ArrayList<>(knowledgeIds.length);
|
||||
for (BigInteger knowledgeId : knowledgeIds) {
|
||||
BotDocumentCollection botDocumentCollection = new BotDocumentCollection();
|
||||
botDocumentCollection.setBotId(botId);
|
||||
botDocumentCollection.setDocumentCollectionId(knowledgeId);
|
||||
list.add(botDocumentCollection);
|
||||
}
|
||||
this.saveBatch(list);
|
||||
redisLockExecutor.executeWithLock(BOT_BINDING_LOCK_KEY_PREFIX + botId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
this.remove(QueryWrapper.create().eq(BotDocumentCollection::getBotId, botId));
|
||||
Set<BigInteger> uniqueKnowledgeIds = new LinkedHashSet<>();
|
||||
if (knowledgeIds != null) {
|
||||
for (BigInteger knowledgeId : knowledgeIds) {
|
||||
if (knowledgeId != null) {
|
||||
uniqueKnowledgeIds.add(knowledgeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uniqueKnowledgeIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<BotDocumentCollection> list = new ArrayList<>(uniqueKnowledgeIds.size());
|
||||
for (BigInteger knowledgeId : uniqueKnowledgeIds) {
|
||||
BotDocumentCollection botDocumentCollection = new BotDocumentCollection();
|
||||
botDocumentCollection.setBotId(botId);
|
||||
botDocumentCollection.setDocumentCollectionId(knowledgeId);
|
||||
list.add(botDocumentCollection);
|
||||
}
|
||||
this.saveBatch(list);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,15 @@ import tech.easyflow.ai.entity.BotMcp;
|
||||
import tech.easyflow.ai.mapper.BotMcpMapper;
|
||||
import tech.easyflow.ai.service.BotMcpService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 服务层实现。
|
||||
@@ -22,29 +26,53 @@ import java.util.Map;
|
||||
@Service
|
||||
public class BotMcpServiceImpl extends ServiceImpl<BotMcpMapper, BotMcp> implements BotMcpService{
|
||||
|
||||
private static final String BOT_BINDING_LOCK_KEY_PREFIX = "easyflow:lock:bot:binding:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void updateBotMcpToolIds(BigInteger botId, List<Map<String, List<List<String>>>> mcpSelectedData) {
|
||||
// 删除原来绑定的mcp
|
||||
this.remove(QueryWrapper.create().eq(BotMcp::getBotId, botId));
|
||||
for (Map<String, List<List<String>>> mcpItem : mcpSelectedData) {
|
||||
for (Map.Entry<String, List<List<String>>> entry : mcpItem.entrySet()) {
|
||||
String mcpId = entry.getKey(); // 上一级id
|
||||
List<List<String>> toolList = entry.getValue(); // 包含name和description的二维数组
|
||||
redisLockExecutor.executeWithLock(BOT_BINDING_LOCK_KEY_PREFIX + botId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
this.remove(QueryWrapper.create().eq(BotMcp::getBotId, botId));
|
||||
if (mcpSelectedData == null || mcpSelectedData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历每个工具的[name, description]
|
||||
for (List<String> toolInfo : toolList) {
|
||||
String toolName = toolInfo.get(0); // 工具名称
|
||||
String toolDesc = toolInfo.get(1); // 工具描述
|
||||
System.out.println("工具名称:" + toolName + ",描述:" + toolDesc);
|
||||
BotMcp botMcp = new BotMcp();
|
||||
botMcp.setBotId(botId);
|
||||
botMcp.setMcpId(new BigInteger(mcpId));
|
||||
botMcp.setMcpToolName(toolName);
|
||||
botMcp.setMcpToolDescription(toolDesc);
|
||||
this.save(botMcp);
|
||||
Set<String> uniqueTools = new LinkedHashSet<>();
|
||||
for (Map<String, List<List<String>>> mcpItem : mcpSelectedData) {
|
||||
for (Map.Entry<String, List<List<String>>> entry : mcpItem.entrySet()) {
|
||||
String mcpId = entry.getKey();
|
||||
List<List<String>> toolList = entry.getValue();
|
||||
if (toolList == null || toolList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (List<String> toolInfo : toolList) {
|
||||
if (toolInfo == null || toolInfo.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
String toolName = toolInfo.get(0);
|
||||
String toolDesc = toolInfo.get(1);
|
||||
if (toolName == null) {
|
||||
continue;
|
||||
}
|
||||
String uniqueKey = mcpId + "|" + toolName;
|
||||
if (!uniqueTools.add(uniqueKey)) {
|
||||
continue;
|
||||
}
|
||||
BotMcp botMcp = new BotMcp();
|
||||
botMcp.setBotId(botId);
|
||||
botMcp.setMcpId(new BigInteger(mcpId));
|
||||
botMcp.setMcpToolName(toolName);
|
||||
botMcp.setMcpToolDescription(toolDesc);
|
||||
this.save(botMcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,21 @@ package tech.easyflow.ai.service.impl;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.ai.entity.BotPlugin;
|
||||
import tech.easyflow.ai.entity.Plugin;
|
||||
import tech.easyflow.ai.mapper.BotPluginMapper;
|
||||
import tech.easyflow.ai.mapper.PluginMapper;
|
||||
import tech.easyflow.ai.service.BotPluginService;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static tech.easyflow.ai.entity.table.BotPluginTableDef.BOT_PLUGIN;
|
||||
|
||||
@@ -25,12 +30,19 @@ import static tech.easyflow.ai.entity.table.BotPluginTableDef.BOT_PLUGIN;
|
||||
@Service
|
||||
public class BotPluginServiceImpl extends ServiceImpl<BotPluginMapper, BotPlugin> implements BotPluginService {
|
||||
|
||||
private static final String BOT_BINDING_LOCK_KEY_PREFIX = "easyflow:lock:bot:binding:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Resource
|
||||
private BotPluginMapper botPluginMapper;
|
||||
|
||||
@Resource
|
||||
private PluginMapper pluginMapper;
|
||||
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
public List<Plugin> getList(String botId) {
|
||||
QueryWrapper w = QueryWrapper.create();
|
||||
@@ -58,15 +70,31 @@ public class BotPluginServiceImpl extends ServiceImpl<BotPluginMapper, BotPlugin
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveBotAndPluginTool(BigInteger botId, BigInteger[] pluginToolIds) {
|
||||
this.remove(QueryWrapper.create().eq(BotPlugin::getBotId, botId));
|
||||
List<BotPlugin> list = new ArrayList<>(pluginToolIds.length);
|
||||
for (BigInteger pluginToolId : pluginToolIds) {
|
||||
BotPlugin aiBotPluginTool = new BotPlugin();
|
||||
aiBotPluginTool.setBotId(botId);
|
||||
aiBotPluginTool.setPluginItemId(pluginToolId);
|
||||
list.add(aiBotPluginTool);
|
||||
}
|
||||
this.saveBatch(list);
|
||||
redisLockExecutor.executeWithLock(BOT_BINDING_LOCK_KEY_PREFIX + botId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
this.remove(QueryWrapper.create().eq(BotPlugin::getBotId, botId));
|
||||
|
||||
Set<BigInteger> uniquePluginToolIds = new LinkedHashSet<>();
|
||||
if (pluginToolIds != null) {
|
||||
for (BigInteger pluginToolId : pluginToolIds) {
|
||||
if (pluginToolId != null) {
|
||||
uniquePluginToolIds.add(pluginToolId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uniquePluginToolIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<BotPlugin> list = new ArrayList<>(uniquePluginToolIds.size());
|
||||
for (BigInteger pluginToolId : uniquePluginToolIds) {
|
||||
BotPlugin aiBotPluginTool = new BotPlugin();
|
||||
aiBotPluginTool.setBotId(botId);
|
||||
aiBotPluginTool.setPluginItemId(pluginToolId);
|
||||
list.add(aiBotPluginTool);
|
||||
}
|
||||
this.saveBatch(list);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,16 @@ import tech.easyflow.ai.mapper.BotWorkflowMapper;
|
||||
import tech.easyflow.ai.service.BotWorkflowService;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.time.Duration;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
|
||||
/**
|
||||
@@ -20,6 +26,13 @@ import com.mybatisflex.core.query.QueryWrapper;
|
||||
@Service
|
||||
public class BotWorkflowServiceImpl extends ServiceImpl<BotWorkflowMapper, BotWorkflow> implements BotWorkflowService {
|
||||
|
||||
private static final String BOT_BINDING_LOCK_KEY_PREFIX = "easyflow:lock:bot:binding:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
public List<BotWorkflow> listByBotId(BigInteger botId) {
|
||||
|
||||
@@ -30,15 +43,31 @@ public class BotWorkflowServiceImpl extends ServiceImpl<BotWorkflowMapper, BotWo
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveBotAndWorkflowTool(BigInteger botId, BigInteger[] workflowIds) {
|
||||
this.remove(QueryWrapper.create().eq(BotWorkflow::getBotId, botId));
|
||||
List<BotWorkflow> list = new ArrayList<>(workflowIds.length);
|
||||
for (BigInteger workflowId : workflowIds) {
|
||||
BotWorkflow botWorkflow = new BotWorkflow();
|
||||
botWorkflow.setBotId(botId);
|
||||
botWorkflow.setWorkflowId(workflowId);
|
||||
list.add(botWorkflow);
|
||||
}
|
||||
this.saveBatch(list);
|
||||
redisLockExecutor.executeWithLock(BOT_BINDING_LOCK_KEY_PREFIX + botId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
this.remove(QueryWrapper.create().eq(BotWorkflow::getBotId, botId));
|
||||
|
||||
Set<BigInteger> uniqueWorkflowIds = new LinkedHashSet<>();
|
||||
if (workflowIds != null) {
|
||||
for (BigInteger workflowId : workflowIds) {
|
||||
if (workflowId != null) {
|
||||
uniqueWorkflowIds.add(workflowId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uniqueWorkflowIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<BotWorkflow> list = new ArrayList<>(uniqueWorkflowIds.size());
|
||||
for (BigInteger workflowId : uniqueWorkflowIds) {
|
||||
BotWorkflow botWorkflow = new BotWorkflow();
|
||||
botWorkflow.setBotId(botId);
|
||||
botWorkflow.setWorkflowId(workflowId);
|
||||
list.add(botWorkflow);
|
||||
}
|
||||
this.saveBatch(list);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,13 @@
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-satoken</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-cache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
import tech.easyflow.common.entity.DatacenterQuery;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
@@ -26,21 +27,35 @@ import tech.easyflow.datacenter.service.DatacenterTableService;
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class DatacenterTableServiceImpl extends ServiceImpl<DatacenterTableMapper, DatacenterTable> implements DatacenterTableService {
|
||||
|
||||
private static final String DATACENTER_TABLE_LOCK_KEY_PREFIX = "easyflow:lock:datacenter:table:";
|
||||
private static final String DATACENTER_TABLE_CREATE_LOCK_KEY_PREFIX = "easyflow:lock:datacenter:table:create:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(15);
|
||||
|
||||
@Resource
|
||||
private DbHandleManager dbHandleManager;
|
||||
@Resource
|
||||
private DatacenterTableFieldMapper fieldsMapper;
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveTable(DatacenterTable entity, LoginAccount loginUser) {
|
||||
String lockKey = buildTableLockKey(entity, loginUser);
|
||||
redisLockExecutor.executeWithLock(lockKey, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
doSaveTable(entity, loginUser);
|
||||
});
|
||||
}
|
||||
|
||||
private void doSaveTable(DatacenterTable entity, LoginAccount loginUser) {
|
||||
DbHandleService dbHandler = dbHandleManager.getDbHandler();
|
||||
|
||||
List<DatacenterTableField> fields = entity.getFields();
|
||||
@@ -117,12 +132,19 @@ public class DatacenterTableServiceImpl extends ServiceImpl<DatacenterTableMappe
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeTable(BigInteger tableId) {
|
||||
DatacenterTable record = getById(tableId);
|
||||
dbHandleManager.getDbHandler().deleteTable(record);
|
||||
removeById(tableId);
|
||||
QueryWrapper wrapper = QueryWrapper.create();
|
||||
wrapper.eq(DatacenterTableField::getTableId, tableId);
|
||||
fieldsMapper.deleteByQuery(wrapper);
|
||||
redisLockExecutor.executeWithLock(
|
||||
DATACENTER_TABLE_LOCK_KEY_PREFIX + tableId,
|
||||
LOCK_WAIT_TIMEOUT,
|
||||
LOCK_LEASE_TIMEOUT,
|
||||
() -> {
|
||||
DatacenterTable record = getById(tableId);
|
||||
dbHandleManager.getDbHandler().deleteTable(record);
|
||||
removeById(tableId);
|
||||
QueryWrapper wrapper = QueryWrapper.create();
|
||||
wrapper.eq(DatacenterTableField::getTableId, tableId);
|
||||
fieldsMapper.deleteByQuery(wrapper);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -247,6 +269,17 @@ public class DatacenterTableServiceImpl extends ServiceImpl<DatacenterTableMappe
|
||||
return "tb_dynamic_" + tableName + "_" + id;
|
||||
}
|
||||
|
||||
private String buildTableLockKey(DatacenterTable table, LoginAccount loginUser) {
|
||||
if (table.getId() != null) {
|
||||
return DATACENTER_TABLE_LOCK_KEY_PREFIX + table.getId();
|
||||
}
|
||||
String tenant = table.getTenantId() != null
|
||||
? table.getTenantId().toString()
|
||||
: (loginUser != null && loginUser.getTenantId() != null ? loginUser.getTenantId().toString() : "0");
|
||||
String tableName = table.getTableName() == null ? "unknown" : table.getTableName();
|
||||
return DATACENTER_TABLE_CREATE_LOCK_KEY_PREFIX + tenant + ":" + tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询条件
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-base</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-cache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-satoken</artifactId>
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.job.entity.SysJob;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -21,4 +22,8 @@ public interface SysJobService extends IService<SysJob> {
|
||||
void addJob(SysJob job);
|
||||
|
||||
void deleteJob(Collection<Serializable> ids);
|
||||
|
||||
void startJob(BigInteger id);
|
||||
|
||||
void stopJob(BigInteger id);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.common.constant.enums.EnumMisfirePolicy;
|
||||
import tech.easyflow.common.constant.enums.EnumJobStatus;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
import tech.easyflow.job.entity.SysJob;
|
||||
import tech.easyflow.job.job.JobConstant;
|
||||
import tech.easyflow.job.job.QuartzJob;
|
||||
@@ -17,7 +19,9 @@ import tech.easyflow.job.util.JobUtil;
|
||||
import javax.annotation.Resource;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 系统任务表 服务层实现。
|
||||
@@ -28,11 +32,18 @@ import java.util.Collection;
|
||||
@Service
|
||||
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {
|
||||
|
||||
private static final String JOB_LOCK_KEY_PREFIX = "easyflow:lock:job:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
protected Logger log = LoggerFactory.getLogger(SysJobServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private Scheduler scheduler;
|
||||
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
public void test() {
|
||||
System.out.println("java bean 动态执行");
|
||||
@@ -92,4 +103,54 @@ public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implem
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startJob(BigInteger id) {
|
||||
redisLockExecutor.executeWithLock(JOB_LOCK_KEY_PREFIX + id, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
SysJob sysJob = this.getById(id);
|
||||
if (sysJob == null) {
|
||||
throw new IllegalStateException("任务不存在,id=" + id);
|
||||
}
|
||||
try {
|
||||
JobKey jobKey = JobUtil.getJobKey(sysJob);
|
||||
if (!scheduler.checkExists(jobKey)) {
|
||||
addJob(sysJob);
|
||||
}
|
||||
if (!Integer.valueOf(EnumJobStatus.RUNNING.getCode()).equals(sysJob.getStatus())) {
|
||||
SysJob update = new SysJob();
|
||||
update.setId(id);
|
||||
update.setStatus(EnumJobStatus.RUNNING.getCode());
|
||||
this.updateById(update);
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
log.error("启动任务失败:id={}", id, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopJob(BigInteger id) {
|
||||
redisLockExecutor.executeWithLock(JOB_LOCK_KEY_PREFIX + id, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
SysJob sysJob = this.getById(id);
|
||||
if (sysJob == null) {
|
||||
throw new IllegalStateException("任务不存在,id=" + id);
|
||||
}
|
||||
try {
|
||||
JobKey jobKey = JobUtil.getJobKey(sysJob);
|
||||
if (scheduler.checkExists(jobKey)) {
|
||||
deleteJob(Collections.singletonList(id));
|
||||
}
|
||||
if (!Integer.valueOf(EnumJobStatus.STOP.getCode()).equals(sysJob.getStatus())) {
|
||||
SysJob update = new SysJob();
|
||||
update.setId(id);
|
||||
update.setStatus(EnumJobStatus.STOP.getCode());
|
||||
this.updateById(update);
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
log.error("停止任务失败:id={}", id, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-satoken</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-cache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
|
||||
@@ -5,11 +5,14 @@ import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.options.SysOptionStore;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.util.StringUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.system.entity.SysOption;
|
||||
import tech.easyflow.system.service.SysOptionService;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
|
||||
@Component
|
||||
public class DefaultOptionStore implements SysOptionStore {
|
||||
@@ -20,28 +23,56 @@ public class DefaultOptionStore implements SysOptionStore {
|
||||
|
||||
@Override
|
||||
public void save(String key, Object value) {
|
||||
BigInteger tenantId = getTenantIdForWrite();
|
||||
if (value == null || !StringUtil.hasText(value.toString())) {
|
||||
optionService.remove(QueryWrapper.create().eq(SysOption::getKey, key));
|
||||
optionService.remove(QueryWrapper.create()
|
||||
.eq(SysOption::getTenantId, tenantId)
|
||||
.eq(SysOption::getKey, key));
|
||||
return;
|
||||
}
|
||||
|
||||
String newValue = value.toString().trim();
|
||||
SysOption option = optionService.getByOptionKey(key);
|
||||
LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
|
||||
SysOption option = optionService.getByOptionKey(key, tenantId);
|
||||
if (option == null) {
|
||||
option = new SysOption(key, newValue);
|
||||
option.setTenantId(loginAccount.getTenantId());
|
||||
optionService.save(option);
|
||||
option.setTenantId(tenantId);
|
||||
try {
|
||||
optionService.save(option);
|
||||
} catch (DuplicateKeyException e) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(SysOption::getTenantId, tenantId)
|
||||
.eq(SysOption::getKey, key);
|
||||
optionService.update(option, queryWrapper);
|
||||
}
|
||||
} else {
|
||||
option.setValue(newValue);
|
||||
QueryWrapper queryWrapper = QueryWrapper.create().eq(SysOption::getKey, key);
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(SysOption::getTenantId, tenantId)
|
||||
.eq(SysOption::getKey, key);
|
||||
optionService.update(option, queryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
SysOption option = optionService.getById(key);
|
||||
BigInteger tenantId = getTenantIdForRead();
|
||||
if (tenantId == null) {
|
||||
return null;
|
||||
}
|
||||
SysOption option = optionService.getByOptionKey(key, tenantId);
|
||||
return option != null ? option.getValue() : null;
|
||||
}
|
||||
|
||||
private BigInteger getTenantIdForWrite() {
|
||||
LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
|
||||
if (loginAccount == null || loginAccount.getTenantId() == null) {
|
||||
throw new BusinessException("未获取到租户信息,无法保存系统配置");
|
||||
}
|
||||
return loginAccount.getTenantId();
|
||||
}
|
||||
|
||||
private BigInteger getTenantIdForRead() {
|
||||
LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
|
||||
return loginAccount != null ? loginAccount.getTenantId() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package tech.easyflow.system.service;
|
||||
import tech.easyflow.system.entity.SysOption;
|
||||
import com.mybatisflex.core.service.IService;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 系统配置信息表。 服务层。
|
||||
*
|
||||
@@ -11,5 +13,9 @@ import com.mybatisflex.core.service.IService;
|
||||
*/
|
||||
public interface SysOptionService extends IService<SysOption> {
|
||||
|
||||
SysOption getByOptionKey(String key);
|
||||
SysOption getByOptionKey(String key, BigInteger tenantId);
|
||||
|
||||
default SysOption getByOptionKey(String key) {
|
||||
return getByOptionKey(key, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package tech.easyflow.system.service.impl;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
import tech.easyflow.system.entity.SysAccount;
|
||||
import tech.easyflow.system.entity.SysAccountPosition;
|
||||
import tech.easyflow.system.entity.SysAccountRole;
|
||||
@@ -13,9 +15,12 @@ import tech.easyflow.system.mapper.SysRoleMapper;
|
||||
import tech.easyflow.system.service.SysAccountService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 用户表 服务层实现。
|
||||
@@ -26,53 +31,79 @@ import java.util.List;
|
||||
@Service
|
||||
public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAccount> implements SysAccountService {
|
||||
|
||||
private static final String ACCOUNT_RELATION_LOCK_KEY_PREFIX = "easyflow:lock:sys:account:relation:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Resource
|
||||
private SysAccountRoleMapper sysAccountRoleMapper;
|
||||
@Resource
|
||||
private SysAccountPositionMapper sysAccountPositionMapper;
|
||||
@Resource
|
||||
private SysRoleMapper sysRoleMapper;
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void syncRelations(SysAccount entity) {
|
||||
if (entity == null || entity.getId() == null) {
|
||||
return;
|
||||
}
|
||||
//sync roleIds
|
||||
List<BigInteger> roleIds = entity.getRoleIds();
|
||||
if (roleIds != null) {
|
||||
QueryWrapper delW = QueryWrapper.create();
|
||||
delW.eq(SysAccountRole::getAccountId, entity.getId());
|
||||
sysAccountRoleMapper.deleteByQuery(delW);
|
||||
if (!roleIds.isEmpty()) {
|
||||
List<SysAccountRole> rows = new ArrayList<>(roleIds.size());
|
||||
roleIds.forEach(roleId -> {
|
||||
SysAccountRole row = new SysAccountRole();
|
||||
row.setAccountId(entity.getId());
|
||||
row.setRoleId(roleId);
|
||||
rows.add(row);
|
||||
});
|
||||
sysAccountRoleMapper.insertBatch(rows);
|
||||
}
|
||||
}
|
||||
redisLockExecutor.executeWithLock(
|
||||
ACCOUNT_RELATION_LOCK_KEY_PREFIX + entity.getId(),
|
||||
LOCK_WAIT_TIMEOUT,
|
||||
LOCK_LEASE_TIMEOUT,
|
||||
() -> {
|
||||
//sync roleIds
|
||||
List<BigInteger> roleIds = entity.getRoleIds();
|
||||
if (roleIds != null) {
|
||||
QueryWrapper delW = QueryWrapper.create();
|
||||
delW.eq(SysAccountRole::getAccountId, entity.getId());
|
||||
sysAccountRoleMapper.deleteByQuery(delW);
|
||||
Set<BigInteger> uniqueRoleIds = new LinkedHashSet<>(roleIds);
|
||||
if (!uniqueRoleIds.isEmpty()) {
|
||||
List<SysAccountRole> rows = new ArrayList<>(uniqueRoleIds.size());
|
||||
uniqueRoleIds.forEach(roleId -> {
|
||||
if (roleId == null) {
|
||||
return;
|
||||
}
|
||||
SysAccountRole row = new SysAccountRole();
|
||||
row.setAccountId(entity.getId());
|
||||
row.setRoleId(roleId);
|
||||
rows.add(row);
|
||||
});
|
||||
if (!rows.isEmpty()) {
|
||||
sysAccountRoleMapper.insertBatch(rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sync positionIds
|
||||
List<BigInteger> positionIds = entity.getPositionIds();
|
||||
if (positionIds != null) {
|
||||
QueryWrapper delW = QueryWrapper.create();
|
||||
delW.eq(SysAccountPosition::getAccountId, entity.getId());
|
||||
sysAccountPositionMapper.deleteByQuery(delW);
|
||||
if (!positionIds.isEmpty()) {
|
||||
List<SysAccountPosition> rows = new ArrayList<>(positionIds.size());
|
||||
positionIds.forEach(positionId -> {
|
||||
SysAccountPosition row = new SysAccountPosition();
|
||||
row.setAccountId(entity.getId());
|
||||
row.setPositionId(positionId);
|
||||
rows.add(row);
|
||||
});
|
||||
sysAccountPositionMapper.insertBatch(rows);
|
||||
}
|
||||
}
|
||||
//sync positionIds
|
||||
List<BigInteger> positionIds = entity.getPositionIds();
|
||||
if (positionIds != null) {
|
||||
QueryWrapper delW = QueryWrapper.create();
|
||||
delW.eq(SysAccountPosition::getAccountId, entity.getId());
|
||||
sysAccountPositionMapper.deleteByQuery(delW);
|
||||
Set<BigInteger> uniquePositionIds = new LinkedHashSet<>(positionIds);
|
||||
if (!uniquePositionIds.isEmpty()) {
|
||||
List<SysAccountPosition> rows = new ArrayList<>(uniquePositionIds.size());
|
||||
uniquePositionIds.forEach(positionId -> {
|
||||
if (positionId == null) {
|
||||
return;
|
||||
}
|
||||
SysAccountPosition row = new SysAccountPosition();
|
||||
row.setAccountId(entity.getId());
|
||||
row.setPositionId(positionId);
|
||||
rows.add(row);
|
||||
});
|
||||
if (!rows.isEmpty()) {
|
||||
sysAccountPositionMapper.insertBatch(rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,14 +3,20 @@ package tech.easyflow.system.service.impl;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
import tech.easyflow.system.entity.SysApiKey;
|
||||
import tech.easyflow.system.entity.SysApiKeyResourceMapping;
|
||||
import tech.easyflow.system.mapper.SysApiKeyResourceMappingMapper;
|
||||
import tech.easyflow.system.service.SysApiKeyResourceMappingService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* apikey-请求接口表 服务层实现。
|
||||
@@ -21,21 +27,43 @@ import java.util.List;
|
||||
@Service
|
||||
public class SysApiKeyResourceMappingServiceImpl extends ServiceImpl<SysApiKeyResourceMappingMapper, SysApiKeyResourceMapping> implements SysApiKeyResourceMappingService {
|
||||
|
||||
private static final String API_KEY_MAPPING_LOCK_PREFIX = "easyflow:lock:sys:apikey:mapping:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
/**
|
||||
* 批量授权apiKey接口
|
||||
* @param entity
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void authInterface(SysApiKey entity) {
|
||||
this.remove(QueryWrapper.create().eq(SysApiKeyResourceMapping::getApiKeyId, entity.getId()));
|
||||
List<SysApiKeyResourceMapping> rows = new ArrayList<>(entity.getPermissionIds().size());
|
||||
BigInteger apiKeyId = entity.getId();
|
||||
for (BigInteger resourceId : entity.getPermissionIds()) {
|
||||
SysApiKeyResourceMapping sysApiKeyResourcePermissionRelationship = new SysApiKeyResourceMapping();
|
||||
sysApiKeyResourcePermissionRelationship.setApiKeyId(apiKeyId);
|
||||
sysApiKeyResourcePermissionRelationship.setApiKeyResourceId(resourceId);
|
||||
rows.add(sysApiKeyResourcePermissionRelationship);
|
||||
if (apiKeyId == null) {
|
||||
return;
|
||||
}
|
||||
this.saveBatch(rows);
|
||||
redisLockExecutor.executeWithLock(API_KEY_MAPPING_LOCK_PREFIX + apiKeyId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
this.remove(QueryWrapper.create().eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId));
|
||||
if (entity.getPermissionIds() == null || entity.getPermissionIds().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<BigInteger> uniqueResourceIds = new LinkedHashSet<>(entity.getPermissionIds());
|
||||
List<SysApiKeyResourceMapping> rows = new ArrayList<>(uniqueResourceIds.size());
|
||||
for (BigInteger resourceId : uniqueResourceIds) {
|
||||
if (resourceId == null) {
|
||||
continue;
|
||||
}
|
||||
SysApiKeyResourceMapping sysApiKeyResourcePermissionRelationship = new SysApiKeyResourceMapping();
|
||||
sysApiKeyResourcePermissionRelationship.setApiKeyId(apiKeyId);
|
||||
sysApiKeyResourcePermissionRelationship.setApiKeyResourceId(resourceId);
|
||||
rows.add(sysApiKeyResourcePermissionRelationship);
|
||||
}
|
||||
if (!rows.isEmpty()) {
|
||||
this.saveBatch(rows);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import tech.easyflow.system.service.SysOptionService;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 系统配置信息表。 服务层实现。
|
||||
*
|
||||
@@ -17,9 +19,12 @@ import org.springframework.stereotype.Service;
|
||||
public class SysOptionServiceImpl extends ServiceImpl<SysOptionMapper, SysOption> implements SysOptionService {
|
||||
|
||||
@Override
|
||||
public SysOption getByOptionKey(String key) {
|
||||
public SysOption getByOptionKey(String key, BigInteger tenantId) {
|
||||
QueryWrapper w = QueryWrapper.create();
|
||||
w.eq(SysOption::getKey, key);
|
||||
if (tenantId != null) {
|
||||
w.eq(SysOption::getTenantId, tenantId);
|
||||
}
|
||||
return getOne(w);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
import tech.easyflow.common.constant.enums.EnumDataScope;
|
||||
import tech.easyflow.system.entity.SysAccountRole;
|
||||
import tech.easyflow.system.entity.SysRole;
|
||||
@@ -17,9 +18,12 @@ import tech.easyflow.system.service.SysAccountRoleService;
|
||||
import tech.easyflow.system.service.SysRoleService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -31,29 +35,49 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
|
||||
|
||||
private static final String ROLE_LOCK_KEY_PREFIX = "easyflow:lock:sys:role:";
|
||||
private static final String ROLE_CREATE_LOCK_KEY_PREFIX = "easyflow:lock:sys:role:create:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Resource
|
||||
private SysAccountRoleService sysAccountRoleService;
|
||||
@Resource
|
||||
private SysRoleMenuMapper sysRoleMenuMapper;
|
||||
@Resource
|
||||
private SysRoleDeptMapper sysRoleDeptMapper;
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveRoleMenu(BigInteger roleId, List<String> keys) {
|
||||
redisLockExecutor.executeWithLock(ROLE_LOCK_KEY_PREFIX + roleId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
QueryWrapper delW = QueryWrapper.create();
|
||||
delW.eq(SysRoleMenu::getRoleId, roleId);
|
||||
sysRoleMenuMapper.deleteByQuery(delW);
|
||||
|
||||
QueryWrapper delW = QueryWrapper.create();
|
||||
delW.eq(SysRoleMenu::getRoleId, roleId);
|
||||
sysRoleMenuMapper.deleteByQuery(delW);
|
||||
|
||||
List<SysRoleMenu> rows = new ArrayList<>(keys.size());
|
||||
keys.forEach(string -> {
|
||||
SysRoleMenu row = new SysRoleMenu();
|
||||
row.setRoleId(roleId);
|
||||
row.setMenuId(new BigInteger(string));
|
||||
rows.add(row);
|
||||
if (CollectionUtil.isEmpty(keys)) {
|
||||
return;
|
||||
}
|
||||
Set<BigInteger> uniqueMenuIds = new LinkedHashSet<>();
|
||||
for (String key : keys) {
|
||||
if (key != null && !key.isEmpty()) {
|
||||
uniqueMenuIds.add(new BigInteger(key));
|
||||
}
|
||||
}
|
||||
if (uniqueMenuIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<SysRoleMenu> rows = new ArrayList<>(uniqueMenuIds.size());
|
||||
for (BigInteger menuId : uniqueMenuIds) {
|
||||
SysRoleMenu row = new SysRoleMenu();
|
||||
row.setRoleId(roleId);
|
||||
row.setMenuId(menuId);
|
||||
rows.add(row);
|
||||
}
|
||||
sysRoleMenuMapper.insertBatch(rows);
|
||||
});
|
||||
sysRoleMenuMapper.insertBatch(rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,40 +95,63 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveRole(SysRole sysRole) {
|
||||
String lockKey = buildRoleLockKey(sysRole);
|
||||
redisLockExecutor.executeWithLock(lockKey, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
saveOrUpdate(sysRole);
|
||||
|
||||
saveOrUpdate(sysRole);
|
||||
|
||||
// 非自定义数据权限,则部门id集合为空
|
||||
if (!EnumDataScope.CUSTOM.getCode().equals(sysRole.getDataScope())) {
|
||||
sysRole.setDeptIds(new ArrayList<>());
|
||||
}
|
||||
|
||||
List<BigInteger> menuIds = sysRole.getMenuIds();
|
||||
List<BigInteger> deptIds = sysRole.getDeptIds();
|
||||
|
||||
QueryWrapper wrm = QueryWrapper.create();
|
||||
wrm.eq(SysRoleMenu::getRoleId, sysRole.getId());
|
||||
sysRoleMenuMapper.deleteByQuery(wrm);
|
||||
QueryWrapper wrd = QueryWrapper.create();
|
||||
wrd.eq(SysRoleDept::getRoleId, sysRole.getId());
|
||||
sysRoleDeptMapper.deleteByQuery(wrd);
|
||||
|
||||
if (CollectionUtil.isNotEmpty(menuIds)) {
|
||||
for (BigInteger menuId : menuIds) {
|
||||
SysRoleMenu roleMenu = new SysRoleMenu();
|
||||
roleMenu.setRoleId(sysRole.getId());
|
||||
roleMenu.setMenuId(menuId);
|
||||
sysRoleMenuMapper.insert(roleMenu);
|
||||
// 非自定义数据权限,则部门id集合为空
|
||||
if (!EnumDataScope.CUSTOM.getCode().equals(sysRole.getDataScope())) {
|
||||
sysRole.setDeptIds(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
if (CollectionUtil.isNotEmpty(deptIds)) {
|
||||
for (BigInteger deptId : deptIds) {
|
||||
SysRoleDept roleDept = new SysRoleDept();
|
||||
roleDept.setRoleId(sysRole.getId());
|
||||
roleDept.setDeptId(deptId);
|
||||
sysRoleDeptMapper.insert(roleDept);
|
||||
Set<BigInteger> uniqueMenuIds = new LinkedHashSet<>();
|
||||
if (CollectionUtil.isNotEmpty(sysRole.getMenuIds())) {
|
||||
uniqueMenuIds.addAll(sysRole.getMenuIds());
|
||||
}
|
||||
Set<BigInteger> uniqueDeptIds = new LinkedHashSet<>();
|
||||
if (CollectionUtil.isNotEmpty(sysRole.getDeptIds())) {
|
||||
uniqueDeptIds.addAll(sysRole.getDeptIds());
|
||||
}
|
||||
|
||||
QueryWrapper wrm = QueryWrapper.create();
|
||||
wrm.eq(SysRoleMenu::getRoleId, sysRole.getId());
|
||||
sysRoleMenuMapper.deleteByQuery(wrm);
|
||||
QueryWrapper wrd = QueryWrapper.create();
|
||||
wrd.eq(SysRoleDept::getRoleId, sysRole.getId());
|
||||
sysRoleDeptMapper.deleteByQuery(wrd);
|
||||
|
||||
if (CollectionUtil.isNotEmpty(uniqueMenuIds)) {
|
||||
for (BigInteger menuId : uniqueMenuIds) {
|
||||
if (menuId == null) {
|
||||
continue;
|
||||
}
|
||||
SysRoleMenu roleMenu = new SysRoleMenu();
|
||||
roleMenu.setRoleId(sysRole.getId());
|
||||
roleMenu.setMenuId(menuId);
|
||||
sysRoleMenuMapper.insert(roleMenu);
|
||||
}
|
||||
}
|
||||
|
||||
if (CollectionUtil.isNotEmpty(uniqueDeptIds)) {
|
||||
for (BigInteger deptId : uniqueDeptIds) {
|
||||
if (deptId == null) {
|
||||
continue;
|
||||
}
|
||||
SysRoleDept roleDept = new SysRoleDept();
|
||||
roleDept.setRoleId(sysRole.getId());
|
||||
roleDept.setDeptId(deptId);
|
||||
sysRoleDeptMapper.insert(roleDept);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String buildRoleLockKey(SysRole sysRole) {
|
||||
if (sysRole.getId() != null) {
|
||||
return ROLE_LOCK_KEY_PREFIX + sysRole.getId();
|
||||
}
|
||||
String tenantPart = sysRole.getTenantId() == null ? "0" : sysRole.getTenantId().toString();
|
||||
String roleKeyPart = sysRole.getRoleKey() == null ? "unknown" : sysRole.getRoleKey();
|
||||
return ROLE_CREATE_LOCK_KEY_PREFIX + tenantPart + ":" + roleKeyPart;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user