TaskController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. <?php
  2. namespace App\Http\Controllers\API;
  3. use App\Exports\DownloadHelper;
  4. use App\Exports\TaskExport;
  5. use App\Http\Controllers\Controller;
  6. use App\Http\Requests\API\Task\AssignRequest;
  7. use App\Http\Requests\API\Task\BatchCreateItemRules;
  8. use App\Http\Requests\API\Task\BatchCreateRequest;
  9. use App\Http\Requests\API\Task\CreateOrUpdateRequest;
  10. use App\Http\Requests\API\Task\LinkContainerRequest;
  11. use App\Http\Resources\API\TaskDetailResource;
  12. use App\Http\Resources\API\TaskResource;
  13. use App\Imports\TaskImport;
  14. use App\Models\Approval;
  15. use App\Models\Container;
  16. use App\Models\Enums\ActionObjectType;
  17. use App\Models\Enums\FileObjectType;
  18. use App\Models\Enums\ObjectAction;
  19. use App\Models\Enums\TaskStatus;
  20. use App\Models\Library;
  21. use App\Models\Project;
  22. use App\Models\Requirement;
  23. use App\Models\Task;
  24. use App\Models\TaskContainer;
  25. use App\Repositories\ActionRepository;
  26. use App\Repositories\CustomFieldRepository;
  27. use App\Services\File\FileAssociationService;
  28. use App\Services\History\ModelChangeDetector;
  29. use Carbon\Carbon;
  30. use Illuminate\Http\Request;
  31. use Illuminate\Support\Facades\Auth;
  32. use Illuminate\Support\Facades\Validator;
  33. use Maatwebsite\Excel\Facades\Excel;
  34. class TaskController extends Controller
  35. {
  36. use DownloadHelper;
  37. /**
  38. * Display a listing of the resource.
  39. */
  40. public function index(Request $request)
  41. {
  42. $pageSize=$request->get('page_size') ?? 10;
  43. $sort=$request->input('sort','desc');
  44. $tasks = Task::query()
  45. ->where("parent_id", 0)
  46. ->with(['children', 'assignTo', 'createdBy'])
  47. ->filter($request->all())
  48. ->allowed()
  49. ->orderBy('created_at',$sort)
  50. ->paginate($pageSize);
  51. return TaskResource::collection($tasks);
  52. }
  53. public function export(Request $request)
  54. {
  55. return $this->downloadExcelHelper(
  56. new TaskExport(),
  57. "task",
  58. $request->get("extension"),
  59. );
  60. }
  61. /**
  62. * Store a newly created resource in storage.
  63. */
  64. public function store(FileAssociationService $service, CreateOrUpdateRequest $request, CustomFieldRepository $customFieldRepo)
  65. {
  66. $isAction=true;
  67. $projectId=$request->get("project_id");
  68. $project = Project::allowed($projectId,$isAction)->find($projectId);
  69. if($project===null||is_null($project)){
  70. return $this->badRequest('Permission denied or project not found. Please contact the administrator.');
  71. };
  72. $requirement = $request->has('requirement_id')
  73. ? Requirement::query()->findOrFail($request->get("requirement_id"))
  74. : null;
  75. $service->check(
  76. $request->get("file_ids", []),
  77. FileObjectType::TASK,
  78. $request->get("file_uuid"),
  79. );
  80. $formData = [
  81. ...$request->all(),
  82. 'company_id' => Auth::user()->company_id,
  83. 'created_by' => Auth::id(),
  84. 'whitelist' => $request->whitelist ? sprintf(",%s,", implode(',', $request->whitelist)) : null,
  85. 'asset_id' => $requirement?->asset_id,
  86. 'description' => $request->description? (new \App\Services\File\ImageUrlService)->interceptImageUrl($request->description) : null,
  87. 'requirement_group_id'=> $requirement?->requirement_group_id,
  88. ];
  89. if ($request->has("naming_rule_id") && $request->get("naming_rule_id") > 0) {
  90. $keys = $customFieldRepo->keysByGroup($request->get("naming_rule_id"));
  91. $formData['naming_rules'] = $request->only($keys);
  92. }
  93. $task = Task::create($formData);
  94. ActionRepository::createByTask($task, ObjectAction::CREATED);
  95. $service->association($task->id);
  96. return $this->created();
  97. }
  98. public function import(Request $request)
  99. {
  100. Excel::import(new TaskImport(), $request->file("file"));
  101. return $this->created();
  102. }
  103. /**
  104. * Display the specified resource.
  105. */
  106. public function show(string $id)
  107. {
  108. $task = Task::query()->allowed($id)->with([
  109. 'containers'
  110. ])->findOrFail($id);
  111. return new TaskDetailResource($task);
  112. }
  113. public function start(Request $request, string $id)
  114. {
  115. $isAction=true;
  116. $task = Task::query()->allowed($id,$isAction)->find($id);
  117. if($task===null||is_null($task)){
  118. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  119. };
  120. $task->status = TaskStatus::DOING->value;
  121. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  122. $task->save();
  123. ActionRepository::createByTask(
  124. $task,
  125. ObjectAction::STARTED,
  126. $request->comment?(new \App\Services\File\ImageUrlService)->interceptImageUrl($request->comment) : null,
  127. objectChanges: $changes
  128. );
  129. return $this->noContent();
  130. }
  131. public function pause(Request $request, string $id)
  132. {
  133. $isAction=true;
  134. $task = Task::query()->allowed($id,$isAction)->find($id);
  135. if($task===null||is_null($task)){
  136. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  137. };
  138. $task->status = TaskStatus::PAUSE->value;
  139. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  140. $task->save();
  141. ActionRepository::createByTask(
  142. $task, ObjectAction::PAUSED,
  143. $request->comment?(new \App\Services\File\ImageUrlService)->interceptImageUrl($request->comment) : null,
  144. objectChanges: $changes
  145. );
  146. return $this->noContent();
  147. }
  148. public function closed(Request $request, string $id)
  149. {
  150. $isAction=true;
  151. $task = Task::query()->allowed($id,$isAction)->find($id);
  152. if($task===null||is_null($task)){
  153. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  154. };
  155. $task->status = TaskStatus::CLOSED->value;
  156. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  157. $task->save();
  158. ActionRepository::createByTask(
  159. $task, ObjectAction::CLOSED,
  160. $request->comment?(new \App\Services\File\ImageUrlService)->interceptImageUrl($request->comment) : null,
  161. objectChanges: $changes
  162. );
  163. return $this->noContent();
  164. }
  165. public function done(Request $request, string $id)
  166. {
  167. $isAction=true;
  168. $task = Task::query()->allowed($id,$isAction)->find($id);
  169. if($task===null||is_null($task)){
  170. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  171. };
  172. $task->fill([
  173. 'status' => TaskStatus::DONE->value,
  174. 'finished_by' => Auth::user()->id,
  175. 'finished_at' => Carbon::now(),
  176. ]);
  177. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  178. $task->save();
  179. ActionRepository::createByTask(
  180. $task, ObjectAction::DONE,
  181. $request->comment?(new \App\Services\File\ImageUrlService)->interceptImageUrl($request->comment) : null,
  182. objectChanges: $changes
  183. );
  184. return $this->noContent();
  185. }
  186. public function cancel(Request $request, string $id)
  187. {
  188. $isAction=true;
  189. $task = Task::query()->allowed($id,$isAction)->find($id);
  190. if($task===null||is_null($task)){
  191. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  192. };
  193. $task->fill([
  194. 'status' => TaskStatus::CANCEL->value,
  195. 'canceled_by' => Auth::user()->id,
  196. 'canceled_at' => Carbon::now(),
  197. ]);
  198. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  199. $task->save();
  200. ActionRepository::createByTask(
  201. $task, ObjectAction::CANCELED,
  202. $request->comment?(new \App\Services\File\ImageUrlService)->interceptImageUrl($request->comment) : null,
  203. objectChanges: $changes
  204. );
  205. return $this->noContent();
  206. }
  207. public function wait(Request $request,string $id){
  208. $isAction=true;
  209. $task = Task::query()->allowed($id,$isAction)->find($id);
  210. if($task===null||is_null($task)){
  211. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  212. };
  213. $task->status=TaskStatus::WAIT->value;
  214. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  215. $task->save();
  216. ActionRepository::createByTask(
  217. $task, ObjectAction::WAITED,
  218. $request->comment?(new \App\Services\File\ImageUrlService)->interceptImageUrl($request->comment) : null,
  219. objectChanges: $changes
  220. );
  221. return $this->noContent();
  222. }
  223. public function assign(AssignRequest $request,string $id){
  224. $isAction=true;
  225. $task = Task::query()->allowed($id,$isAction)->find($id);
  226. if($task===null||is_null($task)){
  227. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  228. };
  229. $task->fill([
  230. 'assign'=>$request->get('assign'),
  231. ...$request->all(),
  232. ]);
  233. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  234. $task->save();
  235. ActionRepository::createByTask(
  236. $task, ObjectAction::ASSIGNED,
  237. $request->comment?(new \App\Services\File\ImageUrlService)->interceptImageUrl($request->comment) : null,
  238. objectChanges: $changes
  239. );
  240. return $this->noContent();
  241. }
  242. /**
  243. * Update the specified resource in storage.
  244. */
  245. public function update(CreateOrUpdateRequest $request, string $id, CustomFieldRepository $customFieldRepo)
  246. {
  247. $isAction=true;
  248. $task = Task::query()->allowed($id,$isAction)->find($id);
  249. if($task===null||is_null($task)){
  250. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  251. };
  252. $requirement = $request->has('requirement_id') && $task->requirement_id != $request->get('requirement_id')
  253. ? Requirement::query()->findOrFail($request->get("requirement_id"))
  254. : null;
  255. $formData = [
  256. ...$request->except('created_by'),
  257. 'whitelist' => $request->whitelist ? sprintf(",%s,", implode(',', $request->whitelist)) : null,
  258. 'description' => $request->description? (new \App\Services\File\ImageUrlService)->interceptImageUrl($request->description) : null,
  259. 'asset_id' => $requirement?->asset_id,
  260. ];
  261. if ($request->has("naming_rule_id") && $request->get("naming_rule_id") > 0) {
  262. $keys = $customFieldRepo->keysByGroup($request->get("naming_rule_id"));
  263. $formData['naming_rules'] = $request->only($keys);
  264. }
  265. $task->fill($formData);
  266. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  267. $task->save();
  268. ActionRepository::createByTask($task, ObjectAction::EDITED, objectChanges: $changes);
  269. return $this->noContent();
  270. }
  271. /**
  272. * Remove the specified resource from storage.
  273. */
  274. public function destroy(string $id)
  275. {
  276. $isAction=true;
  277. $task = Task::query()->allowed($id,$isAction)->find($id);
  278. if($task===null||is_null($task)){
  279. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  280. };
  281. $task->delete();
  282. Approval::query()->where('object_type','task')->where('object_id',$id)->delete();
  283. ActionRepository::createByTask($task, ObjectAction::DELETED);
  284. return $this->noContent();
  285. }
  286. public function batchStore(BatchCreateRequest $request, CustomFieldRepository $customFieldRepo)
  287. {
  288. $isAction=true;
  289. $projectId=$request->get("project_id");
  290. $project = Project::allowed($projectId,$isAction)->find($projectId);
  291. if($project===null||is_null($project)){
  292. return $this->badRequest('Permission denied or project not found. Please contact the administrator.');
  293. };
  294. $parsedItems = [];
  295. $previousItem = [];
  296. foreach ($request->items as $index => $item) {
  297. if ($index == 0) {
  298. $newItem = $item;
  299. } else {
  300. $newItem = [];
  301. foreach ($item as $k => $v) {
  302. $newItem[$k] = $v == "ditto" ? data_get($previousItem, $k) : $v;
  303. }
  304. }
  305. $previousItem = $newItem;
  306. $parsedItems[] = $newItem;
  307. }
  308. $itemRules = new BatchCreateItemRules();
  309. foreach ($parsedItems as $index => $item) {
  310. $rules = $itemRules->rules($item);
  311. try {
  312. $validator = Validator::make($item, $rules);
  313. if ($validator->fails()) {
  314. return $this->badRequest(sprintf("line: %d, %s", $index + 1, $validator->errors()->first()));
  315. }
  316. } catch (\Exception $e) {
  317. return $this->badRequest(sprintf("line: %d, %s", $index + 1, $e->getMessage()));
  318. }
  319. }
  320. foreach ($parsedItems as $item) {
  321. $requirement=$item['requirement_id']>0 ? Requirement::query()->findOrFail($item['requirement_id']) : null;
  322. $item["whitelist"]=!empty($item['whitelist']) ? sprintf(",%s,", implode(',',$item['whitelist'])) : null;
  323. $namingRuleId = data_get($item, "naming_rule_id", 0);
  324. if ($namingRuleId > 0) {
  325. $keys = $customFieldRepo->keysByGroup($namingRuleId);
  326. $item['naming_rules'] = collect($item)->only($keys)->toArray();
  327. }
  328. $task = Task::query()->create([
  329. ...$item,
  330. 'project_id' => $project->id,
  331. 'parent_id' => $request->parent_id,
  332. 'company_id' => Auth::user()->company_id,
  333. 'created_by' => Auth::id(),
  334. 'asset_id' => $requirement?->asset_id,
  335. 'requirement_group_id'=> $requirement?->requirement_group_id,
  336. ]);
  337. ActionRepository::createByTask($task, ObjectAction::CREATED);
  338. }
  339. return $this->noContent();
  340. }
  341. /**
  342. * 容器链接
  343. *
  344. * @param LinkContainerRequest $request
  345. * @param string $id
  346. * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
  347. */
  348. public function linkContainer(LinkContainerRequest $request, string $id)
  349. {
  350. $isAction=true;
  351. $task = Task::query()->allowed($id,$isAction)->find($id);
  352. if($task===null||is_null($task)){
  353. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  354. };
  355. $libraryIds = Library::query()->allowed()->where("project_id", $task->project_id)->pluck("id");
  356. if ($libraryIds->isEmpty()) {
  357. return $this->badRequest("No container exists for the current task item");
  358. }
  359. $containers = Container::query()
  360. ->allowed()
  361. ->whereIn("library_id", $libraryIds->toArray())
  362. ->whereIn("id", $request->get("container_ids", []))
  363. ->get(['id']);
  364. if (! $containers) {
  365. return $this->badRequest("Please select a valid container");
  366. }
  367. foreach ($containers as $container) {
  368. TaskContainer::query()->firstOrCreate([
  369. 'container_id' => $container->id,
  370. 'task_id' => $task->id
  371. ]);
  372. }
  373. return $this->noContent();
  374. }
  375. public function unlinkContainer(string $id)
  376. {
  377. $taskContainer = TaskContainer::query()->findOrFail($id);
  378. $taskId=$taskContainer->task_id;
  379. $isAction=true;
  380. $task = Task::query()->allowed($taskId,$isAction)->find($taskId);
  381. if($task===null||is_null($task)){
  382. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  383. };
  384. $taskContainer->delete();
  385. return $this->noContent();
  386. }
  387. /**
  388. * 待关联的容器
  389. *
  390. * @param Request $request
  391. * @param string $id
  392. * @return \Illuminate\Http\JsonResponse
  393. */
  394. public function containerToBeLinked(Request $request, string $id)
  395. {
  396. $task = Task::query()->allowed($id)->findOrFail($id);
  397. $libraryIds = Library::query()->allowed()->where("project_id", $task->project_id)->pluck("id");
  398. if ($libraryIds->isEmpty()) {
  399. return $this->badRequest("No container exists for the current task item");
  400. }
  401. $containers = Container::query()
  402. ->allowed()
  403. ->leftJoin("task_container", "containers.id", "=", "task_container.container_id")
  404. ->where($request->only(['library_id']))
  405. ->whereIn("library_id", $libraryIds->toArray())
  406. ->whereNull("task_container.id")
  407. ->selectRaw("containers.id, containers.name")
  408. ->get();
  409. return $this->success([
  410. 'data' => $containers
  411. ]);
  412. }
  413. }