From 4981d6dbe69c762e7f05ec64ced2edbd12350d6d Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Mon, 12 Jan 2026 16:55:57 +0800 Subject: [PATCH] update --- app/Console/Commands/SendNotification.php | 26 +- app/Http/Controllers/Admin/UserController.php | 347 +++++++++++++++++- app/Models/BirthdayMessage.php | 22 ++ app/Models/User.php | 3 +- ..._154056_create_birthday_messages_table.php | 94 +++++ ..._163009_add_talent_tags_to_users_table.php | 32 ++ database/seeders/BirthdayMessageSeeder.php | 65 ++++ routes/api.php | 3 + 8 files changed, 580 insertions(+), 12 deletions(-) create mode 100644 app/Models/BirthdayMessage.php create mode 100644 database/migrations/2026_01_12_154056_create_birthday_messages_table.php create mode 100644 database/migrations/2026_01_12_163009_add_talent_tags_to_users_table.php create mode 100644 database/seeders/BirthdayMessageSeeder.php diff --git a/app/Console/Commands/SendNotification.php b/app/Console/Commands/SendNotification.php index 2f94613..1d91ed9 100755 --- a/app/Console/Commands/SendNotification.php +++ b/app/Console/Commands/SendNotification.php @@ -5,6 +5,7 @@ namespace App\Console\Commands; use App\Models\Appointment; use App\Models\AppointmentAccompany; use App\Models\AppointmentConfig; +use App\Models\BirthdayMessage; use App\Models\Config; use App\Models\Course; use App\Models\CourseSign; @@ -239,10 +240,29 @@ class SendNotification extends Command $this->smsNotice($vo, $content); break; case "App\Notifications\BirthdayNotify": - // 生日通知,todo::文案待定 + // 生日通知 $user = User::find($data['user_id']); - $url = $this->urlLink(); - $content = "{$smsSign}亲爱的同学:祝您生日快乐!登陆小程序有惊喜:{$url}"; + if (!$user) { + break; + } + + // 从数据表随机获取一条启用的生日祝福文案 + $messageTemplate = BirthdayMessage::getRandomMessage(); + + if ($messageTemplate) { + // 获取学员姓名,如果为空则使用"校友"作为备用 + $username = $user->username ?: '校友'; + // 替换{username}占位符 + $content = str_replace('{username}', $username, $messageTemplate); + // 添加短信签名 + $content = $smsSign . $content; + } else { + // 如果没有可用的文案,使用默认文案 + $url = $this->urlLink(); + $username = $user->username ?: '校友'; + $content = "{$smsSign}亲爱的{$username}校友,苏州科技商学院祝您生日快乐!打开STBC小程序,查看您的生日惊喜!"; + } + $this->smsNotice($vo, $content); break; } diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index c0a05b1..1f121f8 100755 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -178,6 +178,8 @@ class UserController extends BaseController * @OA\Parameter(name="end_company_date", in="query", @OA\Schema(type="string"), required=true, description="结束成立日期"), * @OA\Parameter(name="start_birthday", in="query", @OA\Schema(type="string"), required=true, description="开始出生日期"), * @OA\Parameter(name="end_birthday", in="query", @OA\Schema(type="string"), required=true, description="结束出生日期"), + * @OA\Parameter(name="sign_start_date", in="query", @OA\Schema(type="string"), required=false, description="报名开始时间"), + * @OA\Parameter(name="sign_end_date", in="query", @OA\Schema(type="string"), required=false, description="报名结束时间"), * @OA\Parameter(name="company_need_fund", in="query", @OA\Schema(type="string"), required=true, description="是否需要融资"), * @OA\Parameter(name="is_fee", in="query", @OA\Schema(type="string"), required=true, description="是否缴费0否1是"), * @OA\Parameter(name="has_openid", in="query", @OA\Schema(type="string"), required=true, description="是否绑定小程序0否1是"), @@ -211,10 +213,10 @@ class UserController extends BaseController $query->with('course.typeDetail')->orderBy('fee_status', 'desc'); } ])->withCount([ - 'appointments' => function ($query) { - $query->whereIn('status', [0, 1]); - } - ]); + 'appointments' => function ($query) { + $query->whereIn('status', [0, 1]); + } + ]); // 是否被投企业 if (isset($all['is_yh_invested'])) { $list = $list->whereHas('company', function ($query) use ($all) { @@ -253,6 +255,14 @@ class UserController extends BaseController if (isset($all['status'])) { $query->where('status', $all['status']); } + // 报名时间筛选 + if (isset($all['sign_start_date']) && isset($all['sign_end_date'])) { + $query->whereBetween('created_at', [$all['sign_start_date'], $all['sign_end_date']]); + } elseif (isset($all['sign_start_date'])) { + $query->where('created_at', '>=', $all['sign_start_date']); + } elseif (isset($all['sign_end_date'])) { + $query->where('created_at', '<=', $all['sign_end_date']); + } $query->whereHas('course', function ($q) use ($all) { if (isset($all['year'])) { $q->where('year', $all['year']); @@ -542,7 +552,8 @@ class UserController extends BaseController } else { if (in_array($k, ['company_type', 'type'])) { $list[$key][$k] = str_replace('、', ',', $value[$v]); - $list[$key][$k] = str_replace(',', ',', $list[$key][$k]);; + $list[$key][$k] = str_replace(',', ',', $list[$key][$k]); + ; } else { $list[$key][$k] = $value[$v]; } @@ -693,11 +704,331 @@ class UserController extends BaseController if (isset($all['is_schoolmate'])) { $data['is_schoolmate'] = $all['is_schoolmate']; } - // if (isset($all['is_black'])) { -// $data['is_black'] = $all['is_black']; -// } $this->model->whereIn('id', $idsArray)->update($data); return $this->success('批量更新成功'); } + /** + * @OA\Post( + * path="/api/admin/users/batch-update", + * tags={"用户信息"}, + * summary="批量更新用户信息", + * description="", + * @OA\Parameter(name="ids", in="query", @OA\Schema(type="string"), required=true, description="英文逗号分隔的用户id"), + * @OA\Parameter(name="data", in="query", @OA\Schema(type="object"), required=true, description="需要更新的字段对象,键为字段名,值为字段值,例如:{\"is_vip\":\"1\",\"is_schoolmate\":\"1\",\"talent_tags\":\"标签1,标签2\"}"), + * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), + * @OA\Response( + * response="200", + * description="暂无" + * ) + * ) + */ + public function batchUpdate() + { + $all = \request()->all(); + $messages = [ + 'ids.required' => '用户ID必填', + 'data.required' => '更新数据必填', + ]; + $validator = Validator::make($all, [ + 'ids' => 'required', + 'data' => 'required|array', + ], $messages); + + if ($validator->fails()) { + return $this->fail([StarterResponseCode::START_ERROR_PARAMETER, implode(',', $validator->errors()->all())]); + } + + // 获取可更新的字段列表(fillable字段) + $fillableFields = $this->model->getFillable(); + + // 构建更新数据,只保留fillable中的字段 + $data = []; + foreach ($all['data'] as $field => $value) { + // 只允许更新fillable中的字段 + if (in_array($field, $fillableFields)) { + $data[$field] = $value; + } + } + + if (empty($data)) { + return $this->fail([StarterResponseCode::START_ERROR_PARAMETER, '没有可更新的有效字段']); + } + + // 解析用户ID + $idsArray = explode(',', $all['ids']); + $idsArray = array_filter(array_map('trim', $idsArray)); + + if (empty($idsArray)) { + return $this->fail([StarterResponseCode::START_ERROR_PARAMETER, '用户ID不能为空']); + } + + DB::beginTransaction(); + try { + $this->model->whereIn('id', $idsArray)->update($data); + DB::commit(); + return $this->success('批量更新成功,共更新 ' . count($idsArray) . ' 条记录'); + } catch (\Exception $exception) { + DB::rollBack(); + return $this->fail([$exception->getCode(), $exception->getMessage()]); + } + } + + /** + * @OA\Post( + * path="/api/admin/users/excel-show-special", + * tags={"用户信息"}, + * summary="特殊导入规则预览(通过姓名、公司、职位匹配,仅更新不创建)", + * description="通过姓名、公司名称、职位三个字段匹配现有用户,未匹配到的用户会在返回结果中提示", + * @OA\Parameter(name="file", in="query", @OA\Schema(type="string"), required=true, description="文件"), + * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), + * @OA\Response( + * response="200", + * description="返回匹配结果,包含未匹配用户列表" + * ) + * ) + */ + public function excelShowSpecial() + { + $file = \request()->file('file'); + //判断文件是否有效 + if (!(\request()->hasFile('file') && $file->isValid())) { + return $this->fail([ResponseCode::ERROR_BUSINESS, '文件不存在或无效']); + } + //获取文件大小 + $img_size = floor($file->getSize() / 1024); + if ($img_size >= 50 * 1024) { + return $this->fail([ResponseCode::ERROR_BUSINESS, '文件必须小于50M']); + } + //过滤文件后缀 + $ext = $file->getClientOriginalExtension(); + if (!in_array($ext, ['xls', 'xlsx', 'csv'])) { + return $this->fail([ResponseCode::ERROR_BUSINESS, '仅支持xls/xlsx/csv格式']); + } + $tempFile = $file->getRealPath(); + $dataArray = (new FastExcel)->import($tempFile)->toArray(); + + // 数据过滤,只能导入数据表有的字段 + $tableName = $this->model->getTable(); + $rowTableFieldByComment = (new CustomFormField)->getRowTableFieldsByComment($tableName); + + $list = []; + $unmatchedUsers = []; // 记录未匹配到的用户 + foreach ($dataArray as $key => $value) { + // 获取姓名、公司名称、职位 + $name = $value['姓名'] ?? $value['name'] ?? ''; + $companyName = $value['公司名称'] ?? $value['company_name'] ?? ''; + $companyPosition = $value['职位'] ?? $value['company_position'] ?? ''; + + // 通过姓名、公司名称、职位匹配用户 + $matchedUser = null; + if (!empty($name) && !empty($companyName) && !empty($companyPosition)) { + $matchedUser = $this->model->where('username', $name) + ->where('company_name', $companyName) + ->where('company_position', $companyPosition) + ->first(); + } + + // 记录未匹配到的用户 + if (!$matchedUser) { + $unmatchedUsers[] = [ + 'row' => $key + 1, // Excel行号(从1开始) + 'name' => $name, + 'company_name' => $companyName, + 'company_position' => $companyPosition, + ]; + } + + // 构建返回数据(使用手机号作为唯一标识) + $list[$key] = [ + 'name' => $name, + 'company_name' => $companyName, + 'company_position' => $companyPosition, + 'matched' => $matchedUser ? true : false, + 'mobile' => $matchedUser ? $matchedUser->mobile : null, // 使用手机号作为唯一标识 + 'existing_data' => $matchedUser ? [ + 'id' => $matchedUser->id, + 'username' => $matchedUser->username, + 'name' => $matchedUser->name, + 'mobile' => $matchedUser->mobile, + 'company_name' => $matchedUser->company_name, + 'company_position' => $matchedUser->company_position, + ] : null, + ]; + + // 处理其他字段 + foreach ($rowTableFieldByComment as $k => $v) { + if (isset($value[$v])) { + // 日期格式 + if ($value[$v] instanceof \DateTimeImmutable) { + $list[$key][$k] = Carbon::parse($value[$v])->toDateString(); + } else { + if (in_array($k, ['company_type', 'type'])) { + $list[$key][$k] = str_replace('、', ',', $value[$v]); + $list[$key][$k] = str_replace(',', ',', $list[$key][$k]); + } else { + $list[$key][$k] = $value[$v]; + } + } + } + } + } + + // 构建返回结果 + $result = [ + 'list' => $list, + 'matched_count' => count($list) - count($unmatchedUsers), + 'unmatched_count' => count($unmatchedUsers), + 'unmatched_users' => $unmatchedUsers, + ]; + + // 如果有未匹配到的用户,添加提示信息 + if (count($unmatchedUsers) > 0) { + $unmatchedNames = array_map(function ($user) { + return "第{$user['row']}行:{$user['name']}({$user['company_name']} - {$user['company_position']})"; + }, $unmatchedUsers); + $result['message'] = '以下用户未匹配到,请检查数据:' . implode(';', $unmatchedNames); + } + + return $this->success($result); + } + + /** + * @OA\Post( + * path="/api/admin/users/import-special", + * tags={"用户信息"}, + * summary="特殊导入规则导入(通过姓名、公司、职位匹配,仅更新不创建)", + * description="仅更新已匹配到的用户,如果存在未匹配到的用户,将返回错误并回滚事务", + * @OA\Parameter(name="data", in="query", @OA\Schema(type="string"), required=true, description="导入分析获取到的二维数组"), + * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), + * @OA\Response( + * response="200", + * description="返回更新结果" + * ) + * ) + */ + public function importSpecial() + { + $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())]); + } + $filteredRecords = $all['data']; + $suc = 0; + $updateCount = 0; + $unmatchedUsers = []; // 记录未匹配到的用户 + + DB::beginTransaction(); + try { + foreach ($filteredRecords as $index => $record) { + // 获取匹配的手机号(如果预览时已匹配到) + $mobile = $record['mobile'] ?? null; + + // 如果没有mobile,尝试通过姓名、公司、职位再次匹配 + if (!$mobile) { + $name = $record['name'] ?? ''; + $companyName = $record['company_name'] ?? ''; + $companyPosition = $record['company_position'] ?? ''; + + if (!empty($name) && !empty($companyName) && !empty($companyPosition)) { + $matchedUser = $this->model->where('username', $name) + ->where('company_name', $companyName) + ->where('company_position', $companyPosition) + ->first(); + if ($matchedUser) { + $mobile = $matchedUser->mobile; + } + } + } + + // 如果仍未匹配到手机号,记录并跳过 + if (!$mobile) { + $unmatchedUsers[] = [ + 'row' => $index + 1, + 'name' => $record['name'] ?? '', + 'company_name' => $record['company_name'] ?? '', + 'company_position' => $record['company_position'] ?? '', + ]; + continue; + } + + // 去除匹配相关的字段,避免更新到数据库 + unset($record['matched'], $record['existing_data']); + + // 去除空值 + $record = array_filter($record, function ($value) { + return $value != ''; + }); + + // 通过手机号查找并更新用户 + $user = $this->model->where('mobile', $mobile)->first(); + if ($user) { + // 设置username(如果name字段存在) + if (isset($record['name'])) { + $record['username'] = $record['name']; + } + + // 所有数据通过追加的形式更新(参考importStudy的逻辑) + foreach ($record as $k => &$v) { + if (!in_array($k, User::$coverFields)) { + // 追加更新 + $tempArray = explode(',', ($user->$k ?? '') . ',' . $v); + $tempArray = array_unique(array_filter($tempArray)); + $v = implode(',', $tempArray); + } + } + $user->fill($record); + $user->save(); + $updateCount++; + $suc++; + + // 写入报名表(如果有课程信息) + if (isset($record['course_id']) && !empty($record['course_id'])) { + $whereSign = ['course_id' => $record['course_id'], 'user_id' => $user->id]; + $dataSign = [ + 'course_id' => $record['course_id'], + 'user_id' => $user->id, + 'is_import' => 1, + 'status' => $record['status'] ?? 1, + 'fee_status' => $record['fee_status'] ?? 0 + ]; + $courseSign = CourseSign::updateOrCreate($whereSign, $dataSign); + // 加导入次数,加预约次数 + if ($courseSign->status == 1) { + CourseAppointmentTotal::add($courseSign->user_id, $courseSign->id); + } + } + } + } + + // 如果有未匹配到的用户,返回错误 + if (count($unmatchedUsers) > 0) { + DB::rollBack(); + $unmatchedNames = array_map(function ($user) { + return "第{$user['row']}行:{$user['name']}({$user['company_name']} - {$user['company_position']})"; + }, $unmatchedUsers); + return $this->fail([ + ResponseCode::ERROR_BUSINESS, + '以下用户未匹配到,无法更新:' . implode(';', $unmatchedNames) + ]); + } + + DB::commit(); + return $this->success([ + 'total' => count($filteredRecords), + 'suc' => $suc, + 'update_count' => $updateCount + ]); + } catch (\Exception $exception) { + DB::rollBack(); + return $this->fail([$exception->getCode(), $exception->getMessage()]); + } + } + } diff --git a/app/Models/BirthdayMessage.php b/app/Models/BirthdayMessage.php new file mode 100644 index 0000000..84e921d --- /dev/null +++ b/app/Models/BirthdayMessage.php @@ -0,0 +1,22 @@ +inRandomOrder() + ->first(); + + return $message ? $message->content : null; + } +} + diff --git a/app/Models/User.php b/app/Models/User.php index 477613b..d2b7590 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -74,7 +74,8 @@ class User extends Authenticatable implements Auditable 'deleted_at', 'no', 'from', - 'open_course_types' + 'open_course_types', + 'talent_tags' ]; protected $appends = ['is_vip_text', 'is_schoolmate_text', 'appointment_total', 'name']; diff --git a/database/migrations/2026_01_12_154056_create_birthday_messages_table.php b/database/migrations/2026_01_12_154056_create_birthday_messages_table.php new file mode 100644 index 0000000..5cd18f2 --- /dev/null +++ b/database/migrations/2026_01_12_154056_create_birthday_messages_table.php @@ -0,0 +1,94 @@ +id(); + // 短信内容 + $table->text('content')->nullable()->comment('短信内容,包含{username}占位符'); + // 状态:0禁用,1启用 + $table->tinyInteger('status')->default(1)->comment('状态:0禁用,1启用'); + // 排序 + $table->integer('sort')->default(0)->comment('排序'); + $table->timestamps(); + $table->softDeletes(); + }); + + // 插入初始7条文案数据 + $messages = [ + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您的科创征途繁花似锦!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 1, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您的科创蓝图,皆能落地生花!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 2, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您所行皆坦途!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 3, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您科创破局,事业长红!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 4, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您前路商道开阔,所遇皆为坦途,岁岁年年皆得圆满!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 5, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您执掌事业乾坤,亦拥生活繁花似锦!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 6, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您基业长青,生活常愉,岁岁皆有新光景!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 7, + 'created_at' => now(), + 'updated_at' => now(), + ], + ]; + + DB::table('birthday_messages')->insert($messages); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('birthday_messages'); + } +}; + diff --git a/database/migrations/2026_01_12_163009_add_talent_tags_to_users_table.php b/database/migrations/2026_01_12_163009_add_talent_tags_to_users_table.php new file mode 100644 index 0000000..4fbf9eb --- /dev/null +++ b/database/migrations/2026_01_12_163009_add_talent_tags_to_users_table.php @@ -0,0 +1,32 @@ +string('talent_tags')->nullable()->comment('人才标签,多个值用英文逗号分隔'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('talent_tags'); + }); + } +}; + diff --git a/database/seeders/BirthdayMessageSeeder.php b/database/seeders/BirthdayMessageSeeder.php new file mode 100644 index 0000000..cfe73c0 --- /dev/null +++ b/database/seeders/BirthdayMessageSeeder.php @@ -0,0 +1,65 @@ + '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您的科创征途繁花似锦!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 1, + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您的科创蓝图,皆能落地生花!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 2, + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您所行皆坦途!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 3, + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您科创破局,事业长红!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 4, + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您前路商道开阔,所遇皆为坦途,岁岁年年皆得圆满!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 5, + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您执掌事业乾坤,亦拥生活繁花似锦!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 6, + ], + [ + 'content' => '亲爱的{username}校友,苏州科技商学院祝您生日快乐!愿您基业长青,生活常愉,岁岁皆有新光景!打开STBC小程序,查看您的生日惊喜!', + 'status' => 1, + 'sort' => 7, + ], + ]; + + foreach ($messages as $message) { + BirthdayMessage::updateOrCreate( + ['sort' => $message['sort']], + $message + ); + } + + $this->command->info('已生成 ' . count($messages) . ' 条生日祝福短信文案数据'); + } +} + diff --git a/routes/api.php b/routes/api.php index b8bf19f..b989792 100755 --- a/routes/api.php +++ b/routes/api.php @@ -84,9 +84,12 @@ Route::group(["namespace" => "Admin", "prefix" => "admin"], function () { Route::post('users/save', [\App\Http\Controllers\Admin\UserController::class, "save"]); Route::get('users/destroy', [\App\Http\Controllers\Admin\UserController::class, "destroy"]); Route::post('users/excel-show', [\App\Http\Controllers\Admin\UserController::class, "excelShow"]); + Route::post('users/excel-show-special', [\App\Http\Controllers\Admin\UserController::class, "excelShowSpecial"]); Route::post('users/import', [\App\Http\Controllers\Admin\UserController::class, "import"]); Route::post('users/import-study', [\App\Http\Controllers\Admin\UserController::class, "importStudy"]); + Route::post('users/import-special', [\App\Http\Controllers\Admin\UserController::class, "importSpecial"]); Route::post('users/batch-update-schoolmate', [\App\Http\Controllers\Admin\UserController::class, "batchUpdateSchoolmate"]); + Route::post('users/batch-update', [\App\Http\Controllers\Admin\UserController::class, "batchUpdate"]); // 老师管理 Route::get('teachers/index', [\App\Http\Controllers\Admin\TeacherController::class, "index"]);