master
cody 3 days ago
parent 29d3b949df
commit 2d2b176ee7

@ -0,0 +1,157 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class CheckMissingSchoolmateData extends Command
{
/**
* 允许直接认定为校友的课程体系名称
*
* @var string[]
*/
protected array $allowedCourseTypeNames = ['高研班', '攀峰班'];
/**
* 允许直接认定为校友的课程名称
*
* @var string[]
*/
protected array $allowedCourseNames = ['首期技术经理人领航班'];
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check_missing_schoolmate_data';
/**
* The console command description.
*
* @var string
*/
protected $description = '按当前校友认定口径,检查符合条件但未进入校友库的用户,并输出名单';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$today = date('Y-m-d');
$allowedCourseTypeNames = $this->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;
}
}

@ -0,0 +1,177 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class CheckSchoolmateData extends Command
{
/**
* 允许直接认定为校友的课程体系名称
*
* @var string[]
*/
protected array $allowedCourseTypeNames = ['高研班', '攀峰班'];
/**
* 允许直接认定为校友的课程名称
*
* @var string[]
*/
protected array $allowedCourseNames = ['首期技术经理人领航班'];
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check_schoolmate_data';
/**
* The console command description.
*
* @var string
*/
protected $description = '按当前校友认定口径校验所有校友数据,并自动修正不合格校友';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$today = date('Y-m-d');
$allowedCourseTypeNames = $this->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;
}
}

@ -711,7 +711,38 @@ class UserController extends BaseController
*/ */
public function import() 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()]);
}
} }
/** /**

Loading…
Cancel
Save