WbwLookupController.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Models\UserDict;
  4. use App\Models\DictInfo;
  5. use App\Models\WbwTemplate;
  6. use App\Models\Channel;
  7. use Illuminate\Http\Request;
  8. use App\Tools\CaseMan;
  9. use Illuminate\Support\Facades\Log;
  10. use Illuminate\Support\Facades\Cache;
  11. use App\Tools\RedisClusters;
  12. use App\Http\Api\DictApi;
  13. use App\Http\Api\AuthApi;
  14. class WbwLookupController extends Controller
  15. {
  16. private $dictList = [
  17. '85dcc61c-c9e1-4ae0-9b44-cd6d9d9f0d01',//社区汇总
  18. '4d3a0d92-0adc-4052-80f5-512a2603d0e8',// system irregular
  19. '8359757e-9575-455b-a772-cc6f036caea0',// system sandhi
  20. '61f23efb-b526-4a8e-999e-076965034e60',// pali myanmar grammar
  21. 'eae9fd6f-7bac-4940-b80d-ad6cd6f433bf',// Concise P-E Dict
  22. '2f93d0fe-3d68-46ee-a80b-11fa445a29c6',// unity
  23. 'beb45062-7c20-4047-bcd4-1f636ba443d1',// U Hau Sein
  24. '8833de18-0978-434c-b281-a2e7387f69be',// 巴汉增订
  25. '3acf0c0f-59a7-4d25-a3d9-bf394a266ebd',// 汉译パーリ语辞典-黃秉榮
  26. '9ce6a53b-e28f-4fb7-b69d-b35fd5d76a24',//缅英字典
  27. ];
  28. /**
  29. * Create a new command instance.
  30. *
  31. * @return void
  32. */
  33. private function initSysDict()
  34. {
  35. // system regular
  36. $this->dictList[] = DictApi::getSysDict('system_regular');
  37. $this->dictList[] = DictApi::getSysDict('robot_compound');
  38. $this->dictList[] = DictApi::getSysDict('community');
  39. $this->dictList[] = DictApi::getSysDict('community_extract');
  40. }
  41. /**
  42. * Display a listing of the resource.
  43. * @param \Illuminate\Http\Request $request
  44. *
  45. * @return \Illuminate\Http\Response
  46. */
  47. public function index(Request $request)
  48. {
  49. //
  50. $startAt = microtime(true)*1000;
  51. $this->initSysDict();
  52. $words = \explode(',',$request->get("word"));
  53. $bases = \explode(',',$request->get("base"));
  54. # 查询深度
  55. $deep = $request->get("deep",2);
  56. $result = $this->lookup($words,$bases,$deep);
  57. $endAt = microtime(true)*1000;
  58. return $this->ok(["rows"=>$result,
  59. "count"=>count($result),
  60. "time"=>(int)($endAt-$startAt)]);
  61. }
  62. //查用户字典获取全部结果
  63. public function lookup($words,$bases,$deep){
  64. $wordPool = array();
  65. $output = array();
  66. foreach ($words as $word) {
  67. $wordPool[$word] = ['base' => false,'done' => false,'apply' => false];
  68. }
  69. foreach ($bases as $base) {
  70. $wordPool[$base] = ['base' => true,'done' => false,'apply' => false];
  71. }
  72. /**
  73. * 先查询字典名称
  74. */
  75. $dict_info = DictInfo::whereIn('id',$this->dictList)->select('id','shortname')->get();
  76. $dict_name = [];
  77. foreach ($dict_info as $key => $value) {
  78. # code...
  79. $dict_name[$value->id] = $value->shortname;
  80. }
  81. $caseman = new CaseMan();
  82. for ($i=0; $i < $deep; $i++) {
  83. $newBase = array();
  84. $newWords = [];
  85. foreach ($wordPool as $word => $info) {
  86. # code...
  87. if($info['done'] === false){
  88. $newWords[] = $word;
  89. $wordPool[$word]['done'] = true;
  90. }
  91. }
  92. $data = UserDict::whereIn('word',$newWords)
  93. ->whereIn('dict_id',$this->dictList)
  94. ->leftJoin('dict_infos', 'user_dicts.dict_id', '=', 'dict_infos.id')
  95. ->orderBy('confidence','desc')
  96. ->get();
  97. foreach ($data as $row) {
  98. # code...
  99. array_push($output,$row);
  100. if(!empty($row->parent) && !isset($wordPool[$row->parent]) ){
  101. //将parent 插入待查询列表
  102. $wordPool[$row->parent] = ['base' => true,'done' => false,'apply' => false];
  103. }
  104. }
  105. //处理查询结果中的拆分信息
  106. $newWordPart = array();
  107. foreach ($wordPool as $word => $info) {
  108. if(!empty($info['factors'])){
  109. $factors = \explode('+',$info['factors']);
  110. foreach ($factors as $factor) {
  111. # 将没有的拆分放入单词查询列表
  112. if(!isset($wordPool[$factor])){
  113. $wordPool[$factor] = ['base' => true,'done' => false,'apply' => false];
  114. }
  115. }
  116. }
  117. }
  118. }
  119. return $output;
  120. }
  121. /**
  122. *
  123. */
  124. private function langCheck($query,$lang){
  125. if($query===[]){
  126. return true;
  127. }else{
  128. if(in_array(strtolower($lang),$query)){
  129. return true;
  130. }else{
  131. $langFamily = explode('-',$lang)[0];
  132. foreach ($query as $value) {
  133. if(strpos($value,$langFamily) !== false){
  134. return true;
  135. }
  136. }
  137. }
  138. }
  139. return false;
  140. }
  141. private function wbwPreference($word,$field,$userId){
  142. $prefix = 'wbw-preference';
  143. $fieldMap = [
  144. 'type'=>1,
  145. 'grammar'=>2,
  146. 'meaning'=>3,
  147. 'factors'=>4,
  148. 'factorMeaning'=>5,
  149. 'parent'=>6,
  150. 'part'=>7,
  151. 'case'=>8,
  152. ];
  153. $fieldId = $fieldMap[$field];
  154. $myPreference = RedisClusters::get("{$prefix}/{$word}/{$fieldId}/{$userId}");
  155. if(!empty($myPreference)){
  156. Log::debug($word.'命中我的wbw-'.$field,['data'=>$myPreference]);
  157. return ['value'=>$myPreference,'status'=>5];
  158. }else{
  159. $myPreference = RedisClusters::get("{$prefix}/{$word}/3/0");
  160. if(!empty($myPreference)){
  161. Log::debug($word.'命中社区wbw-'.$field,['data'=>$myPreference]);
  162. return ['value'=>$myPreference,'status'=>5];
  163. }
  164. }
  165. //Log::debug($word.'未命中'.$field);
  166. return false;
  167. }
  168. /**
  169. * 自动查词
  170. *
  171. * @param \Illuminate\Http\Request $request
  172. * @return \Illuminate\Http\Response
  173. */
  174. public function store(Request $request)
  175. {
  176. //
  177. $user = AuthApi::current($request);
  178. if(!$user ){
  179. //未登录用户
  180. return $this->error(__('auth.failed'),401,401);
  181. }
  182. $startAt = microtime(true)*1000;
  183. // system regular
  184. $this->initSysDict();
  185. $channel = Channel::find($request->get('channel_id'));
  186. $orgData = $request->get('data');
  187. $lang = [];
  188. foreach ($request->get('lang',[]) as $value) {
  189. $lang[] = strtolower($value);
  190. }
  191. //句子中的单词
  192. $words = [];
  193. foreach ($orgData as $word) {
  194. # code...
  195. if( isset($word['type']) && $word['type']['value'] === '.ctl.'){
  196. continue;
  197. }
  198. if(!empty($word['real']['value'])){
  199. $words[] = $word['real']['value'];
  200. }
  201. }
  202. $result = $this->lookup($words,[],2);
  203. $indexed = $this->toIndexed($result);
  204. foreach ($orgData as $key => $word) {
  205. if( isset($word['type']) && $word['type']['value'] === '.ctl.'){
  206. continue;
  207. }
  208. if(empty($word['real']['value'])){
  209. continue;
  210. }
  211. $data = $word;
  212. $preference = $this->wbwPreference($word['real']['value'],'meaning',$user['user_id']);
  213. if($preference!==false){
  214. $data['meaning'] = $preference;
  215. }
  216. $preference = $this->wbwPreference($word['real']['value'],'factors',$user['user_id']);
  217. if($preference!==false){
  218. $data['factors'] = $preference;
  219. }
  220. $preference = $this->wbwPreference($word['real']['value'],'factorMeaning',$user['user_id']);
  221. if($preference!==false){
  222. $data['factorMeaning'] = $preference;
  223. }
  224. $preference = $this->wbwPreference($word['real']['value'],'case',$user['user_id']);
  225. if($preference!==false){
  226. $data['case'] = $preference;
  227. }
  228. if(isset($indexed[$word['real']['value']])){
  229. //parent
  230. $case = [];
  231. $parent = [];
  232. $factors = [];
  233. $factorMeaning = [];
  234. $meaning = [];
  235. $parent2 = [];
  236. $case2 = [];
  237. foreach ($indexed[$word['real']['value']] as $value) {
  238. //非base优先
  239. if(strstr($value->type,'base') === FALSE){
  240. $increment = 10;
  241. }else{
  242. $increment = 1;
  243. }
  244. //将全部结果加上得分放入数组
  245. if($value->type !== '.cp.'){
  246. $parent = $this->insertValue([$value->parent],$parent,$increment);
  247. }
  248. if(isset($data['case']) && $data['case']['status']<5){
  249. if(!empty($value->type) && $value->type !== ".cp."){
  250. $case = $this->insertValue([$value->type."#".$value->grammar],$case,$increment);
  251. }
  252. }
  253. if($data['factors']['status'] < 50){
  254. $factors = $this->insertValue([$value->factors],$factors,$increment);
  255. }
  256. if(isset($data['factorMeaning']) && $data['factorMeaning']['status'] < 50){
  257. $factorMeaning = $this->insertValue([$value->factormean],$factorMeaning,$increment,false);
  258. }
  259. if($data['meaning']['status'] < 50){
  260. if($this->langCheck($lang,$value->language)){
  261. $meaning = $this->insertValue(explode('$',$value->mean),$meaning,$increment,false);
  262. }
  263. }
  264. }
  265. if(count($case)>0){
  266. arsort($case);
  267. $first = array_keys($case)[0];
  268. $data['case'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  269. }
  270. if(count($parent)>0){
  271. arsort($parent);
  272. $first = array_keys($parent)[0];
  273. $data['parent'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  274. }
  275. if(count($factors)>0 && empty($data['factors']['value'])){
  276. arsort($factors);
  277. $first = array_keys($factors)[0];
  278. $data['factors'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  279. }
  280. if(isset($data['factorMeaning']['value'])){
  281. $inputFM = str_replace(['-','+'],['',''],$data['factorMeaning']['value']);
  282. }else{
  283. $inputFM = '';
  284. }
  285. if(count($factorMeaning)>0 && empty($inputFM)){
  286. $first = array_keys($factorMeaning)[0];
  287. if($first==="_null"){
  288. $factorMeaning = [];
  289. }
  290. }
  291. //拆分意思
  292. if(count($factorMeaning) === 0){
  293. //生成自动的拆分意思
  294. $autoMeaning = '';
  295. $currFactors = explode('+',$data['factors']['value']) ;
  296. $autoFM = [];
  297. foreach ($currFactors as $factor) {
  298. $subFactors = explode('-',$factor) ;
  299. $autoSubFM = [];
  300. foreach ($subFactors as $subFactor) {
  301. $preference = $this->wbwPreference($subFactor,'factorMeaning',$user['user_id']);
  302. if($preference !== false){
  303. $autoSubFM[] = $preference['value'];
  304. }else{
  305. $preference = $this->wbwPreference($subFactor,'meaning',$user['user_id']);
  306. if($preference !== false){
  307. $autoSubFM[] = $preference['value'];
  308. }else{
  309. $autoSubFM[] = '';
  310. }
  311. }
  312. }
  313. $autoFM[] = implode('-',$autoSubFM);
  314. $autoMeaning .= implode('',$autoSubFM);
  315. }
  316. $autoMeaning .= implode('',$autoFM);
  317. $factorMeaning = [implode('+',$autoFM)=>1];
  318. if(empty($data['meaning']['value']) && !empty($autoMeaning)){
  319. $data['meaning'] = ['value'=>$autoMeaning,'status'=>5];
  320. }
  321. }
  322. if(count($factorMeaning)>0){
  323. arsort($factorMeaning);
  324. $first = array_keys($factorMeaning)[0];
  325. $data['factorMeaning'] = ['value'=>$first==="_null"?"":$first,'status'=>5];
  326. }
  327. if(isset($data['factorMeaning']) && $data['factorMeaning']['status']<5){
  328. $wbwFactorMeaning = [];
  329. if(!empty($data['factors']['value'])){
  330. foreach (explode("+",$data['factors']['value']) as $factor) {
  331. $preference = $this->wbwPreference($factor,'meaning',$user['user_id']);
  332. if($preference!==false){
  333. $wbwFactorMeaning[] = $preference['value'];
  334. }else{
  335. $wbwFactorMeaning[] = $factor;
  336. }
  337. }
  338. }
  339. $data['factorMeaning'] = ['value'=>implode('+',$wbwFactorMeaning),'status'=>3];
  340. }
  341. if(empty($data['meaning']['value']) && !empty($data['parent']['value'])){
  342. if(isset($indexed[$data['parent']['value']])){
  343. foreach ($indexed[$data['parent']['value']] as $value) {
  344. //根据base 查找词意
  345. //非base优先
  346. $increment = 10;
  347. if($this->langCheck($lang,$value->language)){
  348. $meaning = $this->insertValue(explode('$',$value->mean),$meaning,$increment,false);
  349. }
  350. //查找词源
  351. if(!empty($value->parent) && $value->parent !== $value->word && strstr($value->type,"base") !== FALSE ){
  352. $parent2 = $this->insertValue([$value->grammar."$".$value->parent],$parent2,1,false);
  353. }
  354. }
  355. }
  356. }
  357. if(count($meaning)>0){
  358. arsort($meaning);
  359. $first = array_keys($meaning)[0];
  360. $data['meaning'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  361. }
  362. if(count($parent2)>0){
  363. arsort($parent2);
  364. $first = explode("$",array_keys($parent2)[0]);
  365. $data['parent2'] = ['value'=>$first[1],'status'=>3];
  366. $data['grammar2'] = ['value'=>$first[0],'status'=>3];
  367. }
  368. }
  369. $orgData[$key] = $data;
  370. }
  371. return $this->ok($orgData);
  372. }
  373. private function fmEmpty($value){
  374. if(str_replace(['+','-',' '],'',$value) === ''){
  375. return true;
  376. }else{
  377. return false;
  378. }
  379. }
  380. /**
  381. * 自动查词
  382. *
  383. * @param string $sentId
  384. * @return \Illuminate\Http\Response
  385. */
  386. public function show(Request $request,string $sentId)
  387. {
  388. $startAt = microtime(true)*1000;
  389. $channel = Channel::find($request->get('channel_id'));
  390. //查询句子中的单词
  391. $sent = \explode('-',$sentId);
  392. $wbw = WbwTemplate::where('book',$sent[0])
  393. ->where('paragraph',$sent[1])
  394. ->whereBetween('wid',[$sent[2],$sent[3]])
  395. ->orderBy('wid')
  396. ->get();
  397. $words = [];
  398. foreach ($wbw as $row) {
  399. if($row->type !== '.ctl.' && !empty($row->real)){
  400. $words[] = $row->real;
  401. }
  402. }
  403. $result = $this->lookup($words,[],2);
  404. $indexed = $this->toIndexed($result);
  405. //生成自动填充结果
  406. $wbwContent = [];
  407. foreach ($wbw as $row) {
  408. $type = $row->type=='?'? '':$row->type;
  409. $grammar = $row->gramma=='?'? '':$row->gramma;
  410. $part = $row->part=='?'? '':$row->part;
  411. if(!empty($type) || !empty($grammar)){
  412. $case = "{$type}#$grammar";
  413. }else{
  414. $case = "";
  415. }
  416. $data = [
  417. 'sn'=>[$row->wid],
  418. 'word'=>['value'=>$row->word,'status'=>3],
  419. 'real'=> ['value'=>$row->real,'status'=>3],
  420. 'meaning'=> ['value'=>[],'status'=>3],
  421. 'type'=> ['value'=>$type,'status'=>3],
  422. 'grammar'=> ['value'=>$grammar,'status'=>3],
  423. 'case'=> ['value'=>$case,'status'=>3],
  424. 'style'=> ['value'=>$row->style,'status'=>3],
  425. 'factors'=> ['value'=>$part,'status'=>3],
  426. 'factorMeaning'=> ['value'=>'','status'=>3],
  427. 'confidence'=> 0.5
  428. ];
  429. if($row->type !== '.ctl.' && !empty($row->real)){
  430. if(isset($indexed[$row->real])){
  431. //parent
  432. $case = [];
  433. $parent = [];
  434. $factors = [];
  435. $factorMeaning = [];
  436. $meaning = [];
  437. $parent2 = [];
  438. $case2 = [];
  439. foreach ($indexed[$row->real] as $value) {
  440. //非base优先
  441. if(strstr($value->type,'base') === FALSE){
  442. $increment = 10;
  443. }else{
  444. $increment = 1;
  445. }
  446. //将全部结果加上得分放入数组
  447. $parent = $this->insertValue([$value->parent],$parent,$increment);
  448. $case = $this->insertValue([$value->type."#".$value->grammar],$case,$increment);
  449. $factors = $this->insertValue([$value->factors],$factors,$increment);
  450. $factorMeaning = $this->insertValue([$value->factormean],$factorMeaning,$increment);
  451. $meaning = $this->insertValue(explode('$',$value->mean),$meaning,$increment,false);
  452. }
  453. if(count($case)>0){
  454. arsort($case);
  455. $first = array_keys($case)[0];
  456. $data['case'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  457. }
  458. if(count($parent)>0){
  459. arsort($parent);
  460. $first = array_keys($parent)[0];
  461. $data['parent'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  462. }
  463. if(count($factors)>0){
  464. arsort($factors);
  465. $first = array_keys($factors)[0];
  466. $data['factors'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  467. }
  468. if(count($factorMeaning)>0){
  469. arsort($factorMeaning);
  470. $first = array_keys($factorMeaning)[0];
  471. $data['factorMeaning'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  472. }
  473. //根据base 查找词意
  474. if(!empty($data['parent'])){
  475. if(isset($indexed[$data['parent']['value']])){
  476. foreach ($indexed[$data['parent']['value']] as $value) {
  477. //非base优先
  478. $increment = 10;
  479. $meaning = $this->insertValue(explode('$',$value->mean),$meaning,$increment,false);
  480. }
  481. }else{
  482. //Log::error("no set parent".$data['parent']['value']);
  483. }
  484. }
  485. if(count($meaning)>0){
  486. arsort($meaning);
  487. $first = array_keys($meaning)[0];
  488. $data['meaning'] = ['value'=>$first==="_null"?"":$first,'status'=>3];
  489. }
  490. }
  491. }
  492. $wbwContent[] = $data;
  493. }
  494. $endAt = microtime(true)*1000;
  495. return $this->ok(["rows"=>$wbwContent,
  496. "count"=>count($wbwContent),
  497. "time"=>(int)($endAt-$startAt)]);
  498. }
  499. private function toIndexed($words){
  500. //转成索引数组
  501. $indexed = [];
  502. foreach ($words as $key => $value) {
  503. # code...
  504. $indexed[$value->word][] = $value;
  505. }
  506. return $indexed;
  507. }
  508. /**
  509. * $empty:是否允许空值
  510. */
  511. private function insertValue($value,$container,$increment,$empty=true){
  512. foreach ($value as $one) {
  513. if($empty === false){
  514. if($this->fmEmpty($one)){
  515. break;
  516. }
  517. }
  518. $one=trim($one);
  519. $key = $one;
  520. if(empty($key)){
  521. $key = '_null';
  522. }
  523. if(isset($container[$key])){
  524. $container[$key] += $increment;
  525. }else{
  526. $container[$key] = $increment;
  527. }
  528. }
  529. return $container;
  530. }
  531. /**
  532. * Update the specified resource in storage.
  533. *
  534. * @param \Illuminate\Http\Request $request
  535. * @param \App\Models\UserDict $userDict
  536. * @return \Illuminate\Http\Response
  537. */
  538. public function update(Request $request, UserDict $userDict)
  539. {
  540. //
  541. }
  542. /**
  543. * Remove the specified resource from storage.
  544. *
  545. * @param \App\Models\UserDict $userDict
  546. * @return \Illuminate\Http\Response
  547. */
  548. public function destroy(UserDict $userDict)
  549. {
  550. //
  551. }
  552. }