@@ -7,13 +7,61 @@ import fs from 'fs';
77import path from 'path' ;
88import { fileURLToPath } from 'url' ;
99import { Ollama } from 'ollama' ;
10+ import https from 'https' ;
1011
1112const __filename = fileURLToPath ( import . meta. url ) ;
1213const __dirname = path . dirname ( __filename ) ;
1314
1415// 读取动词库
1516const commonVerbs = JSON . parse ( fs . readFileSync ( path . join ( __dirname , 'common-verbs.json' ) , 'utf8' ) ) ;
1617
18+ // 调用 Jisho API 获取更多动词补充
19+ function searchJisho ( keyword ) {
20+ return new Promise ( ( resolve , reject ) => {
21+ https . get ( `https://jisho.org/api/v1/search/words?keyword=${ encodeURIComponent ( keyword ) } ` , ( res ) => {
22+ let data = '' ;
23+ res . on ( 'data' , chunk => data += chunk ) ;
24+ res . on ( 'end' , ( ) => {
25+ try {
26+ const parsed = JSON . parse ( data ) ;
27+ const verbs = [ ] ;
28+ if ( ! parsed . data ) return resolve ( [ ] ) ;
29+
30+ for ( const item of parsed . data ) {
31+ const senses = item . senses || [ ] ;
32+ let isVerb = false ;
33+ let meaning = '' ;
34+
35+ for ( const sense of senses ) {
36+ const pos = sense . parts_of_speech || [ ] ;
37+ if ( pos . some ( p => p . toLowerCase ( ) . includes ( 'verb' ) ) ) {
38+ isVerb = true ;
39+ meaning = sense . english_definitions . slice ( 0 , 2 ) . join ( ', ' ) ;
40+ break ;
41+ }
42+ }
43+
44+ if ( isVerb && item . japanese && item . japanese . length > 0 ) {
45+ const kanji = item . japanese [ 0 ] . word || item . japanese [ 0 ] . reading ;
46+ const kana = item . japanese [ 0 ] . reading || kanji ;
47+ verbs . push ( {
48+ kanji,
49+ kana,
50+ romaji : wanakana . toRomaji ( kana ) ,
51+ meaning
52+ } ) ;
53+ }
54+ }
55+ resolve ( verbs ) ;
56+ } catch ( e ) {
57+ reject ( e ) ;
58+ }
59+ } ) ;
60+ } ) . on ( 'error' , reject ) ;
61+ } ) ;
62+ }
63+
64+ // 初始化 Ollama
1765const app = express ( ) ;
1866const PORT = process . env . PORT || 3000 ;
1967
@@ -159,23 +207,49 @@ ${JSON.stringify(conjugationResult, null, 2)}
159207} ) ;
160208
161209// 动词自动补全 API
162- app . get ( '/api/suggest' , ( req , res ) => {
210+ app . get ( '/api/suggest' , async ( req , res ) => {
163211 try {
164212 const { q } = req . query ;
165213 if ( ! q || q . trim ( ) === '' ) {
166214 return res . json ( [ ] ) ;
167215 }
168216
169217 const query = q . toLowerCase ( ) . trim ( ) ;
170- const suggestions = commonVerbs . filter ( verb => {
218+
219+ // 1. 本地高频词库快速匹配
220+ const localSuggestions = commonVerbs . filter ( verb => {
171221 return verb . kanji . includes ( query ) ||
172222 verb . kana . includes ( query ) ||
173223 verb . romaji . includes ( query ) ||
174224 verb . meaning . includes ( query ) ;
175- } ) . slice ( 0 , 8 ) ; // 最多返回8条记录,避免前端渲染过大
225+ } ) ;
226+
227+ // 2. 并行调用 Jisho API 获取更广泛的词汇(限制等待时间)
228+ let jishoSuggestions = [ ] ;
229+ try {
230+ jishoSuggestions = await Promise . race ( [
231+ searchJisho ( query ) ,
232+ new Promise ( resolve => setTimeout ( ( ) => resolve ( [ ] ) , 800 ) ) // 800ms 超时,保证输入流畅
233+ ] ) ;
234+ } catch ( e ) {
235+ console . error ( 'Jisho API fetch failed' , e ) ;
236+ }
237+
238+ // 3. 合并去重(以 kanji 作为唯一标识)
239+ const merged = [ ...localSuggestions , ...jishoSuggestions ] ;
240+ const unique = [ ] ;
241+ const seen = new Set ( ) ;
242+
243+ for ( const verb of merged ) {
244+ if ( ! seen . has ( verb . kanji ) ) {
245+ seen . add ( verb . kanji ) ;
246+ unique . push ( verb ) ;
247+ }
248+ }
176249
177- res . json ( suggestions ) ;
250+ res . json ( unique . slice ( 0 , 8 ) ) ; // 最多返回8条记录
178251 } catch ( error ) {
252+ console . error ( error ) ;
179253 res . status ( 500 ) . json ( { error : 'Failed to fetch suggestions' } ) ;
180254 }
181255} ) ;
0 commit comments