diff --git a/src/app/(features)/dictionary/DictionaryPage.tsx b/src/app/(features)/dictionary/DictionaryPage.tsx index 75dc6f7..3dfbe1e 100644 --- a/src/app/(features)/dictionary/DictionaryPage.tsx +++ b/src/app/(features)/dictionary/DictionaryPage.tsx @@ -2,16 +2,15 @@ import { useState, useEffect } from "react"; import Container from "@/components/ui/Container"; -import { lookUp } from "@/lib/server/bigmodel/dictionaryActions"; -import { toast } from "sonner"; import { authClient } from "@/lib/auth-client"; import { Folder } from "../../../../generated/prisma/browser"; import { getFoldersByUserId } from "@/lib/server/services/folderService"; -import { DictLookUpResponse, isDictErrorResponse } from "./types"; +import { DictLookUpResponse } from "./types"; import { SearchForm } from "./SearchForm"; import { SearchResult } from "./SearchResult"; import { useTranslations } from "next-intl"; import { POPULAR_LANGUAGES } from "./constants"; +import { performDictionaryLookup } from "./utils"; export default function Dictionary() { const t = useTranslations("dictionary"); @@ -52,28 +51,22 @@ export default function Dictionary() { setHasSearched(true); setSearchResult(null); - try { - // 使用查询语言和释义语言的 nativeName - const result = await lookUp({ + const result = await performDictionaryLookup( + { text: searchQuery, - definitionLang: getNativeName(definitionLang), - queryLang: getNativeName(queryLang) - }) + queryLang: getNativeName(queryLang), + definitionLang: getNativeName(definitionLang) + }, + t + ); - // 检查是否为错误响应 - if (isDictErrorResponse(result)) { - toast.error(result.error); - setSearchResult(null); - } else { - setSearchResult(result); - } - } catch (error) { - console.error("词典查询失败:", error); - toast.error(t("lookupFailed")); + if (result.success && result.data) { + setSearchResult(result.data); + } else { setSearchResult(null); - } finally { - setIsSearching(false); } + + setIsSearching(false); }; return ( @@ -111,7 +104,7 @@ export default function Dictionary() { )} - {!isSearching && searchResult && !isDictErrorResponse(searchResult) && ( + {!isSearching && searchResult && ( { onSearchingChange(true); - try { - const result = await lookUp({ + const result = await performDictionaryLookup( + { text: searchQuery, - definitionLang: getNativeName(definitionLang), queryLang: getNativeName(queryLang), + definitionLang: getNativeName(definitionLang), forceRelook: true - }); + }, + t + ); - if (isDictErrorResponse(result)) { - toast.error(result.error); - } else { - onResultUpdate(result); - toast.success(t("relookupSuccess")); - } - } catch (error) { - console.error("词典重新查询失败:", error); - toast.error(t("lookupFailed")); - } finally { - onSearchingChange(false); + if (result.success && result.data) { + onResultUpdate(result.data); } + + onSearchingChange(false); }; const handleSave = () => { diff --git a/src/app/(features)/dictionary/utils.ts b/src/app/(features)/dictionary/utils.ts new file mode 100644 index 0000000..f7893af --- /dev/null +++ b/src/app/(features)/dictionary/utils.ts @@ -0,0 +1,51 @@ +import { toast } from "sonner"; +import { lookUp } from "@/lib/server/bigmodel/dictionaryActions"; +import { + DictWordResponse, + DictPhraseResponse, +} from "./types"; + +interface LookupOptions { + text: string; + queryLang: string; + definitionLang: string; + forceRelook?: boolean; +} + +interface LookupResult { + success: boolean; + data?: DictWordResponse | DictPhraseResponse; + error?: string; +} + +/** + * 执行词典查询的通用函数 + * @param options - 查询选项 + * @param t - 翻译函数 + * @returns 查询结果 + */ +export async function performDictionaryLookup( + options: LookupOptions, + t?: (key: string) => string +): Promise { + const { text, queryLang, definitionLang, forceRelook = false } = options; + + try { + const result = await lookUp({ + text, + queryLang, + definitionLang, + forceRelook + }); + + // 成功时显示提示(仅强制重新查询时) + if (forceRelook && t) { + toast.success(t("relookupSuccess")); + } + + return { success: true, data: result }; + } catch (error) { + toast.error(String(error)); + return { success: false, error: String(error) }; + } +} diff --git a/src/lib/server/bigmodel/dictionary/orchestrator.ts b/src/lib/server/bigmodel/dictionary/orchestrator.ts index aeff72a..c2ecff5 100644 --- a/src/lib/server/bigmodel/dictionary/orchestrator.ts +++ b/src/lib/server/bigmodel/dictionary/orchestrator.ts @@ -24,16 +24,12 @@ export async function executeDictionaryLookup( // 代码层面验证:输入是否有效 if (!analysis.isValid) { console.log("[阶段1] 输入无效:", analysis.reason); - return { - error: analysis.reason || "无效输入", - }; + throw analysis.reason || "无效输入"; } if (analysis.isEmpty) { console.log("[阶段1] 输入为空"); - return { - error: "输入为空", - }; + throw "输入为空"; } console.log("[阶段1] 输入分析完成:", analysis); @@ -65,9 +61,7 @@ export async function executeDictionaryLookup( // 代码层面验证:标准形式不能为空 if (!standardFormResult.standardForm) { console.error("[阶段3] 标准形式为空"); - return { - error: "无法生成标准形式", - }; + throw "无法生成标准形式"; } console.log("[阶段3] 标准形式生成完成:", standardFormResult); @@ -99,8 +93,6 @@ export async function executeDictionaryLookup( // 任何阶段失败都返回错误(包含 reason) const errorMessage = error instanceof Error ? error.message : "未知错误"; - return { - error: errorMessage, - }; + throw errorMessage; } } diff --git a/src/lib/server/bigmodel/dictionaryActions.ts b/src/lib/server/bigmodel/dictionaryActions.ts index f2bb7c3..40014d9 100644 --- a/src/lib/server/bigmodel/dictionaryActions.ts +++ b/src/lib/server/bigmodel/dictionaryActions.ts @@ -2,12 +2,11 @@ import { executeDictionaryLookup } from "./dictionary"; import { createLookUp, createPhrase, createWord, createPhraseEntry, createWordEntry, selectLastLookUp } from "../services/dictionaryService"; -import { DictLookUpRequest, DictWordResponse, isDictErrorResponse, isDictPhraseResponse, isDictWordResponse, type DictLookUpResponse } from "@/lib/shared"; -import { text } from "node:stream/consumers"; +import { DictLookUpRequest, DictWordResponse, isDictPhraseResponse, isDictWordResponse, type DictLookUpResponse } from "@/lib/shared"; +import { lookUpValidation } from "@/lib/shared/validations/dictionaryValidations"; const saveResult = async (req: DictLookUpRequest, res: DictLookUpResponse) => { - if (isDictErrorResponse(res)) return; - else if (isDictPhraseResponse(res)) { + if (isDictPhraseResponse(res)) { // 先创建 Phrase const phrase = await createPhrase({ standardForm: res.standardForm, @@ -83,62 +82,59 @@ export const lookUp = async (req: DictLookUpRequest): Promise 0 && "ipa" in entries[0] && "partOfSpeech" in entries[0]; } @@ -57,7 +44,6 @@ export function isDictWordResponse( export function isDictPhraseResponse( response: DictLookUpResponse ): response is DictPhraseResponse { - if (isDictErrorResponse(response)) return false; const entries = (response as DictWordResponse | DictPhraseResponse).entries; return entries.length > 0 && !("ipa" in entries[0] || "partOfSpeech" in entries[0]); } diff --git a/src/lib/shared/validations/dictionaryValidations.ts b/src/lib/shared/validations/dictionaryValidations.ts new file mode 100644 index 0000000..e4dc2c3 --- /dev/null +++ b/src/lib/shared/validations/dictionaryValidations.ts @@ -0,0 +1,22 @@ +import { DictLookUpRequest } from "@/lib/shared"; + +export const lookUpValidation = (req: DictLookUpRequest) => { + const { + text, + queryLang, + definitionLang, + } = req; + + if (text.length > 30) + throw Error("The input should not exceed 30 characters."); + if (queryLang.length > 20) + throw Error("The query language should not exceed 20 characters."); + if (definitionLang.length > 20) + throw Error("The definition language should not exceed 20 characters."); + if (queryLang.length > 20) + throw Error("The query language should not exceed 20 characters."); + if (queryLang.length > 20) + throw Error("The query language should not exceed 20 characters."); + if (queryLang.length > 20) + throw Error("The query language should not exceed 20 characters."); +};