feat: 展示 AI 资源创建人信息

- 为 Bot、工作流、知识库、插件列表补充创建人名称回填

- 在卡片中展示创建者与创建时间

- 补充后端与前端对应测试
This commit is contained in:
2026-04-13 14:58:14 +08:00
parent ae10383f17
commit 855e93ecbf
17 changed files with 642 additions and 3 deletions

View File

@@ -29,6 +29,9 @@ public class Bot extends BotBase {
@Column(ignore = true)
private String displayPublishStatus;
@Column(ignore = true)
private String createdByName;
public boolean isAnonymousEnabled() {
Map<String, Object> options = getOptions();
if (options == null) {
@@ -62,4 +65,22 @@ public class Bot extends BotBase {
this.displayPublishStatus = displayPublishStatus;
}
/**
* 获取创建人展示名称。
*
* @return 创建人展示名称
*/
public String getCreatedByName() {
return createdByName;
}
/**
* 设置创建人展示名称。
*
* @param createdByName 创建人展示名称
*/
public void setCreatedByName(String createdByName) {
this.createdByName = createdByName;
}
}

View File

@@ -37,6 +37,9 @@ public class DocumentCollection extends DocumentCollectionBase implements Visibi
@Column(ignore = true)
private String displayPublishStatus;
@Column(ignore = true)
private String createdByName;
public static final String TYPE_DOCUMENT = "DOCUMENT";
public static final String TYPE_FAQ = "FAQ";
@@ -169,4 +172,22 @@ public class DocumentCollection extends DocumentCollectionBase implements Visibi
public void setDisplayPublishStatus(String displayPublishStatus) {
this.displayPublishStatus = displayPublishStatus;
}
/**
* 获取创建人展示名称。
*
* @return 创建人展示名称
*/
public String getCreatedByName() {
return createdByName;
}
/**
* 设置创建人展示名称。
*
* @param createdByName 创建人展示名称
*/
public void setCreatedByName(String createdByName) {
this.createdByName = createdByName;
}
}

View File

@@ -31,6 +31,9 @@ public class Plugin extends PluginBase {
@com.mybatisflex.annotation.Column(ignore = true)
private String reasonMessage;
@com.mybatisflex.annotation.Column(ignore = true)
private String createdByName;
public String getTitle() {
return this.getName();
}
@@ -74,4 +77,22 @@ public class Plugin extends PluginBase {
public void setReasonMessage(String reasonMessage) {
this.reasonMessage = reasonMessage;
}
/**
* 获取创建人展示名称。
*
* @return 创建人展示名称
*/
public String getCreatedByName() {
return createdByName;
}
/**
* 设置创建人展示名称。
*
* @param createdByName 创建人展示名称
*/
public void setCreatedByName(String createdByName) {
this.createdByName = createdByName;
}
}

View File

@@ -26,6 +26,9 @@ public class Workflow extends WorkflowBase implements VisibilityResource {
@Column(ignore = true)
private String displayPublishStatus;
@Column(ignore = true)
private String createdByName;
public Tool toFunction(boolean needEnglishName) {
return new WorkflowTool(this, needEnglishName);
}
@@ -57,4 +60,22 @@ public class Workflow extends WorkflowBase implements VisibilityResource {
public void setDisplayPublishStatus(String displayPublishStatus) {
this.displayPublishStatus = displayPublishStatus;
}
/**
* 获取创建人展示名称。
*
* @return 创建人展示名称
*/
public String getCreatedByName() {
return createdByName;
}
/**
* 设置创建人展示名称。
*
* @param createdByName 创建人展示名称
*/
public void setCreatedByName(String createdByName) {
this.createdByName = createdByName;
}
}

View File

@@ -40,5 +40,17 @@
<groupId>tech.easyflow</groupId>
<artifactId>easyflow-module-log</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -10,6 +10,7 @@ import tech.easyflow.system.entity.vo.SysAccountImportResultVo;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Map;
/**
* 用户表 服务层。
@@ -19,6 +20,14 @@ import java.util.Collection;
*/
public interface SysAccountService extends IService<SysAccount> {
/**
* 批量解析账号展示名称。
*
* @param accountIds 账号 ID 集合
* @return 账号 ID 到展示名称的映射,名称优先使用昵称,其次登录名,最后回退为 ID 字符串
*/
Map<BigInteger, String> resolveDisplayNameMap(Collection<BigInteger> accountIds);
void syncRelations(SysAccount entity);
SysAccount getByUsername(String userKey);

View File

@@ -55,6 +55,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
@@ -110,6 +111,57 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
@Resource
private PlatformTransactionManager transactionManager;
/**
* 批量解析账号展示名称。
*
* @param accountIds 账号 ID 集合
* @return 账号 ID 到展示名称的映射
*/
@Override
public Map<BigInteger, String> resolveDisplayNameMap(Collection<BigInteger> accountIds) {
if (accountIds == null || accountIds.isEmpty()) {
return Collections.emptyMap();
}
LinkedHashSet<BigInteger> normalizedIds = accountIds.stream()
.filter(Objects::nonNull)
.collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
if (normalizedIds.isEmpty()) {
return Collections.emptyMap();
}
QueryWrapper queryWrapper = QueryWrapper.create()
.select(SysAccount::getId, SysAccount::getNickname, SysAccount::getLoginName)
.in(SysAccount::getId, normalizedIds);
List<SysAccount> accounts = list(queryWrapper);
LinkedHashMap<BigInteger, String> displayNameMap = new LinkedHashMap<>(normalizedIds.size());
for (SysAccount account : accounts) {
if (account == null || account.getId() == null) {
continue;
}
displayNameMap.put(account.getId(), resolveDisplayName(account));
}
normalizedIds.forEach(id -> displayNameMap.putIfAbsent(id, id.toString()));
return displayNameMap;
}
/**
* 解析单个账号的展示名称。
*
* @param account 账号实体
* @return 展示名称
*/
private String resolveDisplayName(SysAccount account) {
String nickname = trimToNull(account.getNickname());
if (StringUtil.hasText(nickname)) {
return nickname;
}
String loginName = trimToNull(account.getLoginName());
if (StringUtil.hasText(loginName)) {
return loginName;
}
return account.getId() == null ? "" : account.getId().toString();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void syncRelations(SysAccount entity) {

View File

@@ -0,0 +1,54 @@
package tech.easyflow.system.service.impl;
import com.mybatisflex.core.query.QueryWrapper;
import org.junit.Test;
import tech.easyflow.system.entity.SysAccount;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* {@link SysAccountServiceImpl} 测试。
*/
public class SysAccountServiceImplTest {
/**
* 验证展示名解析优先级与缺失数据回退。
*/
@Test
public void shouldResolveDisplayNameByNicknameThenLoginNameThenId() {
SysAccountServiceImpl service = spy(new SysAccountServiceImpl());
SysAccount nicknameAccount = new SysAccount();
nicknameAccount.setId(BigInteger.ONE);
nicknameAccount.setNickname("管理员");
nicknameAccount.setLoginName("admin");
SysAccount loginNameAccount = new SysAccount();
loginNameAccount.setId(BigInteger.valueOf(2));
loginNameAccount.setNickname(" ");
loginNameAccount.setLoginName("operator");
doReturn(List.of(nicknameAccount, loginNameAccount))
.when(service)
.list(any(QueryWrapper.class));
Map<BigInteger, String> displayNameMap = service.resolveDisplayNameMap(
List.of(BigInteger.ONE, BigInteger.valueOf(2), BigInteger.valueOf(3))
);
assertEquals("管理员", displayNameMap.get(BigInteger.ONE));
assertEquals("operator", displayNameMap.get(BigInteger.valueOf(2)));
assertEquals("3", displayNameMap.get(BigInteger.valueOf(3)));
verify(service, times(1)).list(any(QueryWrapper.class));
}
}