diff --git a/app/Console/Commands/UpdateCompany.php b/app/Console/Commands/UpdateCompany.php index 2b2dcac..4b5be0c 100755 --- a/app/Console/Commands/UpdateCompany.php +++ b/app/Console/Commands/UpdateCompany.php @@ -43,8 +43,8 @@ class UpdateCompany extends Command public function handle() { $user_id = $this->option('user_id'); - $updateLocal = (int) $this->option('address'); - $updateMarket = (int) $this->option('market'); + $updateLocal = (int)$this->option('address'); + $updateMarket = (int)$this->option('market'); // 更新公司信息 $this->compnay($user_id); // 更新经纬度信息(可选) @@ -72,7 +72,9 @@ class UpdateCompany extends Command // 批量更新(只更新有报名审核通过的用户) $users = User::whereHas('courseSigns', function ($query) { $query->where('status', 1); - })->whereNotNull('company_name')->get(); + })->whereNotNull('company_name') + ->whereNull('company_id') + ->get(); } $total = $users->count(); diff --git a/app/Console/Commands/UpdateUserFromCourseSign.php b/app/Console/Commands/UpdateUserFromCourseSign.php new file mode 100644 index 0000000..2398bde --- /dev/null +++ b/app/Console/Commands/UpdateUserFromCourseSign.php @@ -0,0 +1,247 @@ +allowedMobiles)->get(); + + if ($users->isEmpty()) { + return $this->error('没有找到符合条件的用户'); + } + + $this->info("找到 {$users->count()} 个用户需要处理"); + + $successCount = 0; + $failCount = 0; + + foreach ($users as $user) { + $this->newLine(); + $this->info("========== 处理用户:{$user->name} (手机号: {$user->mobile}, ID: {$user->id}) =========="); + + try { + $this->processUser($user); + $successCount++; + } catch (\Exception $e) { + $this->error("处理用户 {$user->name} 时出错:" . $e->getMessage()); + $failCount++; + } + } + + $this->newLine(); + return $this->info("全部处理完成!成功:{$successCount},失败:{$failCount}"); + } + + /** + * 处理单个用户 + */ + private function processUser($user) + { + $user_id = $user->id; + + // 获取该用户的所有报名记录(携带课程及课程类型) + $courseSigns = CourseSign::with('course.typeDetail') + ->where('user_id', $user_id) + ->whereNotNull('data') + ->get(); + + if ($courseSigns->isEmpty()) { + $this->info('该用户没有报名记录或报名记录中没有数据'); + return; + } + + $this->info("找到 {$courseSigns->count()} 条报名记录"); + + // 如果用户报名了多个课程,并且其中包含课程类型是「高研班」或者「攀峰班」的课程 + // 则优先使用这些课程的报名信息更新用户 + $targetCourseSigns = $courseSigns; + if ($courseSigns->count() > 1) { + $specialCourseSigns = $courseSigns->filter(function ($sign) { + $typeName = $sign->course->typeDetail->name ?? ''; + return in_array($typeName, ['高研班', '攀峰班']); + }); + if ($specialCourseSigns->isNotEmpty()) { + $this->info('检测到高研班/攀峰班课程报名记录,将优先使用这些课程的报名信息更新用户'); + $targetCourseSigns = $specialCourseSigns; + } + } + + // 收集所有需要更新的用户字段 + $userData = []; + $hasCompanyName = false; + + foreach ($targetCourseSigns as $courseSign) { + if (empty($courseSign->data) || !is_array($courseSign->data)) { + continue; + } + + // 获取该课程的表单字段配置,建立 field -> belong_user_table 的映射关系 + $courseForms = CourseForm::where('course_id', $courseSign->course_id) + ->where('belong_user', 1) // 只获取属于用户信息的字段 + ->whereNotNull('belong_user_table') // 必须有对应的用户表字段 + ->where('belong_user_table', '!=', '') // 用户表字段不能为空 + ->get(['field', 'belong_user_table']); + + if ($courseForms->isEmpty()) { + continue; + } + + // 建立 field -> belong_user_table 的映射关系 + $fieldMapping = []; + foreach ($courseForms as $form) { + $fieldMapping[$form->field] = $form->belong_user_table; + } + + // 将 data 数组转换为以 field 为 key 的关联数组 + $dataArray = []; + foreach ($courseSign->data as $item) { + if (isset($item['field']) && isset($item['value'])) { + $dataArray[$item['field']] = $item['value']; + } + } + + // 提取属于用户信息的字段,使用 belong_user_table 作为用户表的字段名 + foreach ($fieldMapping as $field => $userTableField) { + if (isset($dataArray[$field]) && $dataArray[$field] !== null && $dataArray[$field] !== '') { + // 如果字段已经在 $userData 中,且新值不为空,则更新(优先使用非空值) + if (!isset($userData[$userTableField]) || empty($userData[$userTableField])) { + $userData[$userTableField] = $dataArray[$field]; + } + + // 检查是否是公司名字 + if ($userTableField === 'company_name') { + $hasCompanyName = true; + } + } + } + } + + if (empty($userData)) { + $this->info('没有找到需要更新的用户信息'); + return; + } + + $this->info('准备更新以下字段:' . implode(', ', array_keys($userData))); + + // 更新用户信息 + // 根据 User::$coverFields 判断是否需要覆盖更新 + foreach ($userData as $key => $value) { + if (!in_array($key, User::$coverFields)) { + // 追加更新(对于非覆盖字段) + $currentValue = $user->$key ?? ''; + if (!empty($currentValue)) { + $tempArray = explode(',', $currentValue . ',' . $value); + $tempArray = array_unique(array_filter($tempArray)); + $userData[$key] = implode(',', $tempArray); + } + } + } + + // 检查公司名字是否发生变化 + $oldCompanyName = $user->company_name; + $newCompanyName = $userData['company_name'] ?? null; + // 如果公司名字从无到有,或者发生变化,都需要更新 + $companyNameChanged = $hasCompanyName && isset($newCompanyName) && + (empty($oldCompanyName) || $oldCompanyName != $newCompanyName); + + $user->fill($userData); + $user->save(); + + $this->info('用户信息更新成功'); + + // 如果公司名字发生变化或新增,调用 UpdateCompany 脚本 + if ($companyNameChanged) { + if (empty($oldCompanyName)) { + $this->info("检测到新增公司名字({$newCompanyName}),开始调用 UpdateCompany 脚本更新公司信息..."); + } else { + $this->info("检测到公司名字发生变化({$oldCompanyName} -> {$newCompanyName}),开始调用 UpdateCompany 脚本更新公司信息..."); + } + Artisan::call("update_company --user_id={$user_id}"); + $this->info('公司信息更新完成'); + } elseif ($hasCompanyName && isset($newCompanyName)) { + $this->info("公司名字未发生变化({$newCompanyName}),跳过公司信息更新"); + } + + $this->info('用户更新完成'); + } +} + diff --git a/app/Console/Commands/UpdateUserNo.php b/app/Console/Commands/UpdateUserNo.php index 4e56e31..4984525 100755 --- a/app/Console/Commands/UpdateUserNo.php +++ b/app/Console/Commands/UpdateUserNo.php @@ -104,7 +104,7 @@ class UpdateUserNo extends Command $tag = '元禾同事'; // 获取元和员工用户列表 - $users = CourseSign::companyJoin(null, null, null, true); + $users = CourseSign::companyJoin(null, null, null, true, false); $count = 0; foreach ($users as $user) { diff --git a/app/Exports/CommonExport.php b/app/Exports/CommonExport.php index 053dd22..b7f9104 100755 --- a/app/Exports/CommonExport.php +++ b/app/Exports/CommonExport.php @@ -396,10 +396,18 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With $header = array_values($this->fields); $moreFileds = []; if (empty($clear)) { - if (isset($this->data[0]['data']) && is_array($this->data[0]['data'])) { - $moreHeader = array_column($this->data[0]['data'], 'name'); + // 容错,取数据不是空的数组 + foreach ($this->data as $value) { + if (!empty($value['data'])) { + $otherData = $value['data']; + break; + } + } + + if (isset($otherData) && is_array($otherData)) { + $moreHeader = array_column($otherData, 'name'); $header = array_merge($header, $moreHeader); - $moreFileds = array_column($this->data[0]['data'], 'field'); + $moreFileds = array_column($otherData, 'field'); } } $newList[] = $header; diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index dfef33b..2c3738a 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -122,6 +122,11 @@ class OtherController extends CommonController */ public function homeV2() { + // 默认开始时间 + $start_date = CourseType::START_DATE; + // 默认结束日期一年以后 + $end_date = date('Y-m-d', strtotime('+10 year')); + // 校友总数 $list['schoolmate_total'] = User::where('is_schoolmate', 1)->count(); // 今年新增校友数 @@ -140,12 +145,16 @@ class OtherController extends CommonController $list['cover_rencai_total'] = CourseSign::rencai(); // 重点上市公司 $list['cover_stock_total'] = CourseSign::shangshi(); + // 培养人次1 + $start_date = CourseType::START_DATE; + // 默认结束日期一年以后 + $end_date = date('Y-m-d', strtotime('+10 year')); // 培养人次 - $list['course_signs_pass'] = CourseSign::courseSignsTotal(null, null, 1); + $list['course_signs_pass'] = CourseSign::courseSignsTotal($start_date, $end_date, 1); // 培养人数 - $list['course_signs_pass_unique'] = CourseSign::courseSignsTotalByUnique(null, null, 1, null, null); + $list['course_signs_pass_unique'] = CourseSign::courseSignsTotalByUnique($start_date, $end_date, 1, null, null); // 跟班学员数 - $list['genban_total'] = CourseSign::genban(); + $list['genban_total'] = CourseSign::genban($start_date, $end_date); // 本月课程 $monthCourses = Calendar::with('course.teacher') ->where('start_time', 'like', '%' . date('Y-m') . '%') @@ -154,10 +163,7 @@ class OtherController extends CommonController $courseTypes = CourseType::where('is_chart', 1) ->orderBy('sort', 'asc') ->where('is_history', 0)->get(); - // 默认开始时间 - $start_date = CourseType::START_DATE; - // 默认结束日期一年以后 - $end_date = date('Y-m-d', strtotime('+10 year')); + foreach ($courseTypes as $courseType) { // 历史已开设期数 @@ -284,7 +290,7 @@ class OtherController extends CommonController $list['company_invested_year_total'] = CourseSign::companyInvestedYear($start_date, $end_date, $course_ids); // 元和员工参与人数 - $list['company_join_total'] = CourseSign::companyJoin($start_date, $end_date, $course_ids); + $list['company_join_total'] = CourseSign::companyJoin($start_date, $end_date, $course_ids, false, true); // 全市干部参与企业 $list['company_ganbu_total'] = CourseSign::ganbu($start_date, $end_date, $course_ids); // 苏州头部企业 @@ -322,7 +328,7 @@ class OtherController extends CommonController // 被投企业数 'yh_invested_total' => CourseSign::yhInvested($start_date, $end_date, [$course->id]), // 元禾同事数 - 'company_join_total' => CourseSign::companyJoin($start_date, $end_date, [$course->id]), + 'company_join_total' => CourseSign::companyJoin($start_date, $end_date, [$course->id], false, false, false), ]; } } @@ -551,7 +557,7 @@ class OtherController extends CommonController 'course_signs_pass' => CourseSign::courseSignsTotal($start_date, $end_date, 1, [$course->id], false, false), 'genban_total' => CourseSign::genban($start_date, $end_date, [$course->id]), 'yh_invested_total' => CourseSign::yhInvested($start_date, $end_date, [$course->id]), - 'company_join_total' => CourseSign::companyJoin($start_date, $end_date, [$course->id]), + 'company_join_total' => CourseSign::companyJoin($start_date, $end_date, [$course->id], false, false), ]; } } @@ -1039,7 +1045,7 @@ class OtherController extends CommonController case 'company_join_total': // 元和员工参与人员明细 - 使用模型方法(现在返回的是用户列表) - $users = CourseSign::companyJoin($start_date, $end_date, $course_ids, true); + $users = CourseSign::companyJoin($start_date, $end_date, $course_ids, true, false); // 加载关联关系 $users->load('company'); foreach ($users as $user) { diff --git a/app/Http/Controllers/Mobile/CourseController.php b/app/Http/Controllers/Mobile/CourseController.php index 3694059..798be57 100755 --- a/app/Http/Controllers/Mobile/CourseController.php +++ b/app/Http/Controllers/Mobile/CourseController.php @@ -16,6 +16,7 @@ use App\Models\CourseContent; use App\Models\CourseContentCheck; use App\Models\CourseContentEvaluation; use App\Models\CourseContentEvaluationForm; +use App\Models\CourseForm; use App\Models\CourseSign; use App\Models\CourseType; use App\Models\Notice; @@ -110,13 +111,23 @@ class CourseController extends CommonController if ($validator->fails()) { return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); } - $detail = Course::with(['qunImage', 'typeDetail', 'courseForms', 'teacher', 'courseContentEvaluation' => function ($query) { - $query->with(['courseContentEvaluationAsks' => function ($q) { - $q->orderBy('sort', 'asc'); - }]); - }])->withCount(['courseSigns as my_user' => function ($query) { - $query->where('user_id', $this->getUserId()); - }])->find($all['course_id']); + $detail = Course::with([ + 'qunImage', + 'typeDetail', + 'courseForms', + 'teacher', + 'courseContentEvaluation' => function ($query) { + $query->with([ + 'courseContentEvaluationAsks' => function ($q) { + $q->orderBy('sort', 'asc'); + } + ]); + } + ])->withCount([ + 'courseSigns as my_user' => function ($query) { + $query->where('user_id', $this->getUserId()); + } + ])->find($all['course_id']); return $this->success($detail); } @@ -144,9 +155,11 @@ class CourseController extends CommonController if ($validator->fails()) { return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); } - $detail = CourseContentEvaluation::with(['courseContentEvaluationAsks' => function ($q) { - $q->with('courseContent.teacher')->orderBy('sort', 'asc'); - }])->find($all['course_content_evaluation_id']); + $detail = CourseContentEvaluation::with([ + 'courseContentEvaluationAsks' => function ($q) { + $q->with('courseContent.teacher')->orderBy('sort', 'asc'); + } + ])->find($all['course_content_evaluation_id']); return $this->success($detail); } @@ -199,9 +212,11 @@ class CourseController extends CommonController $all = \request()->all(); $messages = [ 'course_id.required' => '课程id必填', + 'data.required' => '表单必填', ]; $validator = Validator::make($all, [ - 'course_id' => 'required' + 'course_id' => 'required', + 'data' => 'required' ], $messages); if ($validator->fails()) { return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); @@ -224,6 +239,33 @@ class CourseController extends CommonController return $this->fail([ResponseCode::ERROR_BUSINESS, '报名已结束']); } } + // 检测必填字段 + $requiredFields = CourseForm::where('course_id', $all['course_id']) + ->where('rule', 'like', '%required%') + ->get(['field', 'name']); + if ($requiredFields->count() > 0) { + // 将 data 数组转换为以 field 为 key 的关联数组 + $dataArray = []; + if (isset($all['data']) && is_array($all['data'])) { + foreach ($all['data'] as $item) { + if (isset($item['field'])) { + $dataArray[$item['field']] = $item['value'] ?? null; + } + } + } + // 检查必填字段 + $missingFields = []; + foreach ($requiredFields as $field) { + $fieldValue = $dataArray[$field->field] ?? null; + // 检查字段是否存在且值不为空(null、空字符串视为空,0是有效值) + if ($fieldValue === null || $fieldValue === '') { + $missingFields[] = $field->name; + } + } + if (!empty($missingFields)) { + return $this->fail([ResponseCode::ERROR_PARAMETER, '以下字段为必填项:' . implode('、', $missingFields)]); + } + } $result = CourseSign::create([ 'is_change' => $all['is_change'] ?? 0, 'course_id' => $all['course_id'], @@ -258,9 +300,11 @@ class CourseController extends CommonController */ public function myCourse() { - $list = Course::with('typeDetail', 'courseContentEvaluation')->with(['courseSigns' => function ($query) { - $query->where('user_id', $this->getUserId()); - }])->whereHas('courseSigns', function ($query) { + $list = Course::with('typeDetail', 'courseContentEvaluation')->with([ + 'courseSigns' => function ($query) { + $query->where('user_id', $this->getUserId()); + } + ])->whereHas('courseSigns', function ($query) { $query->where('user_id', $this->getUserId()); })->where('is_virtual', 0)->orderBy('id', 'desc')->paginate($all['page_size'] ?? 20); return $this->success(compact('list')); @@ -325,11 +369,18 @@ class CourseController extends CommonController if ($validator->fails()) { return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); } - $detail = CourseContent::with(['course', 'teacher', 'courseKeeps', 'courseContentEvaluation' => function ($query) { - $query->with(['courseContentEvaluationAsks' => function ($q) { - $q->orderBy('sort'); - }]); - }])->find($all['course_content_id']); + $detail = CourseContent::with([ + 'course', + 'teacher', + 'courseKeeps', + 'courseContentEvaluation' => function ($query) { + $query->with([ + 'courseContentEvaluationAsks' => function ($q) { + $q->orderBy('sort'); + } + ]); + } + ])->find($all['course_content_id']); return $this->success($detail); } @@ -569,7 +620,7 @@ class CourseController extends CommonController // 获取打卡范围,千米 // $content_check_range = Config::getValueByKey('content_check_range'); $courseContent = CourseContent::find($all['course_content_id']); -// $distance = getDistance($courseContent->longitude, $courseContent->latitude, $all['longitude'], $all['latitude']); + // $distance = getDistance($courseContent->longitude, $courseContent->latitude, $all['longitude'], $all['latitude']); // if ($distance > $content_check_range) { // return $this->fail([ResponseCode::ERROR_BUSINESS, '超出打卡范围']); // } @@ -654,7 +705,7 @@ class CourseController extends CommonController // 获取打卡范围,千米 $content_check_range = Config::getValueByKey('content_check_range'); $course = Course::find($all['course_id']); -// if (empty($course->longitude) || empty($course->latitude)) { + // if (empty($course->longitude) || empty($course->latitude)) { // return $this->fail([ResponseCode::ERROR_BUSINESS, '请先设置课程经纬度']); // } // $distance = getDistance($course->longitude, $course->latitude, $all['longitude'], $all['latitude']); @@ -751,15 +802,17 @@ class CourseController extends CommonController if ($all['type'] == 2) { $query->where('status', 1); } - })->with(['courseSigns' => function ($query) use ($all) { - $query->where('status', 1)->whereHas('course', function ($q) { - $q->where('is_fee', 1); - })->with('course.teacher', 'course.typeDetail') - ->orderByRaw("FIELD(fee_status, 1, 0, 2,3)"); - if (isset($all['course_id'])) { - $query->where('course_id', $all['course_id']); + })->with([ + 'courseSigns' => function ($query) use ($all) { + $query->where('status', 1)->whereHas('course', function ($q) { + $q->where('is_fee', 1); + })->with('course.teacher', 'course.typeDetail') + ->orderByRaw("FIELD(fee_status, 1, 0, 2,3)"); + if (isset($all['course_id'])) { + $query->where('course_id', $all['course_id']); + } } - }])->where(function ($query) use ($all) { + ])->where(function ($query) use ($all) { if ($all['type'] == 1) { $query->where('is_schoolmate', 1); } diff --git a/app/Models/CourseSign.php b/app/Models/CourseSign.php index 546a2be..6db8b29 100755 --- a/app/Models/CourseSign.php +++ b/app/Models/CourseSign.php @@ -90,8 +90,10 @@ class CourseSign extends SoftDeletesModel $query->where('is_chart', 1); // 开始结束日期的筛选。or查询 if ($start_date && $end_date) { - $query->whereBetween('start_date', [$start_date, $end_date]) - ->orWhereBetween('end_date', [$start_date, $end_date]); + $query->where(function ($query) use ($start_date, $end_date) { + $query->whereBetween('start_date', [$start_date, $end_date]) + ->orWhereBetween('end_date', [$start_date, $end_date]); + }); } })->whereNotIn('status', [4, 5, 6]); return $baseQuery; @@ -582,7 +584,7 @@ class CourseSign extends SoftDeletesModel /** * 元和员工参人员 */ - public static function companyJoin($start_date = null, $end_date = null, $course_ids = null, $retList = false) + public static function companyJoin($start_date = null, $end_date = null, $course_ids = null, $retList = false, $needHistory = true) { $courseSignsQuery = self::getStudentList($start_date, $end_date, null, $course_ids); $courseSignByType = $courseSignsQuery->get(); @@ -618,14 +620,17 @@ class CourseSign extends SoftDeletesModel } else { // 基础数据 $baseCount = $list->count(); - // 额外数据 - $employeeParticipations = EmployeeParticipation::where(function ($query) use ($start_date, $end_date) { - // 开始结束日期的筛选。or查询 - if ($start_date && $end_date) { - $query->whereBetween('start_date', [$start_date, $end_date]) - ->orWhereBetween('end_date', [$start_date, $end_date]); - } - })->where('type', 1)->sum('total'); + $employeeParticipations = 0; + if ($needHistory) { + // 额外数据 + $employeeParticipations = EmployeeParticipation::where(function ($query) use ($start_date, $end_date) { + // 开始结束日期的筛选。or查询 + if ($start_date && $end_date) { + $query->whereBetween('start_date', [$start_date, $end_date]) + ->orWhereBetween('end_date', [$start_date, $end_date]); + } + })->where('type', 1)->sum('total'); + } // 返回统计数据 return $baseCount + $employeeParticipations; }