diff --git a/app/Console/Commands/UpdateCompany.php b/app/Console/Commands/UpdateCompany.php index b5ada7d..f6eb36b 100755 --- a/app/Console/Commands/UpdateCompany.php +++ b/app/Console/Commands/UpdateCompany.php @@ -226,8 +226,12 @@ class UpdateCompany extends Command $bar = $this->output->createProgressBar($total); $bar->start(); - // 上市代码正则:数字.SH 或 数字.SZ 或 数字.BJ - $stockCodePattern = '/\d{6}\.(SH|SZ|BJ)/i'; + // 上市代码正则:匹配全球各地上市公司股票代码后缀 + // 支持的后缀:.SH,.SZ,.BJ,.TW,.HK,.SG,.US,.DE,.FR,.JP,.KR,.N,.O,.A,.PK,.Q,.TO,.AX,.L,-S,-B,-SB,-P,-Z,-W,-SW,-SWR,-R,-WR,-X,-SS,-RS,.WS,.U,.PR,.B,.DB,.UN,.RT,.WT,.E,.C,.D,.F,.G,.H,.I,.J,.K,.L,.M,.N,.O,.P,.V,.Y,.Z + // 简化匹配:只要字符串中包含分隔符(.或-)+指定后缀,就算上市 + // 支持格式:688001.SH、AAPG.O、TSLA.US、华兴源创(688001.SH)等 + // 后缀按长度从长到短排序,避免短后缀误匹配 + $stockCodePattern = '/[.\-](SWR|SW|WR|SS|RS|SB|PK|TO|AX|WS|PR|DB|UN|RT|WT|SH|SZ|BJ|TW|HK|SG|US|DE|FR|JP|KR|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|U|V|W|X|Y|Z)(?![A-Za-z0-9])/i'; $updatedCount = 0; foreach ($companies as $company) { diff --git a/app/Http/Controllers/Admin/CompanyController.php b/app/Http/Controllers/Admin/CompanyController.php index 897c332..bd0a774 100644 --- a/app/Http/Controllers/Admin/CompanyController.php +++ b/app/Http/Controllers/Admin/CompanyController.php @@ -120,7 +120,14 @@ class CompanyController extends BaseController } if ($key == 'company_tag') { $valueArray = explode(',', $value); - $query->whereIn($key, $valueArray); + if (!empty($valueArray)) { + $query->where(function ($q) use ($valueArray) { + foreach ($valueArray as $item) { + $item = trim($item); + $q->where('company_tag', 'like', '%' . $item . '%'); + } + }); + } continue; } diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index 25fb440..4f05d9e 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -237,22 +237,23 @@ class OtherController extends CommonController $course_type_id = request('course_type_id'); if ($course_type_id) { $course_type_id = explode(',', $course_type_id); - $query->whereIn('course_type_id1', $course_type_id); + $query->whereIn('course_type_id', $course_type_id); } }); $list['course_total'] = (clone $calendar)->count(); // 开课天数 $list['course_day_total'] = (clone $calendar)->where('is_count_days', 1)->sum('days'); + $course_ids = $courses->pluck('id'); + // 上市公司数(所有上市公司) - $list['company_market_total'] = Company::companyMarket($start_date, $end_date); + $list['company_market_total'] = CourseSign::shangshi($start_date, $end_date, $course_ids); // 跟班学员数(在指定时间范围内报名的学员中,from为'跟班学员'的数量) - $course_ids = $courses->pluck('id'); $list['ganbu_total'] = CourseSign::genban($start_date, $end_date, $course_ids); // 今年上市公司数量(stock_date在今年) - $list['company_market_year_total'] = Company::companyMarketYear($start_date, $end_date, $course_ids); + $list['company_market_year_total'] = CourseSign::companyMarketYear($start_date, $end_date, $course_ids); // 入学后上市公司数量(在指定时间范围内报名的学员所在公司中,在入学后上市的公司数量) $list['company_market_after_enrollment_total'] = CourseSign::companyMarketAfterEnrollment($start_date, $end_date, $course_ids); @@ -260,6 +261,9 @@ class OtherController extends CommonController // 入学后被投企业数量(在指定时间范围内报名的学员所在公司中,在入学后被投的公司数量) $list['company_invested_after_enrollment_total'] = CourseSign::companyInvestedAfterEnrollment($start_date, $end_date, $course_ids); + // 今年被投企业数 + $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); // 全市干部参与企业 @@ -304,30 +308,30 @@ class OtherController extends CommonController } } // 附加历史课程数据 - $historyCourses = HistoryCourse::whereHas('calendar', function ($query) { - $query->where('is_count_people', 1); - })->where(function ($query) use ($start_date, $end_date) { - // 开始结束日期的筛选。or查询 - $query->whereBetween('start_time', [$start_date, $end_date]) - ->orWhereBetween('end_time', [$start_date, $end_date]); - })->where(function ($query) { - $course_type_id = request('course_type_id', ''); - $course_type_id = explode(',', $course_type_id); - if ($course_type_id) { - $query->whereIn('type', $course_type_id); - } - })->get(); - foreach ($historyCourses as $historyCourse) { - $courseTypesSum[] = [ - 'course_type' => $historyCourse->typeDetail->name, - // 培养人数 - 'course_type_signs_pass' => $historyCourse->course_type_signs_pass, - // 去重培养人数 - 'course_type_signs_pass_unique' => $historyCourse->course_type_signs_pass_unique, - 'course_name' => $historyCourse->course_name, - 'course_signs_pass' => $historyCourse->course_signs_pass, - ]; - } +// $historyCourses = HistoryCourse::whereHas('calendar', function ($query) { +// $query->where('is_count_people', 1); +// })->where(function ($query) use ($start_date, $end_date) { +// // 开始结束日期的筛选。or查询 +// $query->whereBetween('start_time', [$start_date, $end_date]) +// ->orWhereBetween('end_time', [$start_date, $end_date]); +// })->where(function ($query) { +// $course_type_id = request('course_type_id'); +// if ($course_type_id) { +// $course_type_id = explode(',', $course_type_id); +// $query->whereIn('type', $course_type_id); +// } +// })->get(); +// foreach ($historyCourses as $historyCourse) { +// $courseTypesSum[] = [ +// 'course_type' => $historyCourse->typeDetail->name, +// // 培养人数 +// 'course_type_signs_pass' => $historyCourses->where('type', $historyCourse->type)->sum('course_type_signs_pass'), +// // 去重培养人数 +// 'course_type_signs_pass_unique' => $historyCourses->where('type', $historyCourse->type)->sum('course_type_signs_pass_unique'), +// 'course_name' => $historyCourse->course_name, +// 'course_signs_pass' => $historyCourse->course_signs_pass, +// ]; +// } // 区域明细统计 $areas = CourseSign::area($start_date, $end_date, 1, $courses->pluck('id'), true); @@ -564,10 +568,17 @@ class OtherController extends CommonController break; case 'company_market_total': - // 上市公司明细 - 所有上市公司 - $companies = Company::companyMarket($start_date, $end_date, true); - foreach ($companies as $company) { - $data[] = [ + // 上市公司明细 - 所有上市公司,关联学员、课程信息 + // 数据结构:主表是公司,子数据是学员信息 + // 导出时:公司信息只在第一行显示,后续行公司信息为空 + $companiesData = CourseSign::shangshi($start_date, $end_date, $course_ids->toArray(), true); + + foreach ($companiesData as $item) { + $company = $item['company']; + $users = $item['users'] ?? []; + + // 公司基本信息(只在第一行使用) + $companyInfo = [ 'company_name' => $company->company_name, 'company_legal_representative' => $company->company_legal_representative ?? '', 'company_date' => $company->company_date ?? '', @@ -575,7 +586,60 @@ class OtherController extends CommonController 'company_address' => $company->company_address ?? '', 'company_city' => $company->company_city ?? '', 'company_area' => $company->company_area ?? '', + 'company_tag' => $company->company_tag ?? '', ]; + + if (empty($users)) { + // 如果没有学员报名记录,仍然导出公司基本信息 + $data[] = array_merge($companyInfo, [ + 'user_name' => '', + 'course_name' => '', + 'course_type' => '', + ]); + } else { + // 每个学员一行,多个课程合并显示 + $isFirstRow = true; + foreach ($users as $userInfo) { + $courses = $userInfo['courses'] ?? []; + + // 合并同一学员的多个课程:格式为"课程体系-课程名称,课程体系-课程名称" + $courseList = []; + foreach ($courses as $courseInfo) { + $courseType = $courseInfo['course_type'] ?? ''; + $courseName = $courseInfo['course_name'] ?? ''; + if ($courseType && $courseName) { + $courseList[] = $courseType . '-' . $courseName; + } elseif ($courseName) { + $courseList[] = $courseName; + } + } + $courseDisplay = implode("\r\n", $courseList); + + if ($isFirstRow) { + // 第一行:显示公司信息 + $data[] = array_merge($companyInfo, [ + 'user_name' => $userInfo['user_name'] ?? '', + 'course_name' => $courseDisplay, + 'course_type' => '', // 课程类型已合并到课程名称中 + ]); + $isFirstRow = false; + } else { + // 后续行:公司信息为空 + $data[] = [ + 'company_name' => '', + 'company_legal_representative' => '', + 'company_date' => '', + 'stock_date' => '', + 'company_address' => '', + 'company_city' => '', + 'company_area' => '', + 'company_tag' => '', + 'user_name' => $userInfo['user_name'] ?? '', + 'course_name' => $courseDisplay, + ]; + } + } + } } $fields = [ 'company_name' => '企业名称', @@ -583,8 +647,11 @@ class OtherController extends CommonController 'company_date' => '成立时间', 'stock_date' => '上市日期', 'company_address' => '地址', - 'company_city' => '营业范围', - 'company_area' => '联系电话', + 'company_city' => '所在城市', + 'company_area' => '所在区域', + 'company_tag' => '企业资质', + 'user_name' => '学员姓名', + 'course_name' => '课程信息', ]; $filename = '上市公司明细'; break; @@ -614,10 +681,17 @@ class OtherController extends CommonController break; case 'company_market_year_total': - // 今年上市公司明细 - 使用模型方法 - $companies = Company::companyMarketYear($start_date, $end_date, $course_ids, true); - foreach ($companies as $company) { - $data[] = [ + // 今年上市公司明细 - 所有今年上市公司,关联学员、课程信息 + // 数据结构:主表是公司,子数据是学员信息 + // 导出时:公司信息只在第一行显示,后续行公司信息为空 + $companiesData = CourseSign::companyMarketYear($start_date, $end_date, $course_ids->toArray(), true); + + foreach ($companiesData as $item) { + $company = $item['company']; + $users = $item['users'] ?? []; + + // 公司基本信息(只在第一行使用) + $companyInfo = [ 'company_name' => $company->company_name, 'company_legal_representative' => $company->company_legal_representative ?? '', 'company_date' => $company->company_date ?? '', @@ -625,7 +699,60 @@ class OtherController extends CommonController 'company_address' => $company->company_address ?? '', 'company_city' => $company->company_city ?? '', 'company_area' => $company->company_area ?? '', + 'company_tag' => $company->company_tag ?? '', ]; + + if (empty($users)) { + // 如果没有学员报名记录,仍然导出公司基本信息 + $data[] = array_merge($companyInfo, [ + 'user_name' => '', + 'course_name' => '', + 'course_type' => '', + ]); + } else { + // 每个学员一行,多个课程合并显示 + $isFirstRow = true; + foreach ($users as $userInfo) { + $courses = $userInfo['courses'] ?? []; + + // 合并同一学员的多个课程:格式为"课程体系-课程名称,课程体系-课程名称" + $courseList = []; + foreach ($courses as $courseInfo) { + $courseType = $courseInfo['course_type'] ?? ''; + $courseName = $courseInfo['course_name'] ?? ''; + if ($courseType && $courseName) { + $courseList[] = $courseType . '-' . $courseName; + } elseif ($courseName) { + $courseList[] = $courseName; + } + } + $courseDisplay = implode("\r\n", $courseList); + + if ($isFirstRow) { + // 第一行:显示公司信息 + $data[] = array_merge($companyInfo, [ + 'user_name' => $userInfo['user_name'] ?? '', + 'course_name' => $courseDisplay, + 'course_type' => '', // 课程类型已合并到课程名称中 + ]); + $isFirstRow = false; + } else { + // 后续行:公司信息为空 + $data[] = [ + 'company_name' => '', + 'company_legal_representative' => '', + 'company_date' => '', + 'stock_date' => '', + 'company_address' => '', + 'company_city' => '', + 'company_area' => '', + 'company_tag' => '', + 'user_name' => $userInfo['user_name'] ?? '', + 'course_name' => $courseDisplay, + ]; + } + } + } } $fields = [ 'company_name' => '企业名称', @@ -633,8 +760,11 @@ class OtherController extends CommonController 'company_date' => '成立时间', 'stock_date' => '上市日期', 'company_address' => '地址', - 'company_city' => '营业范围', - 'company_area' => '联系电话', + 'company_city' => '所在城市', + 'company_area' => '所在区域', + 'company_tag' => '企业资质', + 'user_name' => '学员姓名', + 'course_name' => '课程信息', ]; $filename = '今年上市公司明细'; break; @@ -781,7 +911,7 @@ class OtherController extends CommonController $data[] = [ 'user_name' => $user->name ?? '', 'mobile' => $user->mobile ?? '', - 'company_name' => $user->company->company_name ?? '', + 'company_name' => $user->company->company_name ?? $user->company_name, 'company_position' => $user->company_position ?? '', 'company_city' => $user->company->company_city ?? '', 'company_area' => $user->company->company_area ?? '', diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 6d1438a..7d2ac42 100755 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -55,9 +55,11 @@ class UserController extends BaseController { $all = request()->all(); $list = $this->model->with(underlineToHump($all['show_relation'] ?? [])) - ->with(['courseSigns' => function ($query) use ($all) { - $query->where('status', 1)->with('course.teacher', 'course.typeDetail'); - }])->where(function ($query) use ($all) { + ->with([ + 'courseSigns' => function ($query) use ($all) { + $query->where('status', 1)->with('course.teacher', 'course.typeDetail'); + } + ])->where(function ($query) use ($all) { if (isset($all['keyword'])) { $query->whereHas('courses', function ($q) use ($all) { $q->where('name', 'like', '%' . $all['keyword'] . '%'); @@ -196,13 +198,22 @@ class UserController extends BaseController $start_date = $year . '-01-01'; $end_date = $year . '-12-31'; - $list = $this->model->with('appointments', 'companyIndustryDetail', - 'companyPositionDetail', 'companyAreaDetail', 'company') - ->with(['courseSigns' => function ($query) { - $query->with('course.typeDetail')->orderBy('fee_status', 'desc'); - }])->withCount(['appointments' => function ($query) { - $query->whereIn('status', [0, 1]); - }]); + $list = $this->model->with( + 'appointments', + 'companyIndustryDetail', + 'companyPositionDetail', + 'companyAreaDetail', + 'company' + ) + ->with([ + 'courseSigns' => function ($query) { + $query->with('course.typeDetail')->orderBy('fee_status', 'desc'); + } + ])->withCount([ + 'appointments' => function ($query) { + $query->whereIn('status', [0, 1]); + } + ]); // 是否被投企业 if (isset($all['is_yh_invested'])) { $list = $list->whereHas('company', function ($query) use ($all) { @@ -213,11 +224,27 @@ class UserController extends BaseController } }); } + // company_tag等价于company_type,新旧数据兼容 + if (isset($all['company_type'])) { + $all['company_tag'] = $all['company_type']; + } + + // 新数据 if (isset($all['company_tag'])) { $list = $list->whereHas('company', function ($query) use ($all) { - $query->where('company_tag', 'like', '%' . $all['company_tag'] . '%'); + $string = explode(',', $all['company_tag']); + $query->where(function ($q) use ($string) { + foreach ($string as $index => $v) { + if ($index === 0) { + $q->where('company_tag', 'like', '%' . trim($v) . '%'); + } else { + $q->orWhere('company_tag', 'like', '%' . trim($v) . '%'); + } + } + }); }); } + $list = $list->whereHas('courseSigns', function ($query) use ($all) { if (isset($all['course_id'])) { $query->where('course_id', $all['course_id']); @@ -302,14 +329,6 @@ class UserController extends BaseController $company_area = explode(',', $all['company_area']); $query->whereIn('company_area', $company_area); } - if (isset($all['company_type'])) { - $company_type = explode(',', $all['company_type']); - $query->where(function ($q) use ($company_type) { - foreach ($company_type as $v) { - $q->orWhereRaw('FIND_IN_SET(?, company_type)', [$v]); - } - }); - } if (isset($all['company_industry'])) { $company_industry = explode(',', $all['company_industry']); $query->where(function ($q) use ($company_industry) { @@ -521,7 +540,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]; } @@ -672,7 +692,7 @@ class UserController extends BaseController if (isset($all['is_schoolmate'])) { $data['is_schoolmate'] = $all['is_schoolmate']; } -// if (isset($all['is_black'])) { + // if (isset($all['is_black'])) { // $data['is_black'] = $all['is_black']; // } $this->model->whereIn('id', $idsArray)->update($data); diff --git a/app/Models/Company.php b/app/Models/Company.php index a9feda8..e5a10b2 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -58,52 +58,4 @@ class Company extends SoftDeletesModel } } - /** - * 上市公司(统计或列表) - * @param bool $retList 是否返回列表,false返回数量,true返回列表 - * @return int|\Illuminate\Database\Eloquent\Collection - */ - public static function companyMarket($start_date, $end_date, $retList = false) - { - $courseSignByType = CourseSign::whereDate('created_at', '>=', $start_date) - ->whereDate('created_at', '<=', $end_date) - ->whereNotIn('status', [4, 5]) - ->get(); - $list = Company::whereHas('users', function ($query) use ($courseSignByType) { - $query->whereIn('id', $courseSignByType->pluck('user_id')); - })->where('company_market', 1)->get(); - if ($retList) { - // 返回列表 - return $list; - } else { - // 返回统计数据 - return $list->count(); - } - } - - /** - * 今年上市公司(统计或列表) - * @param int|null $year 年份,不传则使用当前年份 - * @param bool $retList 是否返回列表,false返回数量,true返回列表 - * @return int|\Illuminate\Database\Eloquent\Collection - */ - public static function companyMarketYear($start_date, $end_date, $course_ids, $retList = false) - { - $year = date('Y'); - $courseSignByType = CourseSign::whereDate('created_at', '>=', $start_date) - ->whereDate('created_at', '<=', $end_date) - ->whereNotIn('status', [4, 5]) - ->get(); - $list = Company::whereHas('users', function ($query) use ($courseSignByType) { - $query->whereIn('id', $courseSignByType->pluck('user_id')); - })->where('company_market', 1)->whereYear('stock_date', $year)->get(); - if ($retList) { - // 返回列表 - return $list; - } else { - // 返回统计数据 - return $list->count(); - } - } - } diff --git a/app/Models/CourseSign.php b/app/Models/CourseSign.php index 7725356..08eb36a 100755 --- a/app/Models/CourseSign.php +++ b/app/Models/CourseSign.php @@ -117,9 +117,9 @@ class CourseSign extends SoftDeletesModel $query->whereBetween('start_time', [$start_date, $end_date]) ->orWhereBetween('end_time', [$start_date, $end_date]); })->where(function ($query) { - $course_type_id = request('course_type_id', ''); - $course_type_id = explode(',', $course_type_id); + $course_type_id = request('course_type_id'); if ($course_type_id) { + $course_type_id = explode(',', $course_type_id); $query->whereIn('type', $course_type_id); } })->sum('course_type_signs_pass'); @@ -148,9 +148,9 @@ class CourseSign extends SoftDeletesModel $query->whereBetween('start_time', [$start_date, $end_date]) ->orWhereBetween('end_time', [$start_date, $end_date]); })->where(function ($query) { - $course_type_id = request('course_type_id', ''); - $course_type_id = explode(',', $course_type_id); + $course_type_id = request('course_type_id'); if ($course_type_id) { + $course_type_id = explode(',', $course_type_id); $query->whereIn('type', $course_type_id); } })->sum('course_type_signs_pass_unique'); @@ -213,6 +213,109 @@ class CourseSign extends SoftDeletesModel } } + /** + * 今年被投企业统计(统计或列表) + * @param string|null $start_date 开始日期 + * @param string|null $end_date 结束日期 + * @param array|null $course_ids 课程ID数组,不传则统计所有课程 + * @param bool $retList 是否返回列表,false返回数量,true返回列表(包含学员、课程信息) + * @return int|array + */ + public static function companyInvestedYear($start_date = null, $end_date = null, $course_ids = null, $retList = false) + { + $courseSignsQuery = self::getStudentList($start_date, $end_date, 1, $course_ids); + $courseSigns = $courseSignsQuery->with(['user.company', 'course.typeDetail'])->get(); + + // 获取所有被投企业的ID + $companyIds = $courseSigns->pluck('user.company.id') + ->filter() + ->unique() + ->toArray(); + + $year = date('Y'); + // 获取这些公司中标记为被投的公司 + $allInvestedCompanies = Company::whereIn('id', $companyIds) + ->where('is_yh_invested', 1) + ->get(); + + // 筛选出被投时间在今年的企业 + $companies = []; + foreach ($allInvestedCompanies as $company) { + $projectUsers = $company->project_users ?? []; + $hasInvestThisYear = false; + foreach ($projectUsers as $item) { + $investDate = $item['investDate'] ?? null; + if ($investDate) { + $investYear = date('Y', strtotime($investDate)); + if ($investYear == $year) { + $hasInvestThisYear = true; + break; + } + } + } + if ($hasInvestThisYear) { + $companies[$company->id] = $company; + } + } + $companies = collect($companies); + + if ($retList) { + // 返回详细列表:主表是公司,子数据是学员信息 + $result = []; + foreach ($courseSigns as $courseSign) { + if (!$courseSign->user || !$courseSign->user->company) { + continue; + } + + $companyId = $courseSign->user->company->id; + // 只处理今年被投企业的记录 + if (!isset($companies[$companyId])) { + continue; + } + + $company = $companies[$companyId]; + + // 如果公司还没有在结果中,初始化 + if (!isset($result[$companyId])) { + $result[$companyId] = [ + 'company' => $company, + 'users' => [], + ]; + } + + // 按学员分组,收集每个学员的课程信息 + $userId = $courseSign->user->id; + if (!isset($result[$companyId]['users'][$userId])) { + $result[$companyId]['users'][$userId] = [ + 'user' => $courseSign->user, + 'user_name' => $courseSign->user->name ?? '', + 'mobile' => $courseSign->user->mobile ?? '', + 'courses' => [], + ]; + } + + // 添加该学员的课程信息 + $result[$companyId]['users'][$userId]['courses'][] = [ + 'course_name' => $courseSign->course->name ?? '', + 'course_type' => $courseSign->course->typeDetail->name ?? '', + 'course_sign' => $courseSign, + ]; + } + + // 将 users 转换为数组(去掉 user_id 作为 key) + foreach ($result as $companyId => $item) { + $result[$companyId]['users'] = array_values($item['users']); + } + + // 转换为数组并返回 + return array_values($result); + } else { + // 返回统计数据 + return $companies->count(); + } + } + + /** * 跟班学员(统计或列表) * @param string $start_date 开始日期 @@ -410,14 +513,17 @@ class CourseSign extends SoftDeletesModel '常州团队', '国企元禾' ]; - $company = Company::where(function ($query) use ($companyNameKeyword) { - foreach ($companyNameKeyword as $item) { - $query->orWhere('company_name', 'like', '%' . $item . '%'); - } - })->get(); + // $company = Company::where(function ($query) use ($companyNameKeyword) { +// foreach ($companyNameKeyword as $item) { +// $query->orWhere('company_name', 'like', '%' . $item . '%'); +// } +// })->get(); $list = User::whereIn('id', $courseSignByType->pluck('user_id')) - ->whereIn('company_id', $company->pluck('id')) - ->get(); + ->where(function ($query) use ($companyNameKeyword) { + foreach ($companyNameKeyword as $item) { + $query->orWhere('company_name', 'like', '%' . $item . '%'); + } + })->get(); if ($retList) { // 返回列表 @@ -479,34 +585,206 @@ class CourseSign extends SoftDeletesModel public static function rencai($start_date = null, $end_date = null, $course_ids = null, $retList = false) { $courseSignsQuery = self::getStudentList($start_date, $end_date, 1, $course_ids); - $courseSigns = $courseSignsQuery->whereHas('course', function ($query) { + + // 条件1:人才培训课程类型的用户 + $courseSigns1 = $courseSignsQuery->whereHas('course', function ($query) { $query->whereHas('typeDetail', function ($q) { $q->where('name', '人才培训'); }); })->get(); + + // 条件2:data 字段中 name="个人荣誉" 且 value != "其他" 的用户 + $courseSigns2 = $courseSignsQuery->get()->filter(function ($courseSign) { + if (empty($courseSign->data) || !is_array($courseSign->data)) { + return false; + } + foreach ($courseSign->data as $item) { + if (isset($item['key']) && $item['key'] === 'type') { + if (isset($item['value']) && $item['value'] !== '其他') { + return true; + } + } + } + return false; + }); + + // 合并两个条件的结果(或关系),并去重 user_id + $allUserIds = $courseSigns1->pluck('user_id') + ->merge($courseSigns2->pluck('user_id')) + ->unique() + ->filter(); + if ($retList) { - return User::whereIn('id', $courseSigns->pluck('user_id'))->get(); + return User::whereIn('id', $allUserIds)->get(); } else { - return User::whereIn('id', $courseSigns->pluck('user_id'))->count(); + return $allUserIds->count(); } } /** - * 重点上市公司 + * 上市公司(统计或列表) + * @param string|null $start_date 开始日期 + * @param string|null $end_date 结束日期 + * @param array|null $course_ids 课程ID数组,不传则统计所有课程 + * @param bool $retList 是否返回列表,false返回数量,true返回列表(包含学员、课程信息) + * @return int|array */ public static function shangshi($start_date = null, $end_date = null, $course_ids = null, $retList = false) { $courseSignsQuery = self::getStudentList($start_date, $end_date, 1, $course_ids); - $list = Company::whereHas('users', function ($query) use ($courseSignsQuery) { - $query->whereIn('id', $courseSignsQuery->get()->pluck('user_id')); - })->where('company_market', 1)->get(); + $courseSigns = $courseSignsQuery->with(['user.company', 'course.typeDetail'])->get(); + + // 获取所有上市公司的ID + $companyIds = $courseSigns->pluck('user.company.id') + ->filter() + ->unique() + ->toArray(); + + // 获取这些公司中标记为上市的公司 + $companies = Company::whereIn('id', $companyIds) + ->where('company_market', 1) + ->get() + ->keyBy('id'); + if ($retList) { - return $list; + // 返回详细列表:主表是公司,子数据是学员信息 + $result = []; + foreach ($courseSigns as $courseSign) { + if (!$courseSign->user || !$courseSign->user->company) { + continue; + } + + $companyId = $courseSign->user->company->id; + // 只处理上市公司的记录 + if (!isset($companies[$companyId])) { + continue; + } + + $company = $companies[$companyId]; + + // 如果公司还没有在结果中,初始化 + if (!isset($result[$companyId])) { + $result[$companyId] = [ + 'company' => $company, + 'users' => [], + ]; + } + + // 按学员分组,收集每个学员的课程信息 + $userId = $courseSign->user->id; + if (!isset($result[$companyId]['users'][$userId])) { + $result[$companyId]['users'][$userId] = [ + 'user' => $courseSign->user, + 'user_name' => $courseSign->user->name ?? '', + 'mobile' => $courseSign->user->mobile ?? '', + 'courses' => [], + ]; + } + + // 添加该学员的课程信息 + $result[$companyId]['users'][$userId]['courses'][] = [ + 'course_name' => $courseSign->course->name ?? '', + 'course_type' => $courseSign->course->typeDetail->name ?? '', + 'course_sign' => $courseSign, + ]; + } + + // 将 users 转换为数组(去掉 user_id 作为 key) + foreach ($result as $companyId => $item) { + $result[$companyId]['users'] = array_values($item['users']); + } + + // 转换为数组并返回 + return array_values($result); } else { - return $list->count(); + // 返回统计数据 + return $companies->count(); } + } + + /** + * 今年上市公司(统计或列表) + * @param string|null $start_date 开始日期 + * @param string|null $end_date 结束日期 + * @param array|null $course_ids 课程ID数组,不传则统计所有课程 + * @param bool $retList 是否返回列表,false返回数量,true返回列表(包含学员、课程信息) + * @return int|array + */ + public static function companyMarketYear($start_date = null, $end_date = null, $course_ids = null, $retList = false) + { + $courseSignsQuery = self::getStudentList($start_date, $end_date, 1, $course_ids); + $courseSigns = $courseSignsQuery->with(['user.company', 'course.typeDetail'])->get(); + + // 获取所有上市公司的ID + $companyIds = $courseSigns->pluck('user.company.id') + ->filter() + ->unique() + ->toArray(); + + $year = date('Y'); + // 获取这些公司中标记为上市的公司,且上市日期为今年 + $companies = Company::whereIn('id', $companyIds) + ->where('company_market', 1) + ->whereYear('stock_date', $year) + ->get() + ->keyBy('id'); + + if ($retList) { + // 返回详细列表:主表是公司,子数据是学员信息 + $result = []; + foreach ($courseSigns as $courseSign) { + if (!$courseSign->user || !$courseSign->user->company) { + continue; + } + + $companyId = $courseSign->user->company->id; + // 只处理上市公司的记录 + if (!isset($companies[$companyId])) { + continue; + } + + $company = $companies[$companyId]; + // 如果公司还没有在结果中,初始化 + if (!isset($result[$companyId])) { + $result[$companyId] = [ + 'company' => $company, + 'users' => [], + ]; + } + + // 按学员分组,收集每个学员的课程信息 + $userId = $courseSign->user->id; + if (!isset($result[$companyId]['users'][$userId])) { + $result[$companyId]['users'][$userId] = [ + 'user' => $courseSign->user, + 'user_name' => $courseSign->user->name ?? '', + 'mobile' => $courseSign->user->mobile ?? '', + 'courses' => [], + ]; + } + + // 添加该学员的课程信息 + $result[$companyId]['users'][$userId]['courses'][] = [ + 'course_name' => $courseSign->course->name ?? '', + 'course_type' => $courseSign->course->typeDetail->name ?? '', + 'course_sign' => $courseSign, + ]; + } + + // 将 users 转换为数组(去掉 user_id 作为 key) + foreach ($result as $companyId => $item) { + $result[$companyId]['users'] = array_values($item['users']); + } + + // 转换为数组并返回 + return array_values($result); + } else { + // 返回统计数据 + return $companies->count(); + } } } + diff --git a/课程台账.xlsx b/课程台账.xlsx deleted file mode 100644 index b1e5186..0000000 Binary files a/课程台账.xlsx and /dev/null differ diff --git a/课程统计说明文档.md b/课程统计说明文档.md deleted file mode 100644 index 371a734..0000000 --- a/课程统计说明文档.md +++ /dev/null @@ -1,300 +0,0 @@ -# 课程统计系统数据说明文档 - - -## 🎯 一、基础统计数据 - -### 1. 报名人数(course_signs_total) - -**简单理解**:就像学校统计有多少人报名了课程。 - -**统计方式**: -- 系统会查看在您选择的时间范围内,所有提交了报名申请的人数 -- 不管审核是否通过,只要提交了报名就算一个人 -- 如果同一个人报名了多门课程,会分别计算(比如张三报名了3门课,就算3个人次) - -**举个例子**: -- 时间范围:2024年1月1日 - 2024年12月31日 -- 张三报名了"企业管理"课程 → 算1人 -- 李四报名了"财务管理"和"市场营销"两门课程 → 算2人 -- 总计:3人次 - ---- - -### 2. 审核通过人数(course_signs_pass) - -**简单理解**:报名后,经过审核被批准参加课程的人数。 - -**统计方式**: -- 只统计审核状态为"审核通过"的报名记录 -- 同样,如果一个人通过了多门课程的审核,会分别计算 - -**与报名人数的区别**: -- 报名人数 = 所有提交申请的人(包括待审核、已通过、已拒绝) -- 审核通过人数 = 只有审核通过的人 - -**举个例子**: -- 100人报名,其中80人审核通过,20人待审核或未通过 -- 报名人数 = 100人 -- 审核通过人数 = 80人 - ---- - -### 3. 审核通过人数(去重)(course_signs_pass_unique) - -**简单理解**:这是"真实的人数",一个人不管报名了多少门课程,只算1个人。 - -**统计方式**: -- 系统会通过手机号来识别是否是同一个人 -- 如果同一个手机号报名了多门课程,只算1个人 -- 这样可以知道真正有多少个不同的学员参加了培训 - -**举个例子**: -- 张三用手机号13800138000报名了3门课程,都审核通过了 -- 李四用手机号13900139000报名了1门课程,审核通过了 -- 审核通过人数(不去重)= 4人次(3+1) -- 审核通过人数(去重)= 2人(张三1人 + 李四1人) - -**为什么需要去重?** -- 不去重:可以知道课程的总参与人次,了解课程的受欢迎程度 -- 去重:可以知道实际培养了多少个不同的学员,这是更准确的培养人数 - ---- - -### 4. 开课场次(course_total) - -**简单理解**:在指定时间范围内,实际开了多少次课。 - -**统计方式**: -- 系统会查看课程日历表,统计在您选择的时间范围内,有多少次课程安排 -- 每次课程安排算1场次 -- 比如一门课程在1月、2月、3月各开一次,就算3场次 - -**举个例子**: -- 时间范围:2024年第一季度 -- "企业管理"课程在1月15日、2月15日、3月15日各开一次 → 3场次 -- "财务管理"课程在1月20日、2月20日各开一次 → 2场次 -- 总计:5场次 - ---- - -### 5. 开课天数(course_day_total) - -**简单理解**:所有课程加起来,总共上了多少天课。 - -**统计方式**: -- 系统会统计每场课程持续的天数 -- 把所有场次的天数加起来 -- 比如一门课程持续3天,另一门持续2天,总计就是5天 - -**与开课场次的区别**: -- 开课场次 = 开了多少次课(不管每次上几天) -- 开课天数 = 总共上了多少天课 - -**举个例子**: -- "企业管理"课程开了3次,每次持续2天 → 6天 -- "财务管理"课程开了2次,每次持续1天 → 2天 -- 总计:8天 - ---- - -## 🏢 二、企业相关统计数据 - -### 6. 被投企业数(course_signs_invested) - -**简单理解**:参加培训的学员中,有多少人所在的企业是被我们投资的企业。 - -**统计方式**: -- 系统会查看每个学员所在的企业信息 -- 如果企业的"是否被投"标记为"是",就算1家企业 -- 如果多个学员来自同一家被投企业,只算1家企业(去重) - -**举个例子**: -- 张三、李四、王五都来自"元禾控股"这家被投企业 -- 虽然3个人都参加了培训,但只算1家被投企业 -- 如果还有赵六来自另一家被投企业"元禾原点",那就是2家被投企业 - ---- - -### 7. 上市公司数(company_market_total) - -**简单理解**:参加培训的学员中,有多少人所在的企业是上市公司。 - -**统计方式**: -- 系统会查看每个学员所在的企业信息 -- 如果企业的"是否上市公司"标记为"是",就算1家上市公司 -- 如果多个学员来自同一家上市公司,只算1家企业(去重) - -**举个例子**: -- 张三、李四都来自"苏州某上市公司" -- 虽然2个人都参加了培训,但只算1家上市公司 - ---- - -### 8. 今年上市公司数量(company_market_year_total) - -**简单理解**:在参加培训的学员所在企业中,有多少家是在今年(当前年份)上市的。 - -**统计方式**: -- 系统会查看每个学员所在的企业信息 -- 如果企业是上市公司,且上市日期(stock_date)在今年,就算1家 -- 如果多个学员来自同一家今年上市的公司,只算1家企业(去重) - -**举个例子**: -- 时间范围:2024年1月1日 - 2024年12月31日 -- 张三来自"A公司",A公司在2024年3月上市 → 算1家 -- 李四来自"B公司",B公司在2023年上市 → 不算(不是今年上市的) -- 总计:1家今年上市公司 - ---- - -### 9. 入学后上市公司数量(company_market_after_enrollment_total) - -**简单理解**:学员报名参加培训后,他所在的企业才上市的公司数量。 - -**统计方式**: -- 系统会比较学员的报名时间和企业上市时间 -- 如果企业上市时间 ≥ 学员报名时间,说明是"入学后上市" -- 如果多个学员来自同一家这样的公司,只算1家企业(去重) - -**举个例子**: -- 张三在2024年1月报名参加培训 -- 张三所在的企业在2024年6月上市 -- 因为上市时间(6月)晚于报名时间(1月),所以算1家"入学后上市公司" -- 这体现了培训对企业发展的促进作用 - ---- - -### 10. 元和员工参与企业(company_join_total) - -**简单理解**:参加培训的学员中,有多少人所在的企业名称中包含"元禾"相关关键词。 - -**统计方式**: -- 系统会检查企业名称,如果包含以下关键词之一,就算1家企业: - - 元禾控股、元禾原点、元禾厚望、元禾重元、元禾璞华、元禾谷风、元禾绿柳、元禾辰坤、元禾沙湖 - - 禾裕集团、苏州科服、信诚管理咨询 - - 集成电路公司、常州团队、国企元禾 -- 如果多个学员来自同一家这样的企业,只算1家企业(去重) - -**举个例子**: -- 张三来自"元禾控股有限公司" -- 李四来自"元禾原点投资公司" -- 虽然企业名称不同,但都包含"元禾",所以算2家企业 - ---- - -### 11. 全市干部参与企业(company_ganbu_total) - -**简单理解**:参加培训的学员中,有多少人的来源标记为"跟班学员"(通常是干部)。 - -**统计方式**: -- 系统会查看每个学员的"来源"字段 -- 如果来源是"跟班学员",就算1个人 -- 如果同一个人报名了多门课程,只算1个人(去重) - -**举个例子**: -- 张三的来源是"跟班学员",报名了2门课程 → 算1人 -- 李四的来源是"跟班学员",报名了1门课程 → 算1人 -- 总计:2人 - ---- - -### 12. 苏州头部企业(cover_head_total) - -**简单理解**:参加培训的学员中,有多少人所在的企业是苏州的头部企业(通常是高新技术企业)。 - -**统计方式**: -- 系统会查看企业信息中的"企业资质"字段 -- 如果企业资质中包含"高新技术企业",就算1家企业 -- 如果多个学员来自同一家这样的企业,只算1家企业(去重) - -**举个例子**: -- 张三、李四都来自"苏州某高新技术企业" -- 虽然2个人都参加了培训,但只算1家头部企业 - ---- - -### 13. 高层次人才(cover_rencai_total) - -**简单理解**:参加培训的学员中,有多少人报名的是"人才培训"类型的课程。 - -**统计方式**: -- 系统会查看学员报名的课程类型 -- 如果课程类型是"人才培训",就算1个人 -- 如果同一个人报名了多门"人才培训"课程,只算1个人(去重) - -**举个例子**: -- 张三报名了"人才培训"类型的课程 → 算1人 -- 李四报名了"企业管理"类型的课程 → 不算 -- 总计:1人 - ---- - -### 14. 重点上市公司(cover_stock_total) - -**简单理解**:参加培训的学员中,有多少人所在的企业是重点上市公司。 - -**统计方式**: -- 系统会查看企业信息中的"是否上市公司"字段 -- 如果标记为"是",就算1家企业 -- 如果多个学员来自同一家上市公司,只算1家企业(去重) - -**与"上市公司数"的区别**: -- 这个统计可能有额外的筛选条件(具体看业务需求) -- 通常"重点上市公司"是"上市公司"的一个子集 - ---- - -## 📊 三、明细统计数据 - -### 15. 课程分类明细统计(courseTypesSum) - -**简单理解**:按照不同的课程类型,分别统计每个类型和每门课程的培养人数。 - -**统计方式**: -- 系统会按照课程类型分组统计 -- 对于每个课程类型,会统计: - - 该类型所有课程的培养人数总和 - - 该类型所有课程的培养人数(去重) -- 对于每门具体课程,会统计该课程的培养人数 - -**举个例子**: -- 课程类型:"企业管理" - - 包含课程:"企业管理基础"、"企业管理进阶" - - 该类型培养人数:100人(两门课程加起来) - - 该类型培养人数(去重):80人(去除重复报名的人) - - "企业管理基础"课程培养人数:60人 - - "企业管理进阶"课程培养人数:40人 - ---- - -### 16. 区域明细统计(areas) - -**简单理解**:按照不同的区域,统计每个区域有多少学员参加了培训。 - -**统计方式**: -- 系统会按照学员所在企业的区域进行分组统计 -- 对于苏州市内的企业,会按照具体的区域(比如"工业园区"、"高新区"等)统计 -- 对于苏州市外的企业,统一归为"苏州市外" -- 每个区域会统计: - - 该区域的报名人数(不去重) - - 该区域的报名人数(去重,按手机号) - -**举个例子**: -- 工业园区:50人报名,去重后40人 -- 高新区:30人报名,去重后25人 -- 苏州市外:20人报名,去重后18人 - ---- - -## 🔍 四、统计的时间范围 - -所有统计数据都基于您选择的时间范围: - -- **开始日期**:统计从这个日期开始的报名或开课 -- **结束日期**:统计到这个日期为止的报名或开课 - -**重要说明**: -- 对于报名人数:看的是学员的报名时间是否在这个范围内 -- 对于开课场次:看的是课程的实际开课日期是否在这个范围内 -- 对于企业统计:看的是学员的报名时间是否在这个范围内 -