WbwLookupController.php 22 KB

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