AuthService.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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' => [
  217. ...$getStateInfo($latestInfo),
  218. 'detail' => $latestInfo ? [
  219. 'nickname' => $latestInfo->nickname,
  220. 'avatar' => $latestInfo->avatar,
  221. 'gender' => $latestInfo->gender,
  222. 'mobile' => $latestInfo->mobile,
  223. 'age' => $latestInfo->age,
  224. 'birthday' => $latestInfo->birthday,
  225. 'work_years' => $latestInfo->work_years,
  226. 'intention_city' => $latestInfo->intention_city,
  227. 'introduction' => $latestInfo->introduction,
  228. 'portrait_images' => $latestInfo->portrait_images,
  229. 'created_at' => $latestInfo->created_at,
  230. 'updated_at' => $latestInfo->updated_at,
  231. ] : null,
  232. ],
  233. 'real_auth' => [
  234. ...$getStateInfo($latestRealAuth),
  235. 'detail' => $latestRealAuth ? [
  236. 'real_name' => $latestRealAuth->real_name,
  237. 'id_card' => $latestRealAuth->id_card,
  238. 'id_card_front_photo' => $latestRealAuth->id_card_front_photo,
  239. 'id_card_back_photo' => $latestRealAuth->id_card_back_photo,
  240. 'id_card_hand_photo' => $latestRealAuth->id_card_hand_photo,
  241. 'created_at' => $latestRealAuth->created_at,
  242. 'updated_at' => $latestRealAuth->updated_at,
  243. ] : null,
  244. ],
  245. 'qual_auth' => [
  246. ...$getStateInfo($latestQualAuth),
  247. 'detail' => $latestQualAuth ? [
  248. 'qual_type' => $latestQualAuth->qual_type,
  249. 'qual_no' => $latestQualAuth->qual_no,
  250. 'qual_photo' => $latestQualAuth->qual_photo,
  251. 'valid_start' => $latestQualAuth->valid_start,
  252. 'valid_end' => $latestQualAuth->valid_end,
  253. 'created_at' => $latestQualAuth->created_at,
  254. 'updated_at' => $latestQualAuth->updated_at,
  255. ] : null,
  256. ],
  257. ];
  258. }
  259. /**
  260. * 记录信息日志
  261. */
  262. private function logInfo(string $message, array $data = [])
  263. {
  264. Log::info($message, array_merge([
  265. 'coach_id' => $this->coach->id,
  266. 'ip' => request()->ip(),
  267. 'timestamp' => now()->toDateTimeString(),
  268. ], $data));
  269. }
  270. /**
  271. * 记录错误日志
  272. */
  273. private function logError(string $message, array $data = [], ?\Exception $e = null)
  274. {
  275. Log::error($message, array_merge([
  276. 'coach_id' => $this->coach->id,
  277. 'ip' => request()->ip(),
  278. 'timestamp' => now()->toDateTimeString(),
  279. ], $data, $e ? [
  280. 'error' => $e->getMessage(),
  281. 'file' => $e->getFile(),
  282. 'line' => $e->getLine(),
  283. ] : []));
  284. }
  285. /**
  286. * 清除认证缓存
  287. */
  288. private function clearAuthCache()
  289. {
  290. Cache::forget(self::CACHE_KEY_PREFIX.$this->coach->id);
  291. }
  292. }