DhammaTermController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Api\ChannelApi;
  4. use App\Http\Api\ShareApi;
  5. use App\Http\Api\StudioApi;
  6. use App\Http\Controllers\Concerns\ChecksChannelEditPower;
  7. use App\Http\Resources\TermResource;
  8. use App\Models\Channel;
  9. use App\Models\DhammaTerm;
  10. use App\Services\AuthService;
  11. use App\Tools\Tools;
  12. use Illuminate\Http\Request;
  13. use Illuminate\Http\Response;
  14. use Illuminate\Support\Facades\Cache;
  15. use Illuminate\Support\Facades\DB;
  16. use Illuminate\Support\Str;
  17. class DhammaTermController extends Controller
  18. {
  19. use ChecksChannelEditPower;
  20. /**
  21. * Display a listing of the resource.
  22. *
  23. * @return Response
  24. */
  25. public function index(Request $request)
  26. {
  27. $result = false;
  28. $indexCol = [
  29. 'id',
  30. 'guid',
  31. 'word',
  32. 'meaning',
  33. 'other_meaning',
  34. 'note',
  35. 'tag',
  36. 'language',
  37. 'channal',
  38. 'owner',
  39. 'editor_id',
  40. 'editor_uid',
  41. 'created_at',
  42. 'updated_at',
  43. ];
  44. switch ($request->input('view')) {
  45. case 'create-by-channel':
  46. // 新建术语时。根据术语所在channel 给出新建术语所需数据。如语言,备选意思等。
  47. // 获取channel信息
  48. $currChannel = Channel::where('uid', $request->input('channel'))->first();
  49. if (! $currChannel) {
  50. return $this->error(__('auth.failed'));
  51. }
  52. // TODO 查询studio信息
  53. // 获取同studio的channel列表
  54. $studioChannels = Channel::where('owner_uid', $currChannel->owner_uid)
  55. ->select(['name', 'uid'])
  56. ->get();
  57. // 获取全网意思列表
  58. $meanings = DhammaTerm::where('word', $request->input('word'))
  59. ->where('language', $currChannel->lang)
  60. ->select(['meaning', 'other_meaning'])
  61. ->get();
  62. $meaningList = [];
  63. foreach ($meanings as $key => $value) {
  64. // code...
  65. $meaning1 = [$value->meaning];
  66. if (! empty($value->other_meaning)) {
  67. $meaning2 = \explode(',', $value->other_meaning);
  68. $meaning1 = array_merge($meaning1, $meaning2);
  69. }
  70. foreach ($meaning1 as $key => $value) {
  71. // code...
  72. if (isset($meaningList[$value])) {
  73. $meaningList[$value]++;
  74. } else {
  75. $meaningList[$value] = 1;
  76. }
  77. }
  78. }
  79. $meaningCount = [];
  80. foreach ($meaningList as $key => $value) {
  81. // code...
  82. $meaningCount[] = ['meaning' => $key, 'count' => $value];
  83. }
  84. return $this->ok([
  85. 'word' => $request->input('word'),
  86. 'meaningCount' => $meaningCount,
  87. 'studioChannels' => $studioChannels,
  88. 'language' => $currChannel->lang,
  89. 'studio' => StudioApi::getById($currChannel->owner_uid),
  90. ]);
  91. break;
  92. case 'studio':
  93. // 获取 studio 内所有 term
  94. $user = AuthService::current($request);
  95. if (! $user) {
  96. return $this->error(__('auth.failed'), [], 401);
  97. }
  98. // 判断当前用户是否有指定的studio的权限
  99. if ($user['user_uid'] !== StudioApi::getIdByName($request->input('name'))) {
  100. return $this->error(__('auth.failed'), [], 403);
  101. }
  102. $table = DhammaTerm::select($indexCol)
  103. ->where('owner', $user['user_uid']);
  104. break;
  105. case 'channel':
  106. // 获取 studio 内所有 term
  107. $user = AuthService::current($request);
  108. if (! $user) {
  109. return $this->error(__('auth.failed'));
  110. }
  111. // 判断当前用户是否有指定的 channel 的权限
  112. $channel = Channel::find($request->input('id'));
  113. if ($user['user_uid'] !== $channel->owner_uid) {
  114. // 看是否为协作
  115. $power = ShareApi::getResPower($user['user_uid'], $request->input('id'));
  116. if ($power === 0) {
  117. return $this->error(__('auth.failed'), [], 403);
  118. }
  119. }
  120. $table = DhammaTerm::select($indexCol)
  121. ->where('channal', $request->input('id'));
  122. break;
  123. case 'show':
  124. return $this->ok(DhammaTerm::find($request->input('id')));
  125. break;
  126. case 'user':
  127. // code...
  128. $user = AuthService::current($request);
  129. if (! $user) {
  130. return $this->error(__('auth.failed'));
  131. }
  132. $userUid = $user['user_uid'];
  133. $search = $request->input('search');
  134. $table = DhammaTerm::select($indexCol)
  135. ->where('owner', $userUid);
  136. break;
  137. case 'word':
  138. $table = DhammaTerm::select($indexCol)
  139. ->whereIn('word', explode(',', $request->input('word')))
  140. ->orWhereIn('meaning', explode(',', $request->input('word')));
  141. break;
  142. case 'tag':
  143. $table = DhammaTerm::select($indexCol)
  144. ->whereIn('tag', explode(',', $request->input('tag')));
  145. break;
  146. case 'hot-meaning':
  147. $key = 'term/hot_meaning';
  148. $value = Cache::remember($key, config('mint.cache.expire'), function () use ($request) {
  149. $hotMeaning = [];
  150. $words = DhammaTerm::select('word')
  151. ->where('language', $request->input('language'))
  152. ->groupby('word')
  153. ->get();
  154. foreach ($words as $key => $word) {
  155. // code...
  156. $result = DhammaTerm::select(DB::raw('count(*) as word_count, meaning'))
  157. ->where('language', $request->input('language'))
  158. ->where('word', $word['word'])
  159. ->groupby('meaning')
  160. ->orderby('word_count', 'desc')
  161. ->first();
  162. if ($result) {
  163. $hotMeaning[] = [
  164. 'word' => $word['word'],
  165. 'meaning' => $result['meaning'],
  166. 'language' => $request->input('language'),
  167. 'owner' => '',
  168. ];
  169. }
  170. }
  171. Cache::put($key, $hotMeaning, 3600);
  172. return $hotMeaning;
  173. });
  174. return $this->ok(['rows' => $value, 'count' => count($value)]);
  175. break;
  176. default:
  177. // code...
  178. break;
  179. }
  180. $search = $request->input('search');
  181. if (! empty($search)) {
  182. $table = $table->where(function ($query) use ($search) {
  183. $query->where('word', 'like', $search.'%')
  184. ->orWhere('word_en', 'like', $search.'%')
  185. ->orWhere('meaning', 'like', '%'.$search.'%');
  186. });
  187. }
  188. $count = $table->count();
  189. $table = $table->orderBy($request->input('order', 'updated_at'), $request->input('dir', 'desc'));
  190. $table = $table->skip($request->input('offset', 0))
  191. ->take($request->input('limit', 1000));
  192. $result = $table->get();
  193. return $this->ok(['rows' => TermResource::collection($result), 'count' => $count]);
  194. }
  195. /**
  196. * Store a newly created resource in storage.
  197. *
  198. * @return Response
  199. */
  200. public function store(Request $request)
  201. {
  202. $user = AuthService::current($request);
  203. if (! $user) {
  204. return $this->error(__('auth.failed'));
  205. }
  206. $validated = $request->validate([
  207. 'word' => 'required',
  208. 'meaning' => 'required',
  209. ]);
  210. /**
  211. * 查询重复的
  212. * 一个channel下面word+tag+language 唯一
  213. */
  214. $table = DhammaTerm::where('owner', $user['user_uid'])
  215. ->where('word', $request->input('word'))
  216. ->where('tag', $request->input('tag'));
  217. if (! empty($request->input('channel'))) {
  218. $isDoesntExist = $table->where('channal', $request->input('channel'))
  219. ->doesntExist();
  220. } else {
  221. $isDoesntExist = $table->whereNull('channal')->where('language', $request->input('language'))
  222. ->doesntExist();
  223. }
  224. if ($isDoesntExist) {
  225. // 没有重复的 插入数据
  226. $term = new DhammaTerm;
  227. $term->id = app('snowflake')->id();
  228. $term->guid = Str::uuid();
  229. $term->word = $request->input('word');
  230. $term->word_en = Tools::getWordEn($request->input('word'));
  231. $term->meaning = $request->input('meaning');
  232. $term->other_meaning = $request->input('other_meaning');
  233. $term->note = $request->input('note');
  234. $term->tag = $request->input('tag');
  235. $term->channal = $request->input('channel');
  236. $term->language = $request->input('language');
  237. if (! empty($request->input('channel'))) {
  238. $channelInfo = ChannelApi::getById($request->input('channel'));
  239. if (! $channelInfo) {
  240. return $this->error('channel id failed');
  241. } else {
  242. // 查看有没有channel权限。术语没有 book 概念,access token 的
  243. // book 一律按 0(不限)判定。
  244. if (! $this->userCanEditChannel(
  245. $user['user_uid'],
  246. $request->input('channel'),
  247. 0,
  248. $request->input('access_token')
  249. )) {
  250. return $this->error(__('auth.failed'), [], 403);
  251. }
  252. $term->owner = $channelInfo['studio_id'];
  253. $term->language = $channelInfo['lang'];
  254. }
  255. } else {
  256. if ($request->has('studioId')) {
  257. $studioId = $request->input('studioId');
  258. } elseif ($request->has('studioName')) {
  259. $studioId = StudioApi::getIdByName($request->input('studioName'));
  260. }
  261. if (! isset($studioId) || ! Str::isUuid($studioId)) {
  262. return $this->error('not valid studioId');
  263. }
  264. // studio 级术语(不属于任何 channel)只能由 studio 本人建。
  265. // 此前这里不校验归属,任何登录用户都能往别人 studio 名下写。
  266. // access token 是 channel 级的,代持不了 studio 权限,所以
  267. // AI 模型建 studio 级术语必然走到这里被拒——这是有意的。
  268. if ($studioId !== $user['user_uid']) {
  269. return $this->error(__('auth.failed'), [], 403);
  270. }
  271. $term->owner = $studioId;
  272. }
  273. $term->editor_id = $user['user_id'];
  274. $term->editor_uid = $user['user_uid'];
  275. $term->create_time = time() * 1000;
  276. $term->modify_time = time() * 1000;
  277. $term->save();
  278. // 删除cache
  279. $this->deleteCache($term);
  280. return $this->ok(new TermResource($term));
  281. } else {
  282. return $this->error('word existed', [], 200);
  283. }
  284. }
  285. private function deleteCache($term)
  286. {
  287. if (empty($term->channal)) {
  288. // 通用 查询studio所有channel
  289. $channels = Channel::where('owner_uid', $term->owner)->select('uid')->get();
  290. foreach ($channels as $channel) {
  291. Cache::forget("/term/{$channel}/{$term->word}");
  292. }
  293. } else {
  294. Cache::forget("/term/{$term->channal}/{$term->word}");
  295. }
  296. }
  297. /**
  298. * Display the specified resource.
  299. *
  300. * @param string $id
  301. * @return Response
  302. */
  303. public function show(Request $request, $id)
  304. {
  305. //
  306. $result = DhammaTerm::where('guid', $id)->first();
  307. if ($result) {
  308. return $this->ok(new TermResource($result));
  309. } else {
  310. return $this->error('没有查询到数据');
  311. }
  312. }
  313. /**
  314. * Update the specified resource in storage.
  315. *
  316. * @param DhammaTerm $dhammaTerm
  317. * @return Response
  318. */
  319. public function update(Request $request, string $id)
  320. {
  321. //
  322. $user = AuthService::current($request);
  323. if (! $user) {
  324. return $this->error(__('auth.failed'), [], 401);
  325. }
  326. $dhammaTerm = DhammaTerm::find($id);
  327. if (! $dhammaTerm) {
  328. return $this->error('404');
  329. }
  330. if (empty($dhammaTerm->channal)) {
  331. // 查看有没有studio权限。access token 是 channel 级的,代持不了
  332. // studio 权限,故 studio 级术语只有 owner 本人能改。
  333. if ($user['user_uid'] !== $dhammaTerm->owner) {
  334. return $this->error(__('auth.failed'), [], 403);
  335. }
  336. } else {
  337. // 查看有没有channel权限(owner / 协作者 / access token)
  338. if (! $this->userCanEditChannel(
  339. $user['user_uid'],
  340. $dhammaTerm->channal,
  341. 0,
  342. $request->input('access_token')
  343. )) {
  344. return $this->error(__('auth.failed'), [], 403);
  345. }
  346. }
  347. // 增量更新:只改提交上来的字段。此前这里无条件赋值,客户端漏提一个
  348. // 字段就会把库里的 note/tag 等清成 null。
  349. if ($request->has('word')) {
  350. $dhammaTerm->word = $request->input('word');
  351. $dhammaTerm->word_en = Tools::getWordEn($request->input('word'));
  352. }
  353. foreach (['meaning', 'other_meaning', 'note', 'tag', 'language'] as $field) {
  354. if ($request->has($field)) {
  355. $dhammaTerm->$field = $request->input($field);
  356. }
  357. }
  358. $dhammaTerm->editor_id = $user['user_id'];
  359. $dhammaTerm->editor_uid = $user['user_uid'];
  360. // create_time 是创建时刻,改动时不该被刷新
  361. $dhammaTerm->modify_time = time() * 1000;
  362. $dhammaTerm->save();
  363. // 删除cache
  364. $this->deleteCache($dhammaTerm);
  365. return $this->ok(new TermResource($dhammaTerm));
  366. }
  367. /**
  368. * Remove the specified resource from storage.
  369. *
  370. * @return Response
  371. */
  372. public function destroy(DhammaTerm $dhammaTerm, Request $request)
  373. {
  374. /**
  375. * 一次删除多个单词
  376. */
  377. $user = AuthService::current($request);
  378. if (! $user) {
  379. return $this->error(__('auth.failed'));
  380. }
  381. $count = 0;
  382. if ($request->has('uuid')) {
  383. // 查看是否有删除权限
  384. foreach ($request->input('id') as $key => $uuid) {
  385. $term = DhammaTerm::find($uuid);
  386. if (! $term) {
  387. continue;
  388. }
  389. if ($term->owner !== $user['user_uid']) {
  390. if (! empty($term->channal)) {
  391. // 看是否为协作
  392. $power = ShareApi::getResPower($user['user_uid'], $term->channal);
  393. if ($power < 20) {
  394. continue;
  395. }
  396. } else {
  397. continue;
  398. }
  399. }
  400. $count += $term->delete();
  401. // 删除cache
  402. $this->deleteCache($term);
  403. }
  404. } else {
  405. $arrId = json_decode($request->input('id'), true);
  406. foreach ($arrId as $key => $id) {
  407. // code...
  408. $term = DhammaTerm::where('id', $id)
  409. ->where('owner', $user['user_uid'])
  410. ->first();
  411. if (! $term) {
  412. continue;
  413. }
  414. // 先取到模型再删:此前这里把 query builder 传给 deleteCache,
  415. // 拿不到 word/channal,缓存根本没被清掉。
  416. $result = $term->delete();
  417. if ($result) {
  418. $this->deleteCache($term);
  419. $count++;
  420. }
  421. }
  422. }
  423. return $this->ok($count);
  424. }
  425. }