WbwLookupController.php 20 KB

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