領収証発行サービス
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
11KB

  1. <?php
  2. namespace App\Http\Controllers\Web;
  3. use App\Codes\HTTPResultCode as ResultCode;
  4. use App\Codes\UserRole;
  5. use App\Exceptions\AppCommonException;
  6. use App\Exceptions\GeneralErrorMessageException;
  7. use Exception;
  8. use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
  9. use Illuminate\Foundation\Bus\DispatchesJobs;
  10. use Illuminate\Foundation\Validation\ValidatesRequests;
  11. use Illuminate\Http\JsonResponse;
  12. use Illuminate\Http\Request;
  13. use Illuminate\Http\Response;
  14. use Illuminate\Routing\Controller as BaseController;
  15. use Illuminate\Support\Arr;
  16. use Illuminate\Support\Facades\Auth;
  17. use Illuminate\Support\Facades\Log;
  18. use Illuminate\Support\Facades\Validator;
  19. use Illuminate\Support\Str;
  20. use Illuminate\Validation\ValidationException;
  21. use LogicException;
  22. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  23. use Symfony\Component\HttpKernel\Exception\HttpException;
  24. abstract class WebController extends BaseController
  25. {
  26. use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
  27. const COL_NAME_CREATED_AT = 'created_at';
  28. const COL_NAME_UPDATED_AT = 'updated_at';
  29. const COL_NAME_RESULT_CODE = 'result';
  30. const COL_NAME_DATA = 'data';
  31. const COL_NAME_MESSAGES = 'messages';
  32. const COL_NAME_GENERAL_MESSAGE = 'general';
  33. const COL_NAME_EMAIL_ID = 'email_id';
  34. const COL_NAME_ERRORS = 'errors';
  35. /**
  36. * バリデートした結果を格納
  37. *
  38. * @var array
  39. */
  40. protected $validated = [];
  41. /**
  42. * 画面へ返却するメールID
  43. *
  44. * @var integer|null
  45. */
  46. private int|null $emailId = null;
  47. /**
  48. * 返却するメッセージ
  49. *
  50. * @var array|null
  51. */
  52. private array|null $messages = null;
  53. /**
  54. * 返却するメッセージ
  55. *
  56. * @var string|null
  57. */
  58. private string|null $generalMessage = null;
  59. /**
  60. * 返却するデータ
  61. *
  62. * @var mixed|null
  63. */
  64. private $data = null;
  65. /**
  66. * 返却する結果コード
  67. *
  68. * @var ResultCode|null
  69. */
  70. private ResultCode|null $resultCode = ResultCode::SECCESS;
  71. public function __construct()
  72. {
  73. }
  74. /**
  75. * パラメータオブジェクト
  76. */
  77. protected function getParam(): IParam
  78. {
  79. return $this->param;
  80. }
  81. /**
  82. * コントローラーの名前
  83. * オーバーライドされることを想定
  84. * 主に、Routeのドキュメント作成用
  85. *
  86. * @return string
  87. */
  88. public function name(): string
  89. {
  90. return "---未設定---";
  91. }
  92. /**
  93. * コントローラーの説明
  94. * オーバーライドされることを想定
  95. * 主に、Routeのドキュメント作成用
  96. *
  97. * @return string
  98. */
  99. public function description(): string
  100. {
  101. return "---未設定---";
  102. }
  103. /**
  104. * オーバーライド必要
  105. * メインロジック
  106. *
  107. * @param Request $request
  108. * @return Response|JsonResponse|string
  109. */
  110. protected function run(Request $request): Response|JsonResponse|BinaryFileResponse|string
  111. {
  112. return $this->successResponse();
  113. }
  114. private function getRules()
  115. {
  116. return $this->getParam()->rules();
  117. }
  118. public function entry(Request $request)
  119. {
  120. $this->setLogContext($request);
  121. try {
  122. $validator = Validator::make($request->all(), $this->getRules());
  123. $validator->validate();
  124. } catch (ValidationException $e) {
  125. logger("validate error", ['errors' => $e->errors(), 'request' => $request->all(), 'path' => $request->path()]);
  126. return $this->validateErrorResponse($e);
  127. }
  128. try {
  129. $this->validated = $validator->validated();
  130. $this->getParam()->setData($this->validated);
  131. $this->authorize();
  132. return $this->run($request);
  133. } catch (GeneralErrorMessageException $e) {
  134. return $this->failedResponse([], $e->getMessage());
  135. } catch (AppCommonException $e) {
  136. logs()->error(sprintf("Appエラー:%s", $e->getMessage()));
  137. return $this->failedResponse();
  138. } catch (LogicException $e) {
  139. logs()->error([
  140. sprintf("実装エラー:%s", $e->getMessage()),
  141. get_class($e),
  142. $e->getFile(),
  143. $e->getLine(),
  144. $request->all(),
  145. ]);
  146. logger(array_filter($e->getTrace(), function ($val, $key) {
  147. return $key <= 5;
  148. }, ARRAY_FILTER_USE_BOTH));
  149. return $this->failedResponse();
  150. } catch (ValidationException $e) {
  151. return $this->validateErrorResponse($e);
  152. } catch (HttpException $e) {
  153. if ($e->getStatusCode() === 401) {
  154. return $this->unAuthorizedResponse();
  155. }
  156. throw e;
  157. } catch (Exception $e) {
  158. logs()->error([
  159. sprintf("例外エラー:%s", $e->getMessage()),
  160. get_class($e),
  161. $e->getFile(),
  162. $e->getLine(),
  163. $request->all(),
  164. ]);
  165. logger(array_filter($e->getTrace(), function ($val, $key) {
  166. return $key <= 5;
  167. }, ARRAY_FILTER_USE_BOTH));
  168. return $this->failedResponse();
  169. }
  170. }
  171. protected function successResponse(array|object $data = [], array|string $messages = [])
  172. {
  173. return $this->setData($data)
  174. ->setMessages($messages)
  175. ->setResultCode(ResultCode::SECCESS)
  176. ->makeResponse();
  177. }
  178. protected function failedResponse(array|object $data = [], array|string $messages = [])
  179. {
  180. return $this->setData($data)
  181. ->setMessages($messages)
  182. ->setResultCode(ResultCode::FAILED)
  183. ->makeResponse();
  184. }
  185. protected function unAuthorizedResponse(array|object $data = [], array|string $messages = [])
  186. {
  187. return $this->setData($data)
  188. ->setMessages($messages)
  189. ->setResultCode(ResultCode::UNAUTHORIZED)
  190. ->makeResponse();
  191. }
  192. protected function exclusiveErrorResponse(array|object $data = [], array|string $messages = [])
  193. {
  194. return $this->setData($data)
  195. ->setMessages($messages)
  196. ->setResultCode(ResultCode::EXCLUSIVE_ERROR)
  197. ->makeResponse();
  198. }
  199. protected function validateErrorResponse(ValidationException|array $exception, string|null $generalMessage = null)
  200. {
  201. $errorMessages = [];
  202. $general = null;
  203. if ($exception instanceof ValidationException) {
  204. foreach ($exception->errors() as $key => $m) {
  205. $errorMessages[$key] = $m[0];
  206. }
  207. }
  208. if (is_array($exception)) {
  209. $errorMessages = $exception;
  210. }
  211. $general = $generalMessage ?? data_get($errorMessages, self::COL_NAME_GENERAL_MESSAGE);
  212. return $this->setData([])
  213. ->setMessages($errorMessages)
  214. ->setGeneralMessage($general)
  215. ->setResultCode(ResultCode::FAILED)
  216. ->makeResponse();
  217. }
  218. private function makeResponse()
  219. {
  220. if ($this->resultCode === null) {
  221. abort(403);
  222. }
  223. $ret = [];
  224. Arr::set($ret, self::COL_NAME_RESULT_CODE, $this->resultCode->value);
  225. if ($this->data !== null) {
  226. Arr::set($ret, self::COL_NAME_DATA, $this->data);
  227. }
  228. if ($this->messages !== null) {
  229. Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_ERRORS, $this->messages);
  230. }
  231. if ($this->generalMessage !== null) {
  232. Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_GENERAL_MESSAGE, $this->generalMessage);
  233. }
  234. if ($this->emailId !== null) {
  235. Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_EMAIL_ID, $this->emailId);
  236. }
  237. return response()
  238. ->json($ret)
  239. ->withHeaders($this->makeHeader());
  240. }
  241. private function makeHeader(): array
  242. {
  243. $header = [];
  244. $user = Auth::user();
  245. if ($user) {
  246. $header["App-User-Auth"] = sprintf("%d,%d", $user->id, $user->role->value);
  247. } else {
  248. $header["App-User-Auth"] = 'none';
  249. }
  250. return $header;
  251. }
  252. // 以下 認可関係
  253. protected array|null $roleAllow = null;
  254. protected array|null $roleDisallow = null;
  255. protected array|null $customAllow = null;
  256. protected function roleAllow(UserRole $role)
  257. {
  258. $this->roleAllow = [];
  259. foreach (UserRole::cases() as $ele) {
  260. if ($role->value <= $ele->value) {
  261. $this->roleAllow[] = $ele;
  262. }
  263. }
  264. }
  265. private function authorize()
  266. {
  267. if (!Auth::check()) {
  268. return;
  269. }
  270. $role = Auth::user()->role;
  271. if (!$this->canAccess($role)) {
  272. abort(401);
  273. }
  274. }
  275. public function canAccess(UserRole $role)
  276. {
  277. if (is_array($this->roleAllow) && !in_array($role, $this->roleAllow)) {
  278. return false;
  279. }
  280. if (is_array($this->roleDisallow) && in_array($role, $this->roleDisallow)) {
  281. return false;
  282. }
  283. return $this->canCustomAccess();
  284. }
  285. public function canCustomAccess(): bool
  286. {
  287. if (Auth::user()->role === UserRole::SUPER_ADMIN) {
  288. return true;
  289. }
  290. if ($this->customAllow === null) {
  291. return true;
  292. }
  293. $myCustoms = Auth::user()->contract->custom();
  294. foreach ($this->customAllow as $targetCustom) {
  295. if (in_array($targetCustom, $myCustoms, true)) {
  296. return true;
  297. }
  298. }
  299. return false;
  300. }
  301. // 返却用データの登録
  302. protected function setEmailId(int $emailId)
  303. {
  304. $this->emailId = $emailId;
  305. return $this;
  306. }
  307. protected function setMessages(array|string $messages)
  308. {
  309. if (is_array($messages)) {
  310. $this->messages = $messages;
  311. } else {
  312. $this->setGeneralMessage($messages);
  313. }
  314. return $this;
  315. }
  316. protected function setGeneralMessage(string|null $generalMessage)
  317. {
  318. $this->generalMessage = $generalMessage;
  319. return $this;
  320. }
  321. protected function setData($data)
  322. {
  323. $this->data = $data;
  324. return $this;
  325. }
  326. protected function setResultCode(ResultCode $resultCode)
  327. {
  328. $this->resultCode = $resultCode;
  329. return $this;
  330. }
  331. protected function setLogContext(Request $request)
  332. {
  333. Log::withContext([
  334. '__requestUuid__' => strval(Str::uuid()),
  335. '__userId__' => Auth::id(),
  336. '__path__' => $request->path(),
  337. ]);
  338. }
  339. }