|
|
<?php
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
use App\Models\Course;
|
|
|
use App\Models\CourseSign;
|
|
|
use App\Models\User;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
class SchoolmateEnrollmentService
|
|
|
{
|
|
|
/**
|
|
|
* 仅选择未删除、尚未成为校友且有有效审核通过报名的学员。
|
|
|
* 多门课程取最早开课日期;指定课程补齐不受自动入库开关限制。
|
|
|
*/
|
|
|
public function pendingUsers($courses)
|
|
|
{
|
|
|
return User::query()
|
|
|
->joinSub($this->eligibleSources($courses), 'schoolmate_sources', function ($join) {
|
|
|
$join->on('users.id', '=', 'schoolmate_sources.user_id');
|
|
|
})
|
|
|
->whereRaw('COALESCE(users.is_schoolmate, 0) != 1')
|
|
|
->select('users.*', 'schoolmate_sources.source_start_date');
|
|
|
}
|
|
|
|
|
|
private function eligibleSources($courses)
|
|
|
{
|
|
|
return CourseSign::query()
|
|
|
->join('courses', 'courses.id', '=', 'course_signs.course_id')
|
|
|
->whereNull('courses.deleted_at')
|
|
|
->whereIn('courses.id', (clone $courses)->select('courses.id'))
|
|
|
->where('course_signs.status', 1)
|
|
|
->select('course_signs.user_id')
|
|
|
->selectRaw('MIN(courses.start_date) as source_start_date')
|
|
|
->groupBy('course_signs.user_id');
|
|
|
}
|
|
|
|
|
|
public function ineligibleSchoolmates($courses)
|
|
|
{
|
|
|
return User::query()
|
|
|
->where('users.is_schoolmate', 1)
|
|
|
->whereNotIn('users.id', $this->eligibleSources($courses)
|
|
|
->select('course_signs.user_id')->whereNotNull('course_signs.user_id'));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 锁定用户后重新核验,避免按批次查询时取得的旧资格更新用户。
|
|
|
* 返回 1 表示入库,-1 表示取消,0 表示无需变更。
|
|
|
*/
|
|
|
public function reconcile(int $userId, $courses): int
|
|
|
{
|
|
|
return DB::transaction(function () use ($userId, $courses) {
|
|
|
$user = User::whereKey($userId)->lockForUpdate()->first();
|
|
|
if (!$user) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
$source = $this->eligibleSources($courses)
|
|
|
->where('course_signs.user_id', $userId)->first();
|
|
|
if ($source && $user->is_schoolmate != 1) {
|
|
|
return User::batchUpdateToSchoolmate([$userId], $source->source_start_date);
|
|
|
}
|
|
|
|
|
|
if (!$source && $user->is_schoolmate == 1) {
|
|
|
$user->is_schoolmate = 0;
|
|
|
$user->schoolmate_time = null;
|
|
|
return $user->save() ? -1 : 0;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
public function automaticCourses()
|
|
|
{
|
|
|
// 保留开课前一天入库的原行为,同时补查历史及开课后审核通过的学员。
|
|
|
return Course::where('auto_schoolmate', 1)
|
|
|
->whereNotNull('start_date')
|
|
|
->whereDate('start_date', '<=', now()->addDay()->toDateString());
|
|
|
}
|
|
|
|
|
|
public function promote(int $userId, string $startDate): int
|
|
|
{
|
|
|
return DB::transaction(function () use ($userId, $startDate) {
|
|
|
// 手工补齐和自动任务同时运行时,不覆盖已有校友的入库时间。
|
|
|
$user = User::whereKey($userId)->lockForUpdate()->first();
|
|
|
if (!$user || $user->is_schoolmate == 1) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
return User::batchUpdateToSchoolmate([$userId], $startDate);
|
|
|
});
|
|
|
}
|
|
|
}
|