diff --git a/app/Console/Commands/CheckMissingSchoolmateData.php b/app/Console/Commands/CheckMissingSchoolmateData.php new file mode 100644 index 0000000..ca68387 --- /dev/null +++ b/app/Console/Commands/CheckMissingSchoolmateData.php @@ -0,0 +1,157 @@ +allowedCourseTypeNames; + $allowedCourseNames = $this->allowedCourseNames; + + $missingUsers = User::query() + ->where(function ($query) { + $query->whereNull('is_schoolmate') + ->orWhere('is_schoolmate', '!=', 1); + }) + ->whereHas('courseSigns', function ($query) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $query->where('status', 1) + ->whereHas('course', function ($courseQuery) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $courseQuery->where(function ($eligibleQuery) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $eligibleQuery->where(function ($autoSchoolmateQuery) use ($today) { + $autoSchoolmateQuery->where('auto_schoolmate', 1) + ->whereNotNull('start_date') + ->where('start_date', '<=', $today); + })->orWhereHas('typeDetail', function ($typeQuery) use ($allowedCourseTypeNames) { + $typeQuery->whereIn('name', $allowedCourseTypeNames); + })->orWhereIn('name', $allowedCourseNames); + }); + }); + }) + ->with([ + 'courseSigns' => function ($query) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $query->where('status', 1) + ->whereHas('course', function ($courseQuery) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $courseQuery->where(function ($eligibleQuery) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $eligibleQuery->where(function ($autoSchoolmateQuery) use ($today) { + $autoSchoolmateQuery->where('auto_schoolmate', 1) + ->whereNotNull('start_date') + ->where('start_date', '<=', $today); + })->orWhereHas('typeDetail', function ($typeQuery) use ($allowedCourseTypeNames) { + $typeQuery->whereIn('name', $allowedCourseTypeNames); + })->orWhereIn('name', $allowedCourseNames); + }); + }) + ->with('course.typeDetail'); + } + ]) + ->orderBy('id') + ->get(); + + $this->info('检测口径:用户当前不是校友,但存在以下任一合格来源,则判定为“应进入校友库但未进入”:'); + $this->line('1. 已审核通过,且课程开启自动入校友,且课程已开课'); + $this->line('2. 已审核通过,且课程属于高研班体系'); + $this->line('3. 已审核通过,且课程属于攀峰班体系'); + $this->line('4. 已审核通过,且课程为“首期技术经理人领航班”'); + + if ($missingUsers->isEmpty()) { + $this->info('检测完成:当前未发现应进入校友库但未进入的用户。'); + return self::SUCCESS; + } + + $this->warn('检测完成:发现 ' . $missingUsers->count() . ' 位符合条件但未进入校友库的用户。'); + $this->newLine(); + + $rows = []; + foreach ($missingUsers as $user) { + $eligibleCourses = $user->courseSigns + ->map(function ($courseSign) use ($allowedCourseTypeNames, $allowedCourseNames, $today) { + if (empty($courseSign->course)) { + return '课程已删除'; + } + + $course = $courseSign->course; + $courseTypeName = $course->typeDetail->name ?? '空'; + $reasons = []; + + if ($course->auto_schoolmate == 1 && !empty($course->start_date) && $course->start_date <= $today) { + $reasons[] = '自动入校友'; + } + if (in_array($courseTypeName, $allowedCourseTypeNames, true)) { + $reasons[] = '体系白名单'; + } + if (in_array($course->name, $allowedCourseNames, true)) { + $reasons[] = '课程白名单'; + } + + return sprintf( + '%s[课程体系=%s,auto_schoolmate=%s,start_date=%s,命中规则=%s]', + $course->name, + $courseTypeName, + (string) $course->auto_schoolmate, + $course->start_date ?: '空', + empty($reasons) ? '无' : implode('+', $reasons) + ); + }) + ->filter() + ->unique() + ->values() + ->implode(';'); + + $rows[] = [ + '用户ID' => $user->id, + '姓名' => $user->name, + '手机号' => $user->mobile, + '当前校友状态' => (string) ($user->is_schoolmate ?? '空'), + '应入校友依据' => $eligibleCourses ?: '无', + '不合格原因' => '符合校友认定条件,但当前未进入校友库', + ]; + } + + $this->table( + ['用户ID', '姓名', '手机号', '当前校友状态', '应入校友依据', '不合格原因'], + $rows + ); + + $this->info('本次仅在命令行输出异常名单,不再生成 txt 文件。'); + + return self::FAILURE; + } +} diff --git a/app/Console/Commands/CheckSchoolmateData.php b/app/Console/Commands/CheckSchoolmateData.php new file mode 100644 index 0000000..2db2095 --- /dev/null +++ b/app/Console/Commands/CheckSchoolmateData.php @@ -0,0 +1,177 @@ +allowedCourseTypeNames; + $allowedCourseNames = $this->allowedCourseNames; + $fixedUsers = User::query() + ->where('is_schoolmate', 1) + ->where(function ($query) { + $query->whereNull('mobile') + ->orWhere('mobile', ''); + }) + ->orderBy('id') + ->get(); + + $fixedCount = 0; + foreach ($fixedUsers as $user) { + $user->is_schoolmate = 0; + $user->schoolmate_time = null; + if ($user->isDirty()) { + $user->save(); + $fixedCount++; + } + } + + $invalidUsers = User::query() + ->where('is_schoolmate', 1) + ->whereDoesntHave('courseSigns', function ($query) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $query->where('status', 1) + ->whereHas('course', function ($courseQuery) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $courseQuery->where(function ($eligibleQuery) use ($today, $allowedCourseTypeNames, $allowedCourseNames) { + $eligibleQuery->where(function ($autoSchoolmateQuery) use ($today) { + $autoSchoolmateQuery->where('auto_schoolmate', 1) + ->whereNotNull('start_date') + ->where('start_date', '<=', $today); + })->orWhereHas('typeDetail', function ($typeQuery) use ($allowedCourseTypeNames) { + $typeQuery->whereIn('name', $allowedCourseTypeNames); + })->orWhereIn('name', $allowedCourseNames); + }); + }); + }) + ->with([ + 'courseSigns' => function ($query) { + $query->where('status', 1)->with('course.typeDetail'); + } + ]) + ->orderBy('id') + ->get(); + + $schoolmateTotal = User::where('is_schoolmate', 1)->count(); + $invalidCount = $invalidUsers->count(); + + $this->info("当前校友总数:{$schoolmateTotal}"); + $this->info("已自动修正无手机号但仍是校友的用户数:{$fixedCount}"); + $this->info('检测口径:用户当前是校友,但不存在以下任一合格来源,则判定为不符合要求:'); + $this->line('1. 已审核通过,且课程开启自动入校友,且课程已开课'); + $this->line('2. 已审核通过,且课程属于高研班体系'); + $this->line('3. 已审核通过,且课程属于攀峰班体系'); + $this->line('4. 已审核通过,且课程为“首期技术经理人领航班”'); + + if ($invalidUsers->isEmpty()) { + $this->info('检测完成:当前所有校友数据都符合校友认定规则。'); + return self::SUCCESS; + } + + $this->warn('检测完成:发现 ' . $invalidCount . ' 位校友不符合校友认定规则,以下用户将被自动取消校友身份。'); + $this->newLine(); + + $rows = []; + foreach ($invalidUsers as $user) { + $passedCourses = $user->courseSigns + ->map(function ($courseSign) use ($allowedCourseTypeNames, $allowedCourseNames, $today) { + if (empty($courseSign->course)) { + return '课程已删除'; + } + + $course = $courseSign->course; + $courseTypeName = $course->typeDetail->name ?? '空'; + $reasons = []; + + if ($course->auto_schoolmate == 1 && !empty($course->start_date) && $course->start_date <= $today) { + $reasons[] = '自动入校友'; + } + if (in_array($courseTypeName, $allowedCourseTypeNames, true)) { + $reasons[] = '体系白名单'; + } + if (in_array($course->name, $allowedCourseNames, true)) { + $reasons[] = '课程白名单'; + } + + return sprintf( + '%s[课程体系=%s,auto_schoolmate=%s,start_date=%s,命中规则=%s]', + $course->name, + $courseTypeName, + (string) $course->auto_schoolmate, + $course->start_date ?: '空', + empty($reasons) ? '无' : implode('+', $reasons) + ); + }) + ->filter() + ->unique() + ->values() + ->implode(';'); + + $rows[] = [ + '用户ID' => $user->id, + '姓名' => $user->name, + '手机号' => $user->mobile, + '成为校友时间' => $user->schoolmate_time ?: '空', + '已通过课程' => $passedCourses ?: '无', + '不符合原因' => '没有符合自动入校友规则的已通过课程', + ]; + } + + $this->table( + ['用户ID', '姓名', '手机号', '成为校友时间', '已通过课程', '不符合原因'], + $rows + ); + + $updatedCount = 0; + foreach ($invalidUsers as $user) { + $user->is_schoolmate = 0; + $user->schoolmate_time = null; + if ($user->isDirty()) { + $user->save(); + $updatedCount++; + } + } + + $this->newLine(); + $this->info("已自动取消 {$updatedCount} 位异常校友的校友身份。"); + $this->info('本次仅在命令行输出处理结果,不再生成 txt 文件。'); + + return self::SUCCESS; + } +} diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index b5727c3..40d314c 100755 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -711,7 +711,38 @@ class UserController extends BaseController */ public function import() { - return parent::import(); + $all = \request()->all(); + $messages = [ + 'data.required' => '数据必填', + ]; + $validator = Validator::make($all, [ + 'data' => 'required', + ], $messages); + if ($validator->fails()) { + return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); + } + + $records = $all['data']; + DB::beginTransaction(); + try { + $tableName = $this->model->getTable(); + $existingColumns = (new CustomFormField)->getRowTableFieldsByComment($tableName); + + $filteredRecords = array_map(function ($record) use ($existingColumns) { + return array_intersect_key($record, $existingColumns); + }, $records); + $filteredRecords = array_filter($filteredRecords); + + foreach ($filteredRecords as $record) { + $this->model->create($record); + } + + DB::commit(); + return $this->success(['total' => count($records), 'filter_total' => count($filteredRecords)]); + } catch (\Exception $exception) { + DB::rollBack(); + return $this->fail([$exception->getCode(), $exception->getMessage()]); + } } /**