TaskController.php 16 KB

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