ExternalIdService.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <?php
  2. namespace App\Services;
  3. use App\Models\Student;
  4. use App\Models\Teacher;
  5. use App\Models\User;
  6. use Illuminate\Support\Facades\DB;
  7. use Illuminate\Support\Facades\Hash;
  8. use Illuminate\Support\Facades\Log;
  9. class ExternalIdService
  10. {
  11. /**
  12. * 处理学生外部ID
  13. * 如果ID不存在,自动创建用户和学生记录
  14. */
  15. public function handleStudentExternalId(int|string $studentId, array $studentData = []): Student
  16. {
  17. // student_id是int类型,直接使用
  18. // 检查学生是否已存在
  19. $student = Student::where('student_id', $studentId)->first();
  20. if ($student) {
  21. Log::info('外部学生ID已存在', ['student_id' => $studentId]);
  22. return $student;
  23. }
  24. // 创建用户记录
  25. $userData = [
  26. 'user_id' => (string) $studentId, // user表是varchar类型
  27. 'username' => $studentData['name'],
  28. 'password_hash' => Hash::make(env('STUDENT_HASH', 'UqB6KHYx84')),
  29. 'role' => 'student',
  30. 'full_name' => $studentData['name'] ?? '未知学生',
  31. 'email' => $studentId . '@student.edu',
  32. 'is_active' => 1,
  33. ];
  34. $user = User::create($userData);
  35. // 创建学生记录
  36. $student = Student::create([
  37. 'student_id' => $studentId,
  38. 'name' => $studentData['name'] ?? '未知学生',
  39. 'grade' => $studentData['grade'] ?? '未知年级',
  40. 'class_name' => $studentData['class_name'] ?? '',
  41. 'teacher_id' => $studentData['teacher_id'] ?? null,
  42. 'remark' => $studentData['remark'] ?? '自动创建',
  43. ]);
  44. Log::info('外部学生ID自动创建成功', [
  45. 'student_id' => $studentId,
  46. 'user_id' => $user->user_id
  47. ]);
  48. return $student;
  49. }
  50. /**
  51. * 处理教师外部ID
  52. * 如果ID存在,更新name;如果不存在,创建用户和教师记录
  53. * 【修复】避免邮箱重复导致的错误
  54. */
  55. public function handleTeacherExternalId(int|string $teacherId, array $teacherData = []): Teacher
  56. {
  57. // teacher_id是int类型,直接使用
  58. // 【修复】使用事务确保并发安全
  59. return DB::transaction(function () use ($teacherId, $teacherData) {
  60. // 检查教师是否已存在(使用forUpdate防止并发问题)
  61. $teacher = Teacher::where('teacher_id', $teacherId)
  62. ->lockForUpdate()
  63. ->first();
  64. if ($teacher) {
  65. // 教师已存在,检查是否需要更新name
  66. $needsUpdate = false;
  67. $updateData = [];
  68. if (!empty($teacherData['name']) && $teacher->name !== $teacherData['name']) {
  69. $updateData['name'] = $teacherData['name'];
  70. $needsUpdate = true;
  71. Log::info('外部教师ID存在,更新name', [
  72. 'teacher_id' => $teacherId,
  73. 'old_name' => $teacher->name,
  74. 'new_name' => $teacherData['name']
  75. ]);
  76. }
  77. if (!empty($teacherData['subject']) && $teacher->subject !== $teacherData['subject']) {
  78. $updateData['subject'] = $teacherData['subject'];
  79. $needsUpdate = true;
  80. }
  81. if ($needsUpdate) {
  82. $teacher->update($updateData);
  83. Log::info('外部教师信息更新成功', [
  84. 'teacher_id' => $teacherId,
  85. 'updated_fields' => array_keys($updateData)
  86. ]);
  87. } else {
  88. Log::info('外部教师ID已存在,无需更新', ['teacher_id' => $teacherId]);
  89. }
  90. return $teacher;
  91. }
  92. // 【修复】教师不存在,创建用户和教师记录
  93. // 先尝试创建用户,使用teacher_id作为邮箱
  94. $user = null;
  95. $email = $teacherId . '@teacher.edu';
  96. try {
  97. // 创建用户记录
  98. $userData = [
  99. 'user_id' => (string) $teacherId,
  100. 'username' => $teacherData['phone'] ?? (string) $teacherId,
  101. 'password_hash' => Hash::make(env('TEACHER_HASH', 'TeGiFkEim2')),
  102. 'role' => 'teacher',
  103. 'full_name' => $teacherData['name'] ?? '未知教师',
  104. 'email' => $email,
  105. 'is_active' => 1,
  106. ];
  107. $user = User::create($userData);
  108. Log::info('外部教师用户创建成功', [
  109. 'teacher_id' => $teacherId,
  110. 'user_id' => $user->user_id,
  111. 'email' => $email
  112. ]);
  113. } catch (\Exception $e) {
  114. // 如果邮箱重复,尝试使用teacher_id + 随机数
  115. if (str_contains($e->getMessage(), 'Duplicate entry') && str_contains($e->getMessage(), '@teacher.edu')) {
  116. Log::warning('邮箱已存在,尝试使用随机邮箱', [
  117. 'teacher_id' => $teacherId,
  118. 'original_email' => $email
  119. ]);
  120. // 生成唯一邮箱
  121. $uniqueEmail = $teacherId . '_' . uniqid() . '@teacher.edu';
  122. $userData['email'] = $uniqueEmail;
  123. $user = User::create($userData);
  124. Log::info('外部教师用户创建成功(使用唯一邮箱)', [
  125. 'teacher_id' => $teacherId,
  126. 'user_id' => $user->user_id,
  127. 'email' => $uniqueEmail
  128. ]);
  129. } else {
  130. // 其他错误,重新抛出
  131. throw $e;
  132. }
  133. }
  134. // 创建教师记录
  135. $teacher = Teacher::create([
  136. 'teacher_id' => $teacherId,
  137. 'teacher_string_id' => $teacherData['teacher_string_id'] ?? null,
  138. 'user_id' => $user->user_id,
  139. 'name' => $teacherData['name'] ?? '未知教师',
  140. 'subject' => $teacherData['subject'] ?? '数学',
  141. ]);
  142. Log::info('外部教师ID自动创建成功', [
  143. 'teacher_id' => $teacherId,
  144. 'user_id' => $user->user_id,
  145. 'name' => $teacherData['name'] ?? '未知教师'
  146. ]);
  147. return $teacher;
  148. });
  149. }
  150. /**
  151. * 批量处理学生外部ID
  152. */
  153. public function handleBatchStudentExternalIds(array $studentIds): array
  154. {
  155. $results = [];
  156. foreach ($studentIds as $studentId => $studentData) {
  157. try {
  158. $student = $this->handleStudentExternalId($studentId, $studentData);
  159. $results[$studentId] = ['success' => true, 'student' => $student];
  160. } catch (\Exception $e) {
  161. Log::error('批量处理学生ID失败', [
  162. 'student_id' => $studentId,
  163. 'error' => $e->getMessage()
  164. ]);
  165. $results[$studentId] = ['success' => false, 'error' => $e->getMessage()];
  166. }
  167. }
  168. return $results;
  169. }
  170. /**
  171. * 验证外部ID格式
  172. */
  173. public function validateExternalId(int|string $externalId, string $type = 'student'): bool
  174. {
  175. $externalId = (string) $externalId;
  176. // 基本格式验证
  177. if (empty($externalId)) {
  178. return false;
  179. }
  180. // 长度验证(1-64字符)
  181. if (strlen($externalId) > 64 || strlen($externalId) < 1) {
  182. return false;
  183. }
  184. // 字符验证(允许字母、数字、下划线、连字符)
  185. if (!preg_match('/^[a-zA-Z0-9_-]+$/', $externalId)) {
  186. return false;
  187. }
  188. return true;
  189. }
  190. /**
  191. * 通过外部ID获取学生信息(包含关联)
  192. */
  193. public function getStudentWithRelations(int|string $studentId): ?Student
  194. {
  195. return Student::with(['teacher.user', 'user'])
  196. ->where('student_id', $studentId)
  197. ->first();
  198. }
  199. /**
  200. * 通过外部ID获取教师信息(包含关联)
  201. */
  202. public function getTeacherWithRelations(int|string $teacherId): ?Teacher
  203. {
  204. return Teacher::with(['user', 'students'])
  205. ->where('teacher_id', $teacherId)
  206. ->first();
  207. }
  208. /**
  209. * 获取教师的学生列表
  210. */
  211. public function getTeacherStudents(int|string $teacherId): Collection
  212. {
  213. return Student::with(['user'])
  214. ->where('teacher_id', $teacherId)
  215. ->orderBy('name')
  216. ->get();
  217. }
  218. /**
  219. * 获取学生的试卷列表
  220. */
  221. public function getStudentPapers(int|string $studentId): Collection
  222. {
  223. return Student::find($studentId)
  224. ?->papers()
  225. ->with(['paperQuestions.question'])
  226. ->orderBy('created_at', 'desc')
  227. ->get() ?? collect();
  228. }
  229. /**
  230. * 检查外部ID是否存在
  231. */
  232. public function studentExists(int|string $studentId): bool
  233. {
  234. return Student::where('student_id', $studentId)->exists();
  235. }
  236. /**
  237. * 检查外部ID是否存在
  238. */
  239. public function teacherExists(int|string $teacherId): bool
  240. {
  241. return Teacher::where('teacher_id', $teacherId)->exists();
  242. }
  243. }