fix: 完成系统向智能体数据链路切换
- 切换工作台、聊天历史、资源候选与公共调用到 Agent - 加固资源绑定、删除保护及发布运行并发控制 - 隔离旧 Bot 专属服务和组件并保留兼容入口
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package tech.easyflow.usercenter.controller.agent;
|
||||
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
import tech.easyflow.agent.security.AgentVisibilityQueryHelper;
|
||||
import tech.easyflow.agent.service.AgentService;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.common.annotation.UsePermission;
|
||||
import tech.easyflow.common.domain.Result;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.usercenter.model.agent.UcAgentListItemVo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户中心 Agent 查询接口。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/userCenter/agent")
|
||||
@UsePermission(moduleName = "/api/v1/agent")
|
||||
public class UcAgentController {
|
||||
|
||||
private final AgentService agentService;
|
||||
private final AgentVisibilityQueryHelper agentVisibilityQueryHelper;
|
||||
|
||||
/**
|
||||
* 创建用户中心 Agent 控制器。
|
||||
*
|
||||
* @param agentService Agent 服务
|
||||
* @param agentVisibilityQueryHelper Agent 可见性查询助手
|
||||
*/
|
||||
public UcAgentController(AgentService agentService,
|
||||
AgentVisibilityQueryHelper agentVisibilityQueryHelper) {
|
||||
this.agentService = agentService;
|
||||
this.agentVisibilityQueryHelper = agentVisibilityQueryHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户可见且已发布的 Agent。
|
||||
*
|
||||
* @return Agent 列表项
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<List<UcAgentListItemVo>> list() {
|
||||
requireCurrentAccount();
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.select(
|
||||
Agent::getId,
|
||||
Agent::getPublishedSnapshotJson
|
||||
)
|
||||
.eq(Agent::getStatus, 1)
|
||||
.eq(Agent::getPublishStatus, PublishStatus.PUBLISHED.getCode());
|
||||
agentVisibilityQueryHelper.applyReadableAccess(queryWrapper);
|
||||
queryWrapper.orderBy(Agent::getModified, false);
|
||||
List<Agent> agents = agentService.list(queryWrapper);
|
||||
if (agents == null || agents.isEmpty()) {
|
||||
return Result.ok(Collections.emptyList());
|
||||
}
|
||||
return Result.ok(agents.stream()
|
||||
.map(agent -> toListItem(agent, agentService.fromSnapshot(agent.getPublishedSnapshotJson())))
|
||||
.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并校验当前登录账号。
|
||||
*
|
||||
* @return 当前登录账号
|
||||
* @throws BusinessException 登录信息失效时抛出
|
||||
*/
|
||||
protected LoginAccount requireCurrentAccount() {
|
||||
LoginAccount account = SaTokenUtil.getLoginAccount();
|
||||
if (account == null || account.getTenantId() == null) {
|
||||
throw new BusinessException("当前登录状态失效,请重新登录后再试");
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将已发布快照转换为最小用户端列表项。
|
||||
*
|
||||
* @param liveAgent 当前 Agent 记录
|
||||
* @param publishedAgent 已发布 Agent 快照
|
||||
* @return 用户端列表项
|
||||
*/
|
||||
private UcAgentListItemVo toListItem(Agent liveAgent, Agent publishedAgent) {
|
||||
return new UcAgentListItemVo(
|
||||
liveAgent.getId(),
|
||||
publishedAgent.getName(),
|
||||
publishedAgent.getDescription(),
|
||||
publishedAgent.getAvatar()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package tech.easyflow.usercenter.model.agent;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 用户中心已发布 Agent 列表项。
|
||||
*/
|
||||
public class UcAgentListItemVo {
|
||||
|
||||
private final BigInteger id;
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final String avatar;
|
||||
|
||||
/**
|
||||
* 创建 Agent 列表项。
|
||||
*
|
||||
* @param id Agent ID
|
||||
* @param name Agent 名称
|
||||
* @param description Agent 描述
|
||||
* @param avatar Agent 头像
|
||||
*/
|
||||
public UcAgentListItemVo(BigInteger id, String name, String description, String avatar) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Agent ID。
|
||||
*
|
||||
* @return Agent ID
|
||||
*/
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Agent 名称。
|
||||
*
|
||||
* @return Agent 名称
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Agent 描述。
|
||||
*
|
||||
* @return Agent 描述
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Agent 头像。
|
||||
*
|
||||
* @return Agent 头像
|
||||
*/
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package tech.easyflow.usercenter.controller.agent;
|
||||
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
import tech.easyflow.agent.security.AgentVisibilityQueryHelper;
|
||||
import tech.easyflow.agent.service.AgentService;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.usercenter.model.agent.UcAgentListItemVo;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link UcAgentController} 用户端 Agent 可见性测试。
|
||||
*/
|
||||
public class UcAgentControllerTest {
|
||||
|
||||
/**
|
||||
* 验证列表把可见性限制下推到查询,并仅返回发布快照的展示字段。
|
||||
*/
|
||||
@Test
|
||||
public void listShouldFilterVisibilityAndReturnPublishedSummary() {
|
||||
Agent visibleAgent = liveAgent(BigInteger.ONE, "草稿名称一", "发布名称一");
|
||||
AgentService agentService = proxy(
|
||||
AgentService.class,
|
||||
(instance, method, args) -> {
|
||||
if ("list".equals(method.getName())) {
|
||||
return List.of(visibleAgent);
|
||||
}
|
||||
if ("fromSnapshot".equals(method.getName())) {
|
||||
Map<?, ?> snapshot = (Map<?, ?>) args[0];
|
||||
Agent published = new Agent();
|
||||
published.setName(String.valueOf(snapshot.get("name")));
|
||||
published.setDescription(String.valueOf(snapshot.get("description")));
|
||||
published.setAvatar(String.valueOf(snapshot.get("avatar")));
|
||||
return published;
|
||||
}
|
||||
throw new AssertionError("测试路径不应调用 AgentService." + method.getName());
|
||||
}
|
||||
);
|
||||
RecordingVisibilityQueryHelper visibilityQueryHelper = new RecordingVisibilityQueryHelper();
|
||||
LoginAccount account = new LoginAccount();
|
||||
account.setTenantId(BigInteger.ONE);
|
||||
UcAgentController controller = new TestUcAgentController(
|
||||
agentService,
|
||||
visibilityQueryHelper,
|
||||
account
|
||||
);
|
||||
|
||||
List<UcAgentListItemVo> result = controller.list().getData();
|
||||
|
||||
Assert.assertEquals(1, result.size());
|
||||
Assert.assertEquals(visibleAgent.getId(), result.get(0).getId());
|
||||
Assert.assertEquals("发布名称一", result.get(0).getName());
|
||||
Assert.assertEquals("发布描述一", result.get(0).getDescription());
|
||||
Assert.assertEquals("avatar-一", result.get(0).getAvatar());
|
||||
Assert.assertTrue(visibilityQueryHelper.applied);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建带草稿字段和发布快照的 Agent。
|
||||
*
|
||||
* @param id Agent ID
|
||||
* @param draftName 草稿名称
|
||||
* @param publishedName 发布名称
|
||||
* @return Agent
|
||||
*/
|
||||
private Agent liveAgent(BigInteger id, String draftName, String publishedName) {
|
||||
Agent agent = new Agent();
|
||||
agent.setId(id);
|
||||
agent.setName(draftName);
|
||||
String suffix = BigInteger.ONE.equals(id) ? "一" : "二";
|
||||
agent.setPublishedSnapshotJson(Map.of(
|
||||
"name", publishedName,
|
||||
"description", "发布描述" + suffix,
|
||||
"avatar", "avatar-" + suffix
|
||||
));
|
||||
return agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建接口代理。
|
||||
*
|
||||
* @param type 接口类型
|
||||
* @param handler 调用处理器
|
||||
* @param <T> 接口类型
|
||||
* @return 代理实例
|
||||
*/
|
||||
private <T> T proxy(Class<T> type, java.lang.reflect.InvocationHandler handler) {
|
||||
return type.cast(Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录可见性条件是否已下推到查询。
|
||||
*/
|
||||
private static final class RecordingVisibilityQueryHelper extends AgentVisibilityQueryHelper {
|
||||
|
||||
private boolean applied;
|
||||
|
||||
/**
|
||||
* 创建记录型可见性查询助手。
|
||||
*/
|
||||
private RecordingVisibilityQueryHelper() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void applyReadableAccess(QueryWrapper queryWrapper) {
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用固定登录账号的用户中心 Agent 控制器。
|
||||
*/
|
||||
private static final class TestUcAgentController extends UcAgentController {
|
||||
|
||||
private final LoginAccount account;
|
||||
|
||||
/**
|
||||
* 创建测试控制器。
|
||||
*
|
||||
* @param agentService Agent 服务
|
||||
* @param visibilityQueryHelper 可见性查询助手
|
||||
* @param account 当前账号
|
||||
*/
|
||||
private TestUcAgentController(AgentService agentService,
|
||||
AgentVisibilityQueryHelper visibilityQueryHelper,
|
||||
LoginAccount account) {
|
||||
super(agentService, visibilityQueryHelper);
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected LoginAccount requireCurrentAccount() {
|
||||
return account;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user