|
@@ -0,0 +1,292 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+declare(encoding='UTF-8');
|
|
|
+
|
|
|
+namespace App\Services\Coach;
|
|
|
+
|
|
|
+use App\Enums\TechnicianAuthStatus;
|
|
|
+use App\Models\CoachInfoRecord;
|
|
|
+use App\Models\CoachQualRecord;
|
|
|
+use App\Models\CoachRealRecord;
|
|
|
+use App\Models\CoachUser;
|
|
|
+use App\Services\Third\AliRealAuthService;
|
|
|
+use Illuminate\Support\Facades\Auth;
|
|
|
+use Illuminate\Support\Facades\Cache;
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
+use Illuminate\Support\Facades\Log;
|
|
|
+use Illuminate\Support\Facades\Redis;
|
|
|
+
|
|
|
+class AuthService
|
|
|
+{
|
|
|
+ // 缓存相关常量
|
|
|
+ private const CACHE_KEY_PREFIX = 'coach_auth_';
|
|
|
+ private const CACHE_TTL = 300; // 5分钟
|
|
|
+
|
|
|
+ // 限流相关常量
|
|
|
+ private const RATE_LIMIT_PREFIX = 'coach_auth_limit_';
|
|
|
+ private const RATE_LIMIT_TTL = 86400; // 24小时
|
|
|
+ private const MAX_ATTEMPTS = 5; // 每24小时最多提交5次
|
|
|
+
|
|
|
+ protected ?CoachUser $coach;
|
|
|
+ protected AliRealAuthService $aliRealAuthService;
|
|
|
+
|
|
|
+ public function __construct(AliRealAuthService $aliRealAuthService)
|
|
|
+ {
|
|
|
+ $this->coach = Auth::user()->coach;
|
|
|
+ $this->aliRealAuthService = $aliRealAuthService;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查提交次数限制
|
|
|
+ *
|
|
|
+ * @param string $type 认证类型(basic_info/real_auth/qual_auth)
|
|
|
+ * @throws \Exception 超出限制时抛出异常
|
|
|
+ */
|
|
|
+ private function checkRateLimit(string $type): void
|
|
|
+ {
|
|
|
+ $key = self::RATE_LIMIT_PREFIX . $this->coach->id . ':' . $type;
|
|
|
+ $attempts = Redis::get($key) ?: 0;
|
|
|
+
|
|
|
+ if ($attempts >= self::MAX_ATTEMPTS) {
|
|
|
+ throw new \Exception('今日提交次数已达上限,请24小时后再试');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 增加计数
|
|
|
+ if ($attempts == 0) {
|
|
|
+ Redis::setex($key, self::RATE_LIMIT_TTL, 1);
|
|
|
+ } else {
|
|
|
+ Redis::incr($key);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提交基本信息认证
|
|
|
+ *
|
|
|
+ * @param array $data
|
|
|
+ * @return array
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function submitBasicInfo(array $data): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // 检查提交次数限制
|
|
|
+ $this->checkRateLimit('basic_info');
|
|
|
+
|
|
|
+ DB::beginTransaction();
|
|
|
+
|
|
|
+ // 创建基本信息记录
|
|
|
+ $infoRecord = CoachInfoRecord::create([
|
|
|
+ 'coach_id' => $this->coach->id,
|
|
|
+ 'nickname' => $data['nickname'],
|
|
|
+ 'avatar' => $data['avatar'],
|
|
|
+ 'gender' => (int)$data['gender'],
|
|
|
+ 'mobile' => $data['mobile'],
|
|
|
+ 'age' => $data['age'],
|
|
|
+ 'birthday' => $data['birthday'],
|
|
|
+ 'work_years' => $data['work_years'],
|
|
|
+ 'intention_city' => $data['intention_city'],
|
|
|
+ 'introduction' => $data['introduction'],
|
|
|
+ 'portrait_images' => $data['portrait_images'],
|
|
|
+ 'state' => TechnicianAuthStatus::AUDITING->value
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 清除认证缓存
|
|
|
+ $this->clearAuthCache();
|
|
|
+
|
|
|
+ DB::commit();
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'id' => $infoRecord->id,
|
|
|
+ 'state' => $infoRecord->state,
|
|
|
+ 'state_text' => TechnicianAuthStatus::fromValue($infoRecord->state)->label()
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ DB::rollBack();
|
|
|
+ $this->logError('提交基本信息认证失败', [], $e);
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提交实名认证
|
|
|
+ *
|
|
|
+ * @param array $data
|
|
|
+ * @return array
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function submitRealAuth(array $data): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // 检查提交次数限制
|
|
|
+ $this->checkRateLimit('real_auth');
|
|
|
+
|
|
|
+ DB::beginTransaction();
|
|
|
+
|
|
|
+ // 创建实名认证记录
|
|
|
+ $realRecord = CoachRealRecord::create([
|
|
|
+ 'coach_id' => $this->coach->id,
|
|
|
+ 'real_name' => $data['real_name'],
|
|
|
+ 'id_card' => $data['id_card'],
|
|
|
+ 'id_card_front_photo' => $data['id_card_front_photo'],
|
|
|
+ 'id_card_back_photo' => $data['id_card_back_photo'],
|
|
|
+ 'id_card_hand_photo' => $data['id_card_hand_photo'],
|
|
|
+ 'state' => TechnicianAuthStatus::AUDITING->value
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 调用阿里实名认证
|
|
|
+ $response = $this->aliRealAuthService->verify([
|
|
|
+ 'real_name' => $data['real_name'],
|
|
|
+ 'id_card' => $data['id_card']
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 清除认证缓存
|
|
|
+ $this->clearAuthCache();
|
|
|
+
|
|
|
+ DB::commit();
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'record' => [
|
|
|
+ 'id' => $realRecord->id,
|
|
|
+ 'state' => $realRecord->state,
|
|
|
+ 'state_text' => TechnicianAuthStatus::fromValue($realRecord->state)->label()
|
|
|
+ ],
|
|
|
+ 'can_proceed' => true,
|
|
|
+ 'message' => '实名认证提交成功,系统正在审核,您可以继续提交资质认证'
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ DB::rollBack();
|
|
|
+ $this->logError('提交实名认证失败', [], $e);
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提交资质认证
|
|
|
+ *
|
|
|
+ * @param array $data
|
|
|
+ * @return array
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function submitQualAuth(array $data): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // 检查提交次数限制
|
|
|
+ $this->checkRateLimit('qual_auth');
|
|
|
+
|
|
|
+ DB::beginTransaction();
|
|
|
+
|
|
|
+ // 创建资质认证记录
|
|
|
+ $qualRecord = CoachQualRecord::create([
|
|
|
+ 'coach_id' => $this->coach->id,
|
|
|
+ 'qual_type' => $data['qual_type'],
|
|
|
+ 'qual_no' => $data['qual_no'],
|
|
|
+ 'qual_photo' => $data['qual_photo'],
|
|
|
+ 'valid_start' => $data['valid_start'] ?? null,
|
|
|
+ 'valid_end' => $data['valid_end'] ?? null,
|
|
|
+ 'state' => TechnicianAuthStatus::AUDITING->value
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 检查实名认证状态,添加提示信息
|
|
|
+ $warning = null;
|
|
|
+ if ($this->coach->real_auth?->state === TechnicianAuthStatus::AUDITING->value) {
|
|
|
+ $warning = '实名认证正在审核中,如审核失败可能需要重新提交资质认证';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清除认证缓存
|
|
|
+ $this->clearAuthCache();
|
|
|
+
|
|
|
+ DB::commit();
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'record' => [
|
|
|
+ 'id' => $qualRecord->id,
|
|
|
+ 'state' => $qualRecord->state,
|
|
|
+ 'state_text' => TechnicianAuthStatus::fromValue($qualRecord->state)->label()
|
|
|
+ ],
|
|
|
+ 'warning' => $warning
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ DB::rollBack();
|
|
|
+ $this->logError('提交资质认证失败', [], $e);
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查认证状态
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function checkAuthStatus(): array
|
|
|
+ {
|
|
|
+ // 获取最新的基本信息记录
|
|
|
+ $latestInfo = CoachInfoRecord::where('coach_id', $this->coach->id)
|
|
|
+ ->latest()
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ // 获取最新的实名认证记录
|
|
|
+ $latestRealAuth = CoachRealRecord::where('coach_id', $this->coach->id)
|
|
|
+ ->latest()
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ // 获取最新的资质认证记录
|
|
|
+ $latestQualAuth = CoachQualRecord::where('coach_id', $this->coach->id)
|
|
|
+ ->latest()
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ $getStateInfo = function ($record) {
|
|
|
+ if (!$record) {
|
|
|
+ return [
|
|
|
+ 'state' => 0,
|
|
|
+ 'state_text' => '未提交'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ return [
|
|
|
+ 'state' => $record->state,
|
|
|
+ 'state_text' => TechnicianAuthStatus::fromValue($record->state)->label()
|
|
|
+ ];
|
|
|
+ };
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'basic_info' => $getStateInfo($latestInfo),
|
|
|
+ 'real_auth' => $getStateInfo($latestRealAuth),
|
|
|
+ 'qual_auth' => $getStateInfo($latestQualAuth)
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录信息日志
|
|
|
+ */
|
|
|
+ private function logInfo(string $message, array $data = [])
|
|
|
+ {
|
|
|
+ Log::info($message, array_merge([
|
|
|
+ 'coach_id' => $this->coach->id,
|
|
|
+ 'ip' => request()->ip(),
|
|
|
+ 'timestamp' => now()->toDateTimeString(),
|
|
|
+ ], $data));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录错误日志
|
|
|
+ */
|
|
|
+ private function logError(string $message, array $data = [], ?\Exception $e = null)
|
|
|
+ {
|
|
|
+ Log::error($message, array_merge([
|
|
|
+ 'coach_id' => $this->coach->id,
|
|
|
+ 'ip' => request()->ip(),
|
|
|
+ 'timestamp' => now()->toDateTimeString(),
|
|
|
+ ], $data, $e ? [
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ 'file' => $e->getFile(),
|
|
|
+ 'line' => $e->getLine(),
|
|
|
+ ] : []));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清除认证缓存
|
|
|
+ */
|
|
|
+ private function clearAuthCache()
|
|
|
+ {
|
|
|
+ Cache::forget(self::CACHE_KEY_PREFIX.$this->coach->id);
|
|
|
+ }
|
|
|
+}
|