feat: 添加热词组筛选功能并更新相关API和前端逻辑

- 在 `HotWords.tsx` 中添加热词组筛选选项,并更新 `useEffect` 依赖
- 更新 `hotword.ts` 和 `HotWordController.java` 以支持 `hotWordGroupId` 参数
- 在 `MeetingDetail.tsx` 中添加 `getPinyinSuggestion` API 调用,优化热词创建逻辑
dev_na
chenhao 2026-04-22 16:06:50 +08:00
parent 2d788bac75
commit b36a08adc7
4 changed files with 34 additions and 10 deletions

View File

@ -80,6 +80,7 @@ public class HotWordController {
@RequestParam(defaultValue = "10") Integer size, @RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String word, @RequestParam(required = false) String word,
@RequestParam(required = false) String category, @RequestParam(required = false) String category,
@RequestParam(required = false) Long hotWordGroupId,
@RequestParam(required = false) Long tenantId) { @RequestParam(required = false) Long tenantId) {
LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Long targetTenantId = Boolean.TRUE.equals(loginUser.getIsPlatformAdmin()) ? tenantId : null; Long targetTenantId = Boolean.TRUE.equals(loginUser.getIsPlatformAdmin()) ? tenantId : null;
@ -87,6 +88,7 @@ public class HotWordController {
LambdaQueryWrapper<HotWord> wrapper = new LambdaQueryWrapper<HotWord>() LambdaQueryWrapper<HotWord> wrapper = new LambdaQueryWrapper<HotWord>()
.like(word != null && !word.isEmpty(), HotWord::getWord, word) .like(word != null && !word.isEmpty(), HotWord::getWord, word)
.eq(category != null && !category.isEmpty(), HotWord::getCategory, category) .eq(category != null && !category.isEmpty(), HotWord::getCategory, category)
.eq(hotWordGroupId != null, HotWord::getHotWordGroupId, hotWordGroupId)
.orderByDesc(HotWord::getCreatedAt); .orderByDesc(HotWord::getCreatedAt);
wrapper.eq(targetTenantId != null, HotWord::getTenantId, targetTenantId); wrapper.eq(targetTenantId != null, HotWord::getTenantId, targetTenantId);

View File

@ -35,6 +35,7 @@ export const getHotWordPage = (params: {
size: number; size: number;
word?: string; word?: string;
category?: string; category?: string;
hotWordGroupId?: number;
tenantId?: number; tenantId?: number;
}) => { }) => {
return http.get<{ code: string; data: { records: HotWordVO[]; total: number }; msg: string }>( return http.get<{ code: string; data: { records: HotWordVO[]; total: number }; msg: string }>(

View File

@ -83,6 +83,7 @@ const HotWords: React.FC = () => {
const [size, setSize] = useState(10); const [size, setSize] = useState(10);
const [searchWord, setSearchWord] = useState(""); const [searchWord, setSearchWord] = useState("");
const [searchCategory, setSearchCategory] = useState<string | undefined>(undefined); const [searchCategory, setSearchCategory] = useState<string | undefined>(undefined);
const [searchGroupId, setSearchGroupId] = useState<number | undefined>(undefined);
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
const [editingId, setEditingId] = useState<number | null>(null); const [editingId, setEditingId] = useState<number | null>(null);
@ -103,7 +104,7 @@ const HotWords: React.FC = () => {
useEffect(() => { useEffect(() => {
void fetchData(); void fetchData();
}, [current, searchCategory, searchWord, size]); }, [current, searchCategory, searchGroupId, searchWord, size]);
useEffect(() => { useEffect(() => {
void loadGroupOptions(); void loadGroupOptions();
@ -117,6 +118,7 @@ const HotWords: React.FC = () => {
size, size,
word: searchWord, word: searchWord,
category: searchCategory, category: searchCategory,
hotWordGroupId: searchGroupId,
tenantId: isPlatformAdmin ? activeTenantId : undefined, tenantId: isPlatformAdmin ? activeTenantId : undefined,
}); });
if (res.data?.data) { if (res.data?.data) {
@ -396,6 +398,16 @@ const HotWords: React.FC = () => {
</Option> </Option>
))} ))}
</Select> </Select>
<Select
placeholder="按热词组筛选"
style={{ width: 180 }}
allowClear
options={groupOptions.map((item) => ({ label: item.groupName, value: item.id }))}
onChange={(value) => {
setSearchGroupId(value);
setCurrent(1);
}}
/>
<Input <Input
placeholder="搜索热词原文" placeholder="搜索热词原文"
prefix={<SearchOutlined />} prefix={<SearchOutlined />}

View File

@ -42,7 +42,7 @@ import {
updateSpeakerInfo, updateSpeakerInfo,
} from '../../api/business/meeting'; } from '../../api/business/meeting';
import { getAiModelDefault, getAiModelPage, AiModelVO } from '../../api/business/aimodel'; import { getAiModelDefault, getAiModelPage, AiModelVO } from '../../api/business/aimodel';
import { getHotWordPage, saveHotWord } from '../../api/business/hotword'; import { getHotWordPage, getPinyinSuggestion, saveHotWord } from '../../api/business/hotword';
import { getPromptPage, PromptTemplateVO } from '../../api/business/prompt'; import { getPromptPage, PromptTemplateVO } from '../../api/business/prompt';
import { listUsers } from '../../api'; import { listUsers } from '../../api';
import { useDict } from '../../hooks/useDict'; import { useDict } from '../../hooks/useDict';
@ -977,15 +977,24 @@ const MeetingDetail: React.FC = () => {
await Promise.all( await Promise.all(
toCreate.map((word) => toCreate.map((word) =>
saveHotWord({ (async () => {
word, let pinyinList: string[] = [];
pinyinList: [], try {
matchStrategy: 1, const pinyinRes = await getPinyinSuggestion(word);
category: '', pinyinList = (pinyinRes.data?.data || []).map((item) => item.trim()).filter(Boolean);
weight: 2, } catch {
status: 1, pinyinList = [];
}
return saveHotWord({
word,
pinyinList,
matchStrategy: 1,
category: '',
weight: 2,
status: 1,
remark: meeting ? `来源于会议:${meeting.title}` : '来源于会议关键词', remark: meeting ? `来源于会议:${meeting.title}` : '来源于会议关键词',
}), });
})(),
), ),
); );