AuthService.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. <?php
  2. declare(encoding='UTF-8');
  3. namespace App\Services\Coach;
  4. use App\Enums\TechnicianAuthStatus;
  5. use App\Models\CoachInfoRecord;
  6. use App\Models\CoachQualRecord;
  7. use App\Models\CoachRealRecord;
  8. use App\Models\CoachUser;
  9. use App\Services\Third\AliRealAuthService;
  10. use Illuminate\Support\Facades\Auth;
  11. use Illuminate\Support\Facades\Cache;
  12. use Illuminate\Support\Facades\DB;
  13. use Illuminate\Support\Facades\Log;
  14. use Illuminate\Support\Facades\Redis;
  15. class AuthService
  16. {
  17. // 缓存相关常量
  18. private const CACHE_KEY_PREFIX = 'coach_auth_';
  19. private const CACHE_TTL = 300; // 5分钟
  20. // 限流相关常量
  21. private const RATE_LIMIT_PREFIX = 'coach_auth_limit_';
  22. private const RATE_LIMIT_TTL = 86400; // 24小时
  23. private const MAX_ATTEMPTS = 5; // 每24小时最多提交5次
  24. protected ?CoachUser $coach;
  25. protected AliRealAuthService $aliRealAuthService;
  26. public function __construct(AliRealAuthService $aliRealAuthService)
  27. {
  28. $this->coach = Auth::user()->coach;
  29. $this->aliRealAuthService = $aliRealAuthService;
  30. }
  31. /**
  32. * 检查提交次数限制
  33. *
  34. * @param string $type 认证类型(basic_info/real_auth/qual_auth)
  35. * @throws \Exception 超出限制时抛出异常
  36. */
  37. private function checkRateLimit(string $type): void
  38. {
  39. $key = self::RATE_LIMIT_PREFIX . $this->coach->id . ':' . $type;
  40. $attempts = Redis::get($key) ?: 0;
  41. if ($attempts >= self::MAX_ATTEMPTS) {
  42. throw new \Exception('今日提交次数已达上限,请24小时后再试');
  43. }
  44. // 增加计数
  45. if ($attempts == 0) {
  46. Redis::setex($key, self::RATE_LIMIT_TTL, 1);
  47. } else {
  48. Redis::incr($key);
  49. }
  50. }
  51. /**
  52. * 提交基本信息认证
  53. *
  54. * @param array $data
  55. * @return array
  56. * @throws \Exception
  57. */
  58. public function submitBasicInfo(array $data): array
  59. {
  60. try {
  61. // 检查提交次数限制
  62. $this->checkRateLimit('basic_info');
  63. DB::beginTransaction();
  64. // 创建基本信息记录
  65. $infoRecord = CoachInfoRecord::create([
  66. 'coach_id' => $this->coach->id,
  67. 'nickname' => $data['nickname'],
  68. 'avatar' => $data['avatar'],
  69. 'gender' => (int)$data['gender'],
  70. 'mobile' => $data['mobile'],
  71. 'age' => $data['age'],
  72. 'birthday' => $data['birthday'],
  73. 'work_years' => $data['work_years'],
  74. 'intention_city' => $data['intention_city'],
  75. 'introduction' => $data['introduction'],
  76. 'portrait_images' => $data['portrait_images'],
  77. 'state' => TechnicianAuthStatus::AUDITING->value
  78. ]);
  79. // 清除认证缓存
  80. $this->clearAuthCache();
  81. DB::commit();
  82. return [
  83. 'id' => $infoRecord->id,
  84. 'state' => $infoRecord->state,
  85. 'state_text' => TechnicianAuthStatus::fromValue($infoRecord->state)->label()
  86. ];
  87. } catch (\Exception $e) {
  88. DB::rollBack();
  89. $this->logError('提交基本信息认证失败', [], $e);
  90. throw $e;
  91. }
  92. }
  93. /**
  94. * 提交实名认证
  95. *
  96. * @param array $data
  97. * @return array
  98. * @throws \Exception
  99. */
  100. public function submitRealAuth(array $data): array
  101. {
  102. try {
  103. // 检查提交次数限制
  104. $this->checkRateLimit('real_auth');
  105. DB::beginTransaction();
  106. // 创建实名认证记录
  107. $realRecord = CoachRealRecord::create([
  108. 'coach_id' => $this->coach->id,
  109. 'real_name' => $data['real_name'],
  110. 'id_card' => $data['id_card'],
  111. 'id_card_front_photo' => $data['id_card_front_photo'],
  112. 'id_card_back_photo' => $data['id_card_back_photo'],
  113. 'id_card_hand_photo' => $data['id_card_hand_photo'],
  114. 'state' => TechnicianAuthStatus::AUDITING->value
  115. ]);
  116. // 调用阿里实名认证
  117. $response = $this->aliRealAuthService->verify([
  118. 'real_name' => $data['real_name'],
  119. 'id_card' => $data['id_card']
  120. ]);
  121. // 清除认证缓存
  122. $this->clearAuthCache();
  123. DB::commit();
  124. return [
  125. 'record' => [
  126. 'id' => $realRecord->id,
  127. 'state' => $realRecord->state,
  128. 'state_text' => TechnicianAuthStatus::fromValue($realRecord->state)->label()
  129. ],
  130. 'can_proceed' => true,
  131. 'message' => '实名认证提交成功,系统正在审核,您可以继续提交资质认证'
  132. ];
  133. } catch (\Exception $e) {
  134. DB::rollBack();
  135. $this->logError('提交实名认证失败', [], $e);
  136. throw $e;
  137. }
  138. }
  139. /**
  140. * 提交资质认证
  141. *
  142. * @param array $data
  143. * @return array
  144. * @throws \Exception
  145. */
  146. public function submitQualAuth(array $data): array
  147. {
  148. try {
  149. // 检查提交次数限制
  150. $this->checkRateLimit('qual_auth');
  151. DB::beginTransaction();
  152. // 创建资质认证记录
  153. $qualRecord = CoachQualRecord::create([
  154. 'coach_id' => $this->coach->id,
  155. 'qual_type' => $data['qual_type'],
  156. 'qual_no' => $data['qual_no'],
  157. 'qual_photo' => $data['qual_photo'],
  158. 'valid_start' => $data['valid_start'] ?? null,
  159. 'valid_end' => $data['valid_end'] ?? null,
  160. 'state' => TechnicianAuthStatus::AUDITING->value
  161. ]);
  162. // 检查实名认证状态,添加提示信息
  163. $warning = null;
  164. if ($this->coach->real_auth?->state === TechnicianAuthStatus::AUDITING->value) {
  165. $warning = '实名认证正在审核中,如审核失败可能需要重新提交资质认证';
  166. }
  167. // 清除认证缓存
  168. $this->clearAuthCache();
  169. DB::commit();
  170. return [
  171. 'record' => [
  172. 'id' => $qualRecord->id,
  173. 'state' => $qualRecord->state,
  174. 'state_text' => TechnicianAuthStatus::fromValue($qualRecord->state)->label()
  175. ],
  176. 'warning' => $warning
  177. ];
  178. } catch (\Exception $e) {
  179. DB::rollBack();
  180. $this->logError('提交资质认证失败', [], $e);
  181. throw $e;
  182. }
  183. }
  184. /**
  185. * 检查认证状态
  186. *
  187. * @return array
  188. */
  189. public function checkAuthStatus(): array
  190. {
  191. // 获取最新的基本信息记录
  192. $latestInfo = CoachInfoRecord::where('coach_id', $this->coach->id)
  193. ->latest()
  194. ->first();
  195. // 获取最新的实名认证记录
  196. $latestRealAuth = CoachRealRecord::where('coach_id', $this->coach->id)
  197. ->latest()
  198. ->first();
  199. // 获取最新的资质认证记录
  200. $latestQualAuth = CoachQualRecord::where('coach_id', $this->coach->id)
  201. ->latest()
  202. ->first();
  203. $getStateInfo = function ($record) {
  204. if (!$record) {
  205. return [
  206. 'state' => 0,
  207. 'state_text' => '未提交'
  208. ];
  209. }
  210. return [
  211. 'state' => $record->state,
  212. 'state_text' => TechnicianAuthStatus::fromValue($record->state)->label()
  213. ];
  214. };
  215. return [
  216. 'basic_info' => $getStateInfo($latestInfo),
  217. 'real_auth' => $getStateInfo($latestRealAuth),
  218. 'qual_auth' => $getStateInfo($latestQualAuth)
  219. ];
  220. }
  221. /**
  222. * 记录信息日志
  223. */
  224. private function logInfo(string $message, array $data = [])
  225. {
  226. Log::info($message, array_merge([
  227. 'coach_id' => $this->coach->id,
  228. 'ip' => request()->ip(),
  229. 'timestamp' => now()->toDateTimeString(),
  230. ], $data));
  231. }
  232. /**
  233. * 记录错误日志
  234. */
  235. private function logError(string $message, array $data = [], ?\Exception $e = null)
  236. {
  237. Log::error($message, array_merge([
  238. 'coach_id' => $this->coach->id,
  239. 'ip' => request()->ip(),
  240. 'timestamp' => now()->toDateTimeString(),
  241. ], $data, $e ? [
  242. 'error' => $e->getMessage(),
  243. 'file' => $e->getFile(),
  244. 'line' => $e->getLine(),
  245. ] : []));
  246. }
  247. /**
  248. * 清除认证缓存
  249. */
  250. private function clearAuthCache()
  251. {
  252. Cache::forget(self::CACHE_KEY_PREFIX.$this->coach->id);
  253. }
  254. }