TaskController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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 = [...$request->all(),
  256. 'whitelist' => $request->whitelist ? sprintf(",%s,", implode(',', $request->whitelist)) : null,
  257. 'description' => $request->description? (new \App\Services\File\ImageUrlService)->interceptImageUrl($request->description) : null,
  258. 'asset_id' => $requirement?->asset_id,
  259. ];
  260. if ($request->has("naming_rule_id") && $request->get("naming_rule_id") > 0) {
  261. $keys = $customFieldRepo->keysByGroup($request->get("naming_rule_id"));
  262. $formData['naming_rules'] = $request->only($keys);
  263. }
  264. $task->fill($formData);
  265. $changes = ModelChangeDetector::detector(ActionObjectType::TASK, $task);
  266. $task->save();
  267. ActionRepository::createByTask($task, ObjectAction::EDITED, objectChanges: $changes);
  268. return $this->noContent();
  269. }
  270. /**
  271. * Remove the specified resource from storage.
  272. */
  273. public function destroy(string $id)
  274. {
  275. $isAction=true;
  276. $task = Task::query()->allowed($id,$isAction)->find($id);
  277. if($task===null||is_null($task)){
  278. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  279. };
  280. $task->delete();
  281. Approval::query()->where('object_type','task')->where('object_id',$id)->delete();
  282. ActionRepository::createByTask($task, ObjectAction::DELETED);
  283. return $this->noContent();
  284. }
  285. public function batchStore(BatchCreateRequest $request, CustomFieldRepository $customFieldRepo)
  286. {
  287. $isAction=true;
  288. $projectId=$request->get("project_id");
  289. $project = Project::allowed($projectId,$isAction)->find($projectId);
  290. if($project===null||is_null($project)){
  291. return $this->badRequest('Permission denied or project not found. Please contact the administrator.');
  292. };
  293. $parsedItems = [];
  294. $previousItem = [];
  295. foreach ($request->items as $index => $item) {
  296. if ($index == 0) {
  297. $newItem = $item;
  298. } else {
  299. $newItem = [];
  300. foreach ($item as $k => $v) {
  301. $newItem[$k] = $v == "ditto" ? data_get($previousItem, $k) : $v;
  302. }
  303. }
  304. $previousItem = $newItem;
  305. $parsedItems[] = $newItem;
  306. }
  307. $itemRules = new BatchCreateItemRules();
  308. foreach ($parsedItems as $index => $item) {
  309. $rules = $itemRules->rules($item);
  310. try {
  311. $validator = Validator::make($item, $rules);
  312. if ($validator->fails()) {
  313. return $this->badRequest(sprintf("line: %d, %s", $index + 1, $validator->errors()->first()));
  314. }
  315. } catch (\Exception $e) {
  316. return $this->badRequest(sprintf("line: %d, %s", $index + 1, $e->getMessage()));
  317. }
  318. }
  319. foreach ($parsedItems as $item) {
  320. $requirement=$item['requirement_id']>0 ? Requirement::query()->findOrFail($item['requirement_id']) : null;
  321. $item["whitelist"]=!empty($item['whitelist']) ? sprintf(",%s,", implode(',',$item['whitelist'])) : null;
  322. $namingRuleId = data_get($item, "naming_rule_id", 0);
  323. if ($namingRuleId > 0) {
  324. $keys = $customFieldRepo->keysByGroup($namingRuleId);
  325. $item['naming_rules'] = collect($item)->only($keys)->toArray();
  326. }
  327. $task = Task::query()->create([
  328. ...$item,
  329. 'project_id' => $project->id,
  330. 'parent_id' => $request->parent_id,
  331. 'company_id' => Auth::user()->company_id,
  332. 'created_by' => Auth::id(),
  333. 'asset_id' => $requirement?->asset_id,
  334. 'requirement_group_id'=> $requirement?->requirement_group_id,
  335. ]);
  336. ActionRepository::createByTask($task, ObjectAction::CREATED);
  337. }
  338. return $this->noContent();
  339. }
  340. /**
  341. * 容器链接
  342. *
  343. * @param LinkContainerRequest $request
  344. * @param string $id
  345. * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
  346. */
  347. public function linkContainer(LinkContainerRequest $request, string $id)
  348. {
  349. $isAction=true;
  350. $task = Task::query()->allowed($id,$isAction)->find($id);
  351. if($task===null||is_null($task)){
  352. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  353. };
  354. $libraryIds = Library::query()->allowed()->where("project_id", $task->project_id)->pluck("id");
  355. if ($libraryIds->isEmpty()) {
  356. return $this->badRequest("No container exists for the current task item");
  357. }
  358. $containers = Container::query()
  359. ->allowed()
  360. ->whereIn("library_id", $libraryIds->toArray())
  361. ->whereIn("id", $request->get("container_ids", []))
  362. ->get(['id']);
  363. if (! $containers) {
  364. return $this->badRequest("Please select a valid container");
  365. }
  366. foreach ($containers as $container) {
  367. TaskContainer::query()->firstOrCreate([
  368. 'container_id' => $container->id,
  369. 'task_id' => $task->id
  370. ]);
  371. }
  372. return $this->noContent();
  373. }
  374. public function unlinkContainer(string $id)
  375. {
  376. $taskContainer = TaskContainer::query()->findOrFail($id);
  377. $taskId=$taskContainer->task_id;
  378. $isAction=true;
  379. $task = Task::query()->allowed($taskId,$isAction)->find($taskId);
  380. if($task===null||is_null($task)){
  381. return $this->badRequest('Permission denied or task not found. Please contact the administrator.');
  382. };
  383. $taskContainer->delete();
  384. return $this->noContent();
  385. }
  386. /**
  387. * 待关联的容器
  388. *
  389. * @param Request $request
  390. * @param string $id
  391. * @return \Illuminate\Http\JsonResponse
  392. */
  393. public function containerToBeLinked(Request $request, string $id)
  394. {
  395. $task = Task::query()->allowed($id)->findOrFail($id);
  396. $libraryIds = Library::query()->allowed()->where("project_id", $task->project_id)->pluck("id");
  397. if ($libraryIds->isEmpty()) {
  398. return $this->badRequest("No container exists for the current task item");
  399. }
  400. $containers = Container::query()
  401. ->allowed()
  402. ->leftJoin("task_container", "containers.id", "=", "task_container.container_id")
  403. ->where($request->only(['library_id']))
  404. ->whereIn("library_id", $libraryIds->toArray())
  405. ->whereNull("task_container.id")
  406. ->selectRaw("containers.id, containers.name")
  407. ->get();
  408. return $this->success([
  409. 'data' => $containers
  410. ]);
  411. }
  412. }