領収証発行サービス
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

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