feat(prompt): 增加提示词模板默认配置与作用域
重构 PromptTemplate 相关服务与实体,增加 isDefault 和 defaultScope 字段以支持个人、租户和平台级别的默认模板配置。前端同步更新模板管理页面与 API 接口,并在会议创建组件中集成相关逻辑。dev_na
parent
a53d85bbec
commit
7dcf4f0646
|
|
@ -76,6 +76,7 @@ import java.time.LocalDate;
|
|||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -454,7 +455,13 @@ public class AndroidMeetingController {
|
|||
? List.of()
|
||||
: promptTemplateList.getRecords().stream()
|
||||
.filter(item -> Integer.valueOf(1).equals(item.getStatus()))
|
||||
.toList();
|
||||
.collect(Collectors.toList());
|
||||
PromptTemplate effectiveDefault = promptTemplateService.findEffectiveUserDefaultTemplate(tenantId, userId);
|
||||
if (effectiveDefault != null) {
|
||||
enabledTemplates.sort(Comparator.comparing(
|
||||
item -> !Objects.equals(item.getId(), effectiveDefault.getId())
|
||||
));
|
||||
}
|
||||
resultVo.setTemplateList(enabledTemplates);
|
||||
PageResult<List<AiModelVO>> modelList = aiModelService.pageModels(1, 1000, null, "LLM", tenantId, false);
|
||||
List<AiModelVO> enabledModels = modelList.getRecords() == null
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ public class AndroidMeetingRealtimeController {
|
|||
meetingAuthorizationService.assertCanCreateMeeting(authContext);
|
||||
RealtimeMeetingRuntimeProfile runtimeProfile = meetingRuntimeProfileResolver.resolve(
|
||||
authContext.getTenantId(),
|
||||
authContext.getUserId(),
|
||||
command == null ? null : command.getAsrModelId(),
|
||||
command == null ? null : command.getSummaryModelId(),
|
||||
command == null ? null : command.getPromptId(),
|
||||
|
|
|
|||
|
|
@ -119,6 +119,37 @@ public class PromptTemplateController {
|
|||
return ApiResponse.ok(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "设置默认提示词模板")
|
||||
@PutMapping("/{id}/default")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@Log(value = "设置默认提示词模板", type = "提示词模板管理")
|
||||
public ApiResponse<Boolean> setDefault(@PathVariable Long id) {
|
||||
LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
boolean success = promptTemplateService.setUserDefaultTemplate(
|
||||
id,
|
||||
loginUser.getTenantId(),
|
||||
loginUser.getUserId(),
|
||||
loginUser.getIsPlatformAdmin(),
|
||||
loginUser.getIsTenantAdmin()
|
||||
);
|
||||
return success ? ApiResponse.ok(true) : ApiResponse.error("模板不存在、不可用或无权限访问");
|
||||
}
|
||||
|
||||
@Operation(summary = "取消默认提示词模板")
|
||||
@DeleteMapping("/{id}/default")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@Log(value = "取消默认提示词模板", type = "提示词模板管理")
|
||||
public ApiResponse<Boolean> clearDefault(@PathVariable Long id) {
|
||||
LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
return ApiResponse.ok(promptTemplateService.clearUserDefaultTemplate(
|
||||
id,
|
||||
loginUser.getTenantId(),
|
||||
loginUser.getUserId(),
|
||||
loginUser.getIsPlatformAdmin(),
|
||||
loginUser.getIsTenantAdmin()
|
||||
));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除提示词模板")
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
|
|
|
|||
|
|
@ -30,6 +30,14 @@ public class PromptTemplateVO {
|
|||
private String hotWordGroupName;
|
||||
@Schema(description = "绑定热词列表")
|
||||
private List<String> hotWords;
|
||||
@Schema(description = "是否为当前用户默认模板")
|
||||
private Boolean isDefault;
|
||||
@Schema(description = "默认模板来源:PERSONAL-个人,TENANT-租户,PLATFORM-平台")
|
||||
private String defaultScope;
|
||||
@Schema(description = "是否为模板所属层级默认模板")
|
||||
private Boolean isTemplateDefault;
|
||||
@Schema(description = "默认模板当前是否有效")
|
||||
private Boolean defaultAvailable;
|
||||
@Schema(description = "使用次数")
|
||||
private Integer usageCount;
|
||||
@Schema(description = "提示词正文")
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ public class PromptTemplate extends BaseEntity {
|
|||
@Schema(description = "是否系统内置")
|
||||
private Integer isSystem;
|
||||
|
||||
@Schema(description = "是否为所属层级默认模板:1-是,0-否")
|
||||
private Integer isDefault;
|
||||
|
||||
@Schema(description = "创建人ID")
|
||||
private Long creatorId;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,4 +23,7 @@ public class PromptTemplateUserConfig extends BaseEntity {
|
|||
|
||||
@Schema(description = "模板ID")
|
||||
private Long templateId;
|
||||
|
||||
@Schema(description = "是否为当前用户默认模板:1-是,0-否")
|
||||
private Integer isDefault;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
|
|||
: MeetingConstants.SUMMARY_DETAIL_STANDARD;
|
||||
RealtimeMeetingRuntimeProfile runtimeProfile = runtimeProfileResolver.resolve(
|
||||
tenantId,
|
||||
creatorUserId,
|
||||
null,
|
||||
requestedSummaryModelId,
|
||||
requestedPromptId,
|
||||
|
|
@ -202,6 +203,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
|
|||
|
||||
RealtimeMeetingRuntimeProfile profile = runtimeProfileResolver.resolve(
|
||||
loginUser.getTenantId(),
|
||||
loginUser.getUserId(),
|
||||
null,
|
||||
effectiveSummaryModelId,
|
||||
effectivePromptId,
|
||||
|
|
@ -279,6 +281,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
|
|||
}
|
||||
RealtimeMeetingRuntimeProfile profile = runtimeProfileResolver.resolve(
|
||||
meeting.getTenantId(),
|
||||
loginUser.getUserId(),
|
||||
null,
|
||||
effectiveSummaryModelId,
|
||||
effectivePromptId,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
|||
|
||||
public interface MeetingRuntimeProfileResolver {
|
||||
RealtimeMeetingRuntimeProfile resolve(Long tenantId,
|
||||
Long userId,
|
||||
Long asrModelId,
|
||||
Long summaryModelId,
|
||||
Long promptId,
|
||||
|
|
|
|||
|
|
@ -17,4 +17,10 @@ public interface PromptTemplateService extends IService<PromptTemplate> {
|
|||
Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin);
|
||||
boolean updateUserTemplateStatus(Long templateId, Integer status, Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin);
|
||||
boolean isTemplateEnabledForUser(Long templateId, Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin);
|
||||
|
||||
boolean setUserDefaultTemplate(Long templateId, Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin);
|
||||
|
||||
boolean clearUserDefaultTemplate(Long templateId, Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin);
|
||||
|
||||
PromptTemplate findEffectiveUserDefaultTemplate(Long tenantId, Long userId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
String meetingSource,
|
||||
String sourceDeviceCode,
|
||||
String sourceDeviceMode) {
|
||||
RealtimeMeetingRuntimeProfile runtimeProfile = resolveCreateProfile(command, tenantId);
|
||||
RealtimeMeetingRuntimeProfile runtimeProfile = resolveCreateProfile(command, tenantId, creatorId);
|
||||
Long hostUserId = resolveHostUserId(command.getHostUserId(), creatorId);
|
||||
String resolvedCreatorName = resolveMeetingUserName(creatorId, creatorName);
|
||||
String hostName = resolveMeetingUserName(hostUserId, resolvedCreatorName);
|
||||
|
|
@ -245,7 +245,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MeetingVO createRealtimeMeeting(CreateRealtimeMeetingCommand command, Long tenantId, Long creatorId, String creatorName, String meetingSource) {
|
||||
RealtimeMeetingRuntimeProfile runtimeProfile = resolveCreateProfile(command, tenantId);
|
||||
RealtimeMeetingRuntimeProfile runtimeProfile = resolveCreateProfile(command, tenantId, creatorId);
|
||||
Long hostUserId = resolveHostUserId(command.getHostUserId(), creatorId);
|
||||
String resolvedCreatorName = resolveMeetingUserName(creatorId, creatorName);
|
||||
String hostName = resolveMeetingUserName(hostUserId, resolvedCreatorName);
|
||||
|
|
@ -299,6 +299,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
String deviceCode) {
|
||||
RealtimeMeetingRuntimeProfile runtimeProfile = meetingRuntimeProfileResolver.resolve(
|
||||
tenantId,
|
||||
creatorId,
|
||||
command.getAsrModelId(),
|
||||
command.getSummaryModelId(),
|
||||
command.getPromptId(),
|
||||
|
|
@ -1542,9 +1543,10 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
return resumeConfig;
|
||||
}
|
||||
|
||||
private RealtimeMeetingRuntimeProfile resolveCreateProfile(CreateMeetingCommand command, Long tenantId) {
|
||||
private RealtimeMeetingRuntimeProfile resolveCreateProfile(CreateMeetingCommand command, Long tenantId, Long userId) {
|
||||
return meetingRuntimeProfileResolver.resolve(
|
||||
tenantId,
|
||||
userId,
|
||||
command.getAsrModelId(),
|
||||
command.getSummaryModelId(),
|
||||
command.getPromptId(),
|
||||
|
|
@ -1560,9 +1562,10 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
|
|||
);
|
||||
}
|
||||
|
||||
private RealtimeMeetingRuntimeProfile resolveCreateProfile(CreateRealtimeMeetingCommand command, Long tenantId) {
|
||||
private RealtimeMeetingRuntimeProfile resolveCreateProfile(CreateRealtimeMeetingCommand command, Long tenantId, Long userId) {
|
||||
return meetingRuntimeProfileResolver.resolve(
|
||||
tenantId,
|
||||
userId,
|
||||
command.getAsrModelId(),
|
||||
command.getSummaryModelId(),
|
||||
command.getPromptId(),
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class MeetingRuntimeProfileResolverImpl implements MeetingRuntimeProfileR
|
|||
|
||||
@Override
|
||||
public RealtimeMeetingRuntimeProfile resolve(Long tenantId,
|
||||
Long userId,
|
||||
Long asrModelId,
|
||||
Long summaryModelId,
|
||||
Long promptId,
|
||||
|
|
@ -49,7 +50,7 @@ public class MeetingRuntimeProfileResolverImpl implements MeetingRuntimeProfileR
|
|||
long resolvedTenantId = tenantId == null ? 0L : tenantId;
|
||||
AiModelVO asrModel = resolveModel("ASR", asrModelId, resolvedTenantId);
|
||||
AiModelVO summaryModel = resolveModel("LLM", summaryModelId, resolvedTenantId);
|
||||
PromptTemplate promptTemplate = resolvePrompt(promptId, resolvedTenantId);
|
||||
PromptTemplate promptTemplate = resolvePrompt(promptId, resolvedTenantId, userId);
|
||||
|
||||
RealtimeMeetingRuntimeProfile profile = new RealtimeMeetingRuntimeProfile();
|
||||
profile.setResolvedAsrModelId(asrModel.getId());
|
||||
|
|
@ -194,7 +195,7 @@ public class MeetingRuntimeProfileResolverImpl implements MeetingRuntimeProfileR
|
|||
return entity == null ? null : entity.getId();
|
||||
}
|
||||
|
||||
private PromptTemplate resolvePrompt(Long requestedId, Long tenantId) {
|
||||
private PromptTemplate resolvePrompt(Long requestedId, Long tenantId, Long userId) {
|
||||
if (requestedId != null) {
|
||||
PromptTemplate template = promptTemplateService.getById(requestedId);
|
||||
if (template == null) {
|
||||
|
|
@ -204,7 +205,12 @@ public class MeetingRuntimeProfileResolverImpl implements MeetingRuntimeProfileR
|
|||
return template;
|
||||
}
|
||||
|
||||
PromptTemplate template = promptTemplateService.getOne(new LambdaQueryWrapper<PromptTemplate>()
|
||||
PromptTemplate template = promptTemplateService.findEffectiveUserDefaultTemplate(tenantId, userId);
|
||||
if (template != null) {
|
||||
return template;
|
||||
}
|
||||
|
||||
template = promptTemplateService.getOne(new LambdaQueryWrapper<PromptTemplate>()
|
||||
.eq(PromptTemplate::getStatus, 1)
|
||||
.eq(PromptTemplate::getIsSystem, 1)
|
||||
.and(wrapper -> wrapper.eq(PromptTemplate::getTenantId, tenantId).or().eq(PromptTemplate::getTenantId, 0L))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.imeeting.service.biz.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.imeeting.dto.biz.PromptTemplateDTO;
|
||||
|
|
@ -69,10 +70,20 @@ public class PromptTemplateServiceImpl extends ServiceImpl<PromptTemplateMapper,
|
|||
Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin) {
|
||||
LambdaQueryWrapper<PromptTemplate> wrapper = buildVisibilityWrapper(tenantId, userId, isPlatformAdmin, isTenantAdmin);
|
||||
wrapper.like(name != null && !name.isEmpty(), PromptTemplate::getTemplateName, name)
|
||||
.eq(category != null && !category.isEmpty(), PromptTemplate::getCategory, category)
|
||||
.orderByAsc(PromptTemplate::getIsSystem)
|
||||
.eq(category != null && !category.isEmpty(), PromptTemplate::getCategory, category);
|
||||
|
||||
PromptTemplateUserConfig configuredDefault = findUserDefaultConfig(tenantId, userId);
|
||||
Long configuredDefaultId = configuredDefault == null ? null : configuredDefault.getTemplateId();
|
||||
DefaultTemplateSelection defaultSelection = findEffectiveDefaultTemplate(tenantId, userId);
|
||||
PromptTemplate defaultTemplate = defaultSelection == null ? null : defaultSelection.template();
|
||||
if (defaultTemplate == null) {
|
||||
wrapper.orderByAsc(PromptTemplate::getIsSystem)
|
||||
.orderByDesc(PromptTemplate::getTenantId)
|
||||
.orderByDesc(PromptTemplate::getCreatedAt);
|
||||
.orderByDesc(PromptTemplate::getCreatedAt);
|
||||
} else {
|
||||
wrapper.last("ORDER BY CASE WHEN id = " + defaultTemplate.getId()
|
||||
+ " THEN 0 ELSE 1 END, is_system ASC, tenant_id DESC, created_at DESC");
|
||||
}
|
||||
|
||||
Page<PromptTemplate> page = this.page(new Page<>(current, size), wrapper);
|
||||
List<PromptTemplate> records = page.getRecords();
|
||||
|
|
@ -80,7 +91,16 @@ public class PromptTemplateServiceImpl extends ServiceImpl<PromptTemplateMapper,
|
|||
Map<Long, HotWordGroup> hotWordGroupMap = queryHotWordGroupMap(records.stream().map(PromptTemplate::getHotWordGroupId).toList());
|
||||
|
||||
List<PromptTemplateVO> vos = records.stream()
|
||||
.map(template -> toVO(template, effectiveStatus(template.getIsSystem(), template.getStatus(), userStatusMap.get(template.getId())), hotWordGroupMap))
|
||||
.map(template -> {
|
||||
Integer status = effectiveStatus(template.getIsSystem(), template.getStatus(), userStatusMap.get(template.getId()));
|
||||
PromptTemplateVO vo = toVO(template, status, hotWordGroupMap);
|
||||
boolean isConfiguredPersonalDefault = Objects.equals(configuredDefaultId, template.getId());
|
||||
boolean isEffectiveDefault = defaultTemplate != null && Objects.equals(defaultTemplate.getId(), template.getId());
|
||||
vo.setIsDefault(isEffectiveDefault || isConfiguredPersonalDefault);
|
||||
vo.setDefaultAvailable(isEffectiveDefault);
|
||||
vo.setDefaultScope(isEffectiveDefault ? defaultSelection.scope() : isConfiguredPersonalDefault ? DEFAULT_SCOPE_PERSONAL : null);
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<List<PromptTemplateVO>> result = new PageResult<>();
|
||||
|
|
@ -98,7 +118,17 @@ public class PromptTemplateServiceImpl extends ServiceImpl<PromptTemplateMapper,
|
|||
throw new IllegalArgumentException("模板不存在");
|
||||
}
|
||||
Map<Long, HotWordGroup> hotWordGroupMap = queryHotWordGroupMap(java.util.Collections.singletonList(template.getHotWordGroupId()));
|
||||
PromptTemplateVO vo = toVO(template, template.getStatus(), hotWordGroupMap);
|
||||
Integer userStatus = queryUserStatusMap(tenantId, userId, java.util.Collections.singletonList(template.getId()))
|
||||
.get(template.getId());
|
||||
Integer status = effectiveStatus(template.getIsSystem(), template.getStatus(), userStatus);
|
||||
PromptTemplateVO vo = toVO(template, status, hotWordGroupMap);
|
||||
PromptTemplateUserConfig configuredDefault = findUserDefaultConfig(tenantId, userId);
|
||||
DefaultTemplateSelection defaultSelection = findEffectiveDefaultTemplate(tenantId, userId);
|
||||
boolean isConfiguredPersonalDefault = configuredDefault != null && Objects.equals(configuredDefault.getTemplateId(), template.getId());
|
||||
boolean isEffectiveDefault = defaultSelection != null && Objects.equals(defaultSelection.template().getId(), template.getId());
|
||||
vo.setIsDefault(isEffectiveDefault || isConfiguredPersonalDefault);
|
||||
vo.setDefaultAvailable(isEffectiveDefault);
|
||||
vo.setDefaultScope(isEffectiveDefault ? defaultSelection.scope() : isConfiguredPersonalDefault ? DEFAULT_SCOPE_PERSONAL : null);
|
||||
vo.setHotWords(resolveHotWords(template.getHotWordGroupId()));
|
||||
return vo;
|
||||
}
|
||||
|
|
@ -156,6 +186,138 @@ public class PromptTemplateServiceImpl extends ServiceImpl<PromptTemplateMapper,
|
|||
return effectiveStatus(template.getIsSystem(), template.getStatus(), userStatus) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean setUserDefaultTemplate(Long templateId, Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin) {
|
||||
if (Boolean.TRUE.equals(isPlatformAdmin)) {
|
||||
return setSystemDefaultTemplate(templateId, 0L);
|
||||
}
|
||||
if (Boolean.TRUE.equals(isTenantAdmin)) {
|
||||
return setSystemDefaultTemplate(templateId, tenantId);
|
||||
}
|
||||
if (!isTemplateEnabledForUser(templateId, tenantId, userId, isPlatformAdmin, isTenantAdmin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
userConfigMapper.update(null, new LambdaUpdateWrapper<PromptTemplateUserConfig>()
|
||||
.eq(PromptTemplateUserConfig::getTenantId, tenantId)
|
||||
.eq(PromptTemplateUserConfig::getUserId, userId)
|
||||
.eq(PromptTemplateUserConfig::getIsDefault, 1)
|
||||
.set(PromptTemplateUserConfig::getIsDefault, 0));
|
||||
|
||||
PromptTemplateUserConfig existing = findUserConfig(tenantId, userId, templateId);
|
||||
if (existing == null) {
|
||||
PromptTemplateUserConfig entity = new PromptTemplateUserConfig();
|
||||
entity.setTenantId(tenantId);
|
||||
entity.setUserId(userId);
|
||||
entity.setTemplateId(templateId);
|
||||
entity.setStatus(1);
|
||||
entity.setIsDefault(1);
|
||||
return userConfigMapper.insert(entity) > 0;
|
||||
}
|
||||
existing.setIsDefault(1);
|
||||
return userConfigMapper.updateById(existing) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean clearUserDefaultTemplate(Long templateId, Long tenantId, Long userId, Boolean isPlatformAdmin, Boolean isTenantAdmin) {
|
||||
if (Boolean.TRUE.equals(isPlatformAdmin)) {
|
||||
return clearSystemDefaultTemplate(templateId, 0L);
|
||||
}
|
||||
if (Boolean.TRUE.equals(isTenantAdmin)) {
|
||||
return clearSystemDefaultTemplate(templateId, tenantId);
|
||||
}
|
||||
userConfigMapper.update(null, new LambdaUpdateWrapper<PromptTemplateUserConfig>()
|
||||
.eq(PromptTemplateUserConfig::getTenantId, tenantId)
|
||||
.eq(PromptTemplateUserConfig::getUserId, userId)
|
||||
.eq(PromptTemplateUserConfig::getTemplateId, templateId)
|
||||
.eq(PromptTemplateUserConfig::getIsDefault, 1)
|
||||
.set(PromptTemplateUserConfig::getIsDefault, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PromptTemplate findEffectiveUserDefaultTemplate(Long tenantId, Long userId) {
|
||||
if (tenantId == null) {
|
||||
return null;
|
||||
}
|
||||
DefaultTemplateSelection selection = findEffectiveDefaultTemplate(tenantId, userId);
|
||||
return selection == null ? null : selection.template();
|
||||
}
|
||||
|
||||
private boolean setSystemDefaultTemplate(Long templateId, Long scopeTenantId) {
|
||||
PromptTemplate template = this.getById(templateId);
|
||||
if (template == null
|
||||
|| !Integer.valueOf(1).equals(template.getIsSystem())
|
||||
|| !Objects.equals(template.getTenantId(), scopeTenantId)
|
||||
|| !Integer.valueOf(1).equals(template.getStatus())) {
|
||||
return false;
|
||||
}
|
||||
this.update(new LambdaUpdateWrapper<PromptTemplate>()
|
||||
.eq(PromptTemplate::getTenantId, scopeTenantId)
|
||||
.eq(PromptTemplate::getIsSystem, 1)
|
||||
.eq(PromptTemplate::getIsDefault, 1)
|
||||
.set(PromptTemplate::getIsDefault, 0));
|
||||
template.setIsDefault(1);
|
||||
return this.updateById(template);
|
||||
}
|
||||
|
||||
private boolean clearSystemDefaultTemplate(Long templateId, Long scopeTenantId) {
|
||||
return this.update(new LambdaUpdateWrapper<PromptTemplate>()
|
||||
.eq(PromptTemplate::getId, templateId)
|
||||
.eq(PromptTemplate::getTenantId, scopeTenantId)
|
||||
.eq(PromptTemplate::getIsSystem, 1)
|
||||
.eq(PromptTemplate::getIsDefault, 1)
|
||||
.set(PromptTemplate::getIsDefault, 0));
|
||||
}
|
||||
|
||||
private DefaultTemplateSelection findEffectiveDefaultTemplate(Long tenantId, Long userId) {
|
||||
PromptTemplateUserConfig config = findUserDefaultConfig(tenantId, userId);
|
||||
if (config != null && Integer.valueOf(1).equals(config.getStatus())) {
|
||||
PromptTemplate template = findAvailableDefaultTemplate(config.getTemplateId(), tenantId, userId);
|
||||
if (template != null) {
|
||||
return new DefaultTemplateSelection(template, DEFAULT_SCOPE_PERSONAL);
|
||||
}
|
||||
}
|
||||
PromptTemplate template = findAvailableSystemDefaultTemplate(tenantId, tenantId, userId);
|
||||
if (template != null) {
|
||||
return new DefaultTemplateSelection(template, DEFAULT_SCOPE_TENANT);
|
||||
}
|
||||
template = findAvailableSystemDefaultTemplate(0L, tenantId, userId);
|
||||
return template == null ? null : new DefaultTemplateSelection(template, DEFAULT_SCOPE_PLATFORM);
|
||||
}
|
||||
|
||||
private PromptTemplate findAvailableDefaultTemplate(Long templateId, Long tenantId, Long userId) {
|
||||
PromptTemplate template = this.getById(templateId);
|
||||
if (template == null || !Integer.valueOf(1).equals(template.getStatus())) {
|
||||
return null;
|
||||
}
|
||||
if (Integer.valueOf(0).equals(template.getIsSystem()) && !Objects.equals(template.getCreatorId(), userId)) {
|
||||
return null;
|
||||
}
|
||||
return Objects.equals(template.getTenantId(), tenantId) || Long.valueOf(0L).equals(template.getTenantId()) ? template : null;
|
||||
}
|
||||
|
||||
private PromptTemplate findAvailableSystemDefaultTemplate(Long scopeTenantId, Long userTenantId, Long userId) {
|
||||
PromptTemplate template = this.getOne(new LambdaQueryWrapper<PromptTemplate>()
|
||||
.eq(PromptTemplate::getTenantId, scopeTenantId)
|
||||
.eq(PromptTemplate::getIsSystem, 1)
|
||||
.eq(PromptTemplate::getIsDefault, 1)
|
||||
.eq(PromptTemplate::getStatus, 1)
|
||||
.last("LIMIT 1"));
|
||||
if (template == null) {
|
||||
return null;
|
||||
}
|
||||
if (userId == null) {
|
||||
return template;
|
||||
}
|
||||
PromptTemplateUserConfig config = findUserConfig(userTenantId, userId, template.getId());
|
||||
return effectiveStatus(template.getIsSystem(), template.getStatus(), config == null ? null : config.getStatus()) == 1
|
||||
? template
|
||||
: null;
|
||||
}
|
||||
|
||||
private void validateHotWordGroupBinding(Long hotWordGroupId, Long templateTenantId) {
|
||||
if (hotWordGroupId == null) {
|
||||
return;
|
||||
|
|
@ -215,6 +377,25 @@ public class PromptTemplateServiceImpl extends ServiceImpl<PromptTemplateMapper,
|
|||
return statusMap;
|
||||
}
|
||||
|
||||
private PromptTemplateUserConfig findUserConfig(Long tenantId, Long userId, Long templateId) {
|
||||
return userConfigMapper.selectOne(new LambdaQueryWrapper<PromptTemplateUserConfig>()
|
||||
.eq(PromptTemplateUserConfig::getTenantId, tenantId)
|
||||
.eq(PromptTemplateUserConfig::getUserId, userId)
|
||||
.eq(PromptTemplateUserConfig::getTemplateId, templateId)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
private PromptTemplateUserConfig findUserDefaultConfig(Long tenantId, Long userId) {
|
||||
if (tenantId == null || userId == null) {
|
||||
return null;
|
||||
}
|
||||
return userConfigMapper.selectOne(new LambdaQueryWrapper<PromptTemplateUserConfig>()
|
||||
.eq(PromptTemplateUserConfig::getTenantId, tenantId)
|
||||
.eq(PromptTemplateUserConfig::getUserId, userId)
|
||||
.eq(PromptTemplateUserConfig::getIsDefault, 1)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
private Map<Long, HotWordGroup> queryHotWordGroupMap(List<Long> hotWordGroupIds) {
|
||||
List<Long> ids = hotWordGroupIds == null ? List.of() : hotWordGroupIds.stream()
|
||||
.filter(Objects::nonNull)
|
||||
|
|
@ -271,6 +452,7 @@ public class PromptTemplateServiceImpl extends ServiceImpl<PromptTemplateMapper,
|
|||
vo.setDescription(entity.getDescription());
|
||||
vo.setCategory(entity.getCategory());
|
||||
vo.setIsSystem(entity.getIsSystem());
|
||||
vo.setIsTemplateDefault(Integer.valueOf(1).equals(entity.getIsDefault()));
|
||||
vo.setTags(entity.getTags());
|
||||
Long hotWordGroupId = entity.getHotWordGroupId();
|
||||
vo.setHotWordGroupId(hotWordGroupId);
|
||||
|
|
@ -284,4 +466,11 @@ public class PromptTemplateServiceImpl extends ServiceImpl<PromptTemplateMapper,
|
|||
vo.setUpdatedAt(entity.getUpdatedAt());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private static final String DEFAULT_SCOPE_PERSONAL = "PERSONAL";
|
||||
private static final String DEFAULT_SCOPE_TENANT = "TENANT";
|
||||
private static final String DEFAULT_SCOPE_PLATFORM = "PLATFORM";
|
||||
|
||||
private record DefaultTemplateSelection(PromptTemplate template, String scope) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ export interface PromptTemplateVO {
|
|||
hotWordGroupId?: number;
|
||||
hotWordGroupName?: string;
|
||||
hotWords?: string[];
|
||||
isDefault?: boolean;
|
||||
defaultScope?: "PERSONAL" | "TENANT" | "PLATFORM";
|
||||
isTemplateDefault?: boolean;
|
||||
defaultAvailable?: boolean;
|
||||
usageCount: number;
|
||||
promptContent: string;
|
||||
status: number;
|
||||
|
|
@ -78,3 +82,11 @@ export const updatePromptStatus = (id: number, status: number) => {
|
|||
{ params: { status } }
|
||||
);
|
||||
};
|
||||
|
||||
export const setPromptDefault = (id: number) => {
|
||||
return http.put<{ code: string; data: boolean; msg: string }>(`/api/biz/prompt/${id}/default`);
|
||||
};
|
||||
|
||||
export const clearPromptDefault = (id: number) => {
|
||||
return http.delete<{ code: string; data: boolean; msg: string }>(`/api/biz/prompt/${id}/default`);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ export const MeetingCreateDrawer: React.FC<MeetingCreateDrawerProps> = ({
|
|||
setHotWordGroups((hotWordGroupRes.data.data || []).filter((item: HotWordGroupVO) => item.status === 1));
|
||||
setUserList(users || []);
|
||||
|
||||
const defaultPrompt = activePrompts[0];
|
||||
const defaultPrompt = activePrompts.find((item: PromptTemplateVO) => item.isDefault && item.defaultAvailable);
|
||||
form.setFieldsValue({
|
||||
title: nextType === "upload" ? `文件会议 ${dayjs().format("MM-DD HH:mm")}` : `实时会议 ${dayjs().format("MM-DD HH:mm")}`,
|
||||
meetingTime: dayjs(),
|
||||
|
|
@ -517,14 +517,28 @@ export const MeetingCreateDrawer: React.FC<MeetingCreateDrawerProps> = ({
|
|||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item name="promptId" label="总结模板" rules={[{ required: true }]}>
|
||||
<Form.Item
|
||||
name="promptId"
|
||||
label="总结模板"
|
||||
extra="未选择时,系统会按当前可用模板规则自动选择"
|
||||
>
|
||||
{prompts.length > 15 ? (
|
||||
<Select placeholder="请选择总结模板" showSearch optionFilterProp="children">
|
||||
<Select allowClear placeholder="请选择总结模板" showSearch optionFilterProp="children">
|
||||
{prompts.map(p => <Option key={p.id} value={p.id}>{p.templateName}</Option>)}
|
||||
</Select>
|
||||
) : (
|
||||
<div className="meeting-create-template-grid">
|
||||
<Row gutter={[12, 12]}>
|
||||
<Col xs={24} sm={12} md={8}>
|
||||
<div
|
||||
onClick={() => form.setFieldsValue({promptId: undefined})}
|
||||
className={watchedPromptId == null ? "meeting-create-template-card is-selected" : "meeting-create-template-card"}
|
||||
>
|
||||
<div className="meeting-create-template-card__name">系统自动选择</div>
|
||||
{watchedPromptId == null &&
|
||||
<div className="meeting-create-template-card__check"><CheckOutlined/></div>}
|
||||
</div>
|
||||
</Col>
|
||||
{prompts.map(p => {
|
||||
const isSelected = watchedPromptId === p.id;
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -40,11 +40,25 @@
|
|||
gap: 4px;
|
||||
}
|
||||
|
||||
.prompt-template-name-cell > .ant-typography {
|
||||
.prompt-template-name-cell__title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.prompt-template-name-cell__title-row > .ant-typography {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.prompt-template-name-cell__title-row > .ant-tag {
|
||||
flex: 0 0 auto;
|
||||
margin: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.prompt-template-description.ant-typography {
|
||||
display: block;
|
||||
max-width: 360px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
App,
|
||||
Button,
|
||||
|
|
@ -21,16 +21,27 @@ import PageContainer from "@/components/shared/PageContainer";
|
|||
import DataListPanel from "@/components/shared/DataListPanel";
|
||||
import FormDrawer from "@/components/shared/FormDrawer";
|
||||
import SectionCard from "@/components/shared/SectionCard";
|
||||
import { CopyOutlined, DeleteOutlined, EditOutlined, EyeOutlined, PlusOutlined, SaveOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
CopyOutlined,
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
EyeOutlined,
|
||||
PlusOutlined,
|
||||
SaveOutlined,
|
||||
StarFilled,
|
||||
StarOutlined
|
||||
} from '@ant-design/icons';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDict } from '../../hooks/useDict';
|
||||
import {
|
||||
deletePromptTemplate,
|
||||
clearPromptDefault,
|
||||
getPromptDetail,
|
||||
getPromptPage,
|
||||
savePromptTemplate,
|
||||
setPromptDefault,
|
||||
updatePromptStatus,
|
||||
updatePromptTemplate,
|
||||
type PromptTemplateVO,
|
||||
|
|
@ -295,7 +306,11 @@ const PromptTemplates: React.FC = () => {
|
|||
width: 280,
|
||||
render: (_: unknown, item: PromptTemplateVO) => (
|
||||
<div className="prompt-template-name-cell">
|
||||
<Text strong ellipsis={{ tooltip: item.templateName }}>{item.templateName}</Text>
|
||||
<div className="prompt-template-name-cell__title-row">
|
||||
<Text strong ellipsis={{tooltip: item.templateName}}>{item.templateName}</Text>
|
||||
{item.isDefault ? <Tag
|
||||
color={item.defaultAvailable ? 'gold' : 'default'}>{item.defaultAvailable ? '默认' : '默认已失效'}</Tag> : null}
|
||||
</div>
|
||||
{item.description ? <Text type="secondary" className="prompt-template-description"
|
||||
ellipsis={{tooltip: item.description}}>{item.description}</Text> : null}
|
||||
</div>
|
||||
|
|
@ -345,6 +360,19 @@ const PromptTemplates: React.FC = () => {
|
|||
fixed: 'right' as const,
|
||||
render: (_: unknown, item: PromptTemplateVO) => {
|
||||
const canEdit = canManageTemplate(item);
|
||||
const canManageDefault = isPlatformAdmin
|
||||
? item.isSystem === 1 && Number(item.tenantId) === 0
|
||||
: isTenantAdmin
|
||||
? item.isSystem === 1 && Number(item.tenantId) === activeTenantId
|
||||
: item.status === 1;
|
||||
const isCurrentScopeDefault = isPlatformAdmin || isTenantAdmin
|
||||
? item.isTemplateDefault === true
|
||||
: item.isDefault === true && item.defaultScope === "PERSONAL";
|
||||
const defaultActionLabel = isPlatformAdmin
|
||||
? "平台默认"
|
||||
: isTenantAdmin
|
||||
? "租户默认"
|
||||
: "个人默认";
|
||||
return (
|
||||
<Space size={2} onClick={(e) => e.stopPropagation()}>
|
||||
<Tooltip title="查看">
|
||||
|
|
@ -361,6 +389,23 @@ const PromptTemplates: React.FC = () => {
|
|||
<Button type="text" size="small" icon={<CopyOutlined/>} onClick={() => handleOpenDrawer(item, true)}
|
||||
aria-label="以此创建模板"/>
|
||||
</Tooltip>
|
||||
{canManageDefault && (
|
||||
<Tooltip title={isCurrentScopeDefault ? `取消${defaultActionLabel}` : `设为${defaultActionLabel}`}>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={isCurrentScopeDefault ? <StarFilled/> : <StarOutlined/>}
|
||||
onClick={() => {
|
||||
const request = isCurrentScopeDefault ? clearPromptDefault(item.id) : setPromptDefault(item.id);
|
||||
request.then(() => {
|
||||
message.success(isCurrentScopeDefault ? `已取消${defaultActionLabel}` : `已设为${defaultActionLabel}`);
|
||||
void fetchData();
|
||||
});
|
||||
}}
|
||||
aria-label={isCurrentScopeDefault ? `取消${defaultActionLabel}` : `设为${defaultActionLabel}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{canEdit && (
|
||||
<Popconfirm
|
||||
title="确认删除该模板吗?"
|
||||
|
|
|
|||
Loading…
Reference in New Issue