領収証発行サービス
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.

425 lines
12KB

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