Merge branch 'master' of ssh://47.101.48.251:/data/git/wx.sstbc.com

master
lion 5 months ago
commit 52a77e62f8

@ -64,7 +64,7 @@ class CommonExport implements FromCollection
} elseif (str_contains($field, 'partners')) {
// 股东信息
$temp[$field] = $this->partners($info);
} elseif (str_contains($field, 'partners')) {
} elseif (str_contains($field, 'project_users')) {
// 项目经理
$temp[$field] = $this->projectManager($info);
} elseif (str_contains($field, 'users')) {
@ -156,7 +156,7 @@ class CommonExport implements FromCollection
{
$list = [];
foreach ($data['project_users'] as $item) {
$list[] = $item['groupName'] . '-' . $item['userName'] ?? '';
$list[] = $item['groupName'] . '-' . ($item['userName'] ?? '') . '-' . ($item['investDate'] ?? '');
}
return implode("、\r\n", $list);
}
@ -169,7 +169,11 @@ class CommonExport implements FromCollection
{
$list = [];
foreach ($data['users'] as $item) {
$list[] = $item['username'] . '-' . $item['company_position'] . '-' . $item['mobile'] ?? '';
$base = $item['no'] . '-' . $item['username'] . '-' . $item['is_schoolmate_text'] . '-' . $item['company_position'] . '-' . ($item['mobile'] ?? '');
foreach ($item['course_signs'] as $i) {
$base .= '-' . $i['course']['name'] . '-' . ($i['created_at'] ?? '');
}
$list[] = $base;
}
return implode("、\r\n", $list);
}

@ -12,6 +12,7 @@ use App\Models\CourseContentEvaluationAsk;
use App\Models\CourseContentEvaluationForm;
use App\Models\CustomForm;
use App\Models\CustomFormField;
use App\Models\HistoryCourse;
use App\Models\SupplyDemand;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
@ -49,16 +50,13 @@ class CalendarsController extends BaseController
public function index()
{
$all = \request()->all();
$messages = [
'month.required' => '月份必填',
];
$validator = Validator::make($all, [
'month' => 'required',
], $messages);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$list = Calendar::with('course', 'courseContent')->where('start_time', 'like', $all['month'] . '%')->orderBy('date')->get();
$list = Calendar::with('course', 'courseContent')
->where(function ($query) use ($all) {
if (isset($all['month'])) {
$query->where('start_time', 'like', $all['month'] . '%');
}
})->orderBy('date')
->get();
if (isset($all['is_export']) && $all['is_export'] == 1) {
$list = $list->toArray();
return Excel::download(new CommonExport($list, $all['export_fields'] ?? ''), ($all['file_name'] ?? '') . date('YmdHis') . '.xlsx');
@ -93,7 +91,7 @@ class CalendarsController extends BaseController
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$detail = $this->model->with('courseContent')->find($all['id']);
$detail = $this->model->with(['courseContent', 'historyCourses'])->find($all['id']);
return $this->success($detail);
}
@ -116,6 +114,7 @@ class CalendarsController extends BaseController
* @OA\Parameter(name="is_publish", in="query", @OA\Schema(type="string"), required=true, description="是否向用户发布0否1是"),
* @OA\Parameter(name="address", in="query", @OA\Schema(type="string"), required=true, description="地址"),
* @OA\Parameter(name="days", in="query", @OA\Schema(type="string"), required=true, description="天数"),
* @OA\Parameter(name="history_courses", in="query", @OA\Schema(type="array", @OA\Items(type="object")), required=false, description="历史课程数组每项包含type(课程体系ID), course_name(课程名称), course_type_signs_pass(培养人数未去重), course_type_signs_pass_unique(培养人数去重), course_signs_pass(课程培养人数), start_time(开始时间), end_time(结束时间)"),
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="认证token"),
* @OA\Response(
* response="200",
@ -125,7 +124,39 @@ class CalendarsController extends BaseController
*/
public function save()
{
return parent::save();
$all = \request()->all();
DB::beginTransaction();
try {
if (isset($all['id'])) {
$model = $this->model->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '数据不存在']);
}
} else {
$model = $this->model;
$all['admin_id'] = $this->getUserId();
$all['department_id'] = $this->getUser()->department_id;
}
$original = $model->getOriginal();
$model->fill($all);
$model->save();
// 处理历史课程数据
if (isset($all['history_courses']) && is_array($all['history_courses'])) {
// 删除原有的历史课程数据
$model->historyCourses()->delete();
$model->historyCourses()->createMany($all['history_courses']);
}
DB::commit();
// 记录日志
$this->saveLogs($original, $model);
// 返回带有历史课程的数据
$model->load('historyCourses');
return $this->success($model);
} catch (\Exception $exception) {
DB::rollBack();
return $this->fail([$exception->getCode(), $exception->getMessage()]);
}
}
/**
@ -144,7 +175,33 @@ class CalendarsController extends BaseController
*/
public function destroy()
{
return parent::destroy();
$all = \request()->all();
$messages = [
'id.required' => 'Id必填',
];
$validator = Validator::make($all, [
'id' => 'required'
], $messages);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
DB::beginTransaction();
try {
$model = $this->model->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '数据不存在']);
}
// 删除关联的历史课程数据
$model->historyCourses()->delete();
// 删除日历
$model->delete();
DB::commit();
return $this->success([]);
} catch (\Exception $exception) {
DB::rollBack();
return $this->fail([$exception->getCode(), $exception->getMessage()]);
}
}
}

@ -230,10 +230,13 @@ class OtherController extends CommonController
// 审核通过人数去重
$list['course_signs_pass_unique'] = CourseSign::courseSignsTotalByUnique($start_date, $end_date, 1, $courses->pluck('id'), null);
// 开课场次
$calendar = Calendar::whereIn('course_id', $courses->pluck('id'))->whereBetween('date', [$start_date, $end_date])->get();
$list['course_total'] = $calendar->count();
$calendar = Calendar::where(function ($query) use ($start_date, $end_date) {
$query->whereBetween('start_time', [$start_date, $end_date])
->orWhereBetween('end_time', [$start_date, $end_date]);
})->whereIn('course_id', $courses->pluck('id'));
$list['course_total'] = (clone $calendar)->count();
// 开课天数
$list['course_day_total'] = $calendar->sum('days');
$list['course_day_total'] = (clone $calendar)->sum('days');
// 上市公司数(所有上市公司)
$list['company_market_total'] = Company::companyMarket($start_date, $end_date);
@ -248,6 +251,9 @@ class OtherController extends CommonController
// 入学后上市公司数量(在指定时间范围内报名的学员所在公司中,在入学后上市的公司数量)
$list['company_market_after_enrollment_total'] = CourseSign::companyMarketAfterEnrollment($start_date, $end_date, $course_ids);
// 入学后被投企业数量(在指定时间范围内报名的学员所在公司中,在入学后被投的公司数量)
$list['company_invested_after_enrollment_total'] = CourseSign::companyInvestedAfterEnrollment($start_date, $end_date, $course_ids);
// 元和员工参与企业
$list['company_join_total'] = CourseSign::companyJoin($start_date, $end_date, $course_ids);
// 全市干部参与企业
@ -307,7 +313,7 @@ class OtherController extends CommonController
* tags={"其他"},
* summary="课程统计明细导出",
* description="导出课程统计数据的明细",
* @OA\Parameter(name="export_type", in="query", @OA\Schema(type="string"), required=true, description="导出类型course_signs_invested-被投企业明细, course_signs_total-报名人数明细, course_signs_pass-审核通过人数明细, course_signs_pass_unique-审核通过人数去重明细, courseTypesSum-课程分类明细, areas-区域明细, company_market_total-上市公司明细, ganbu_total-跟班学员明细, company_market_year_total-今年上市公司明细, company_market_after_enrollment_total-入学后上市公司明细, course_total-开课场次明细, course_day_total-开课天数明细, company_join_total-元和员工参与企业明细, company_ganbu_total-全市干部参与企业明细, cover_head_total-苏州头部企业明细, cover_rencai_total-高层次人才明细, cover_stock_total-重点上市公司明细"),
* @OA\Parameter(name="export_type", in="query", @OA\Schema(type="string"), required=true, description="导出类型course_signs_invested-被投企业明细, course_signs_total-报名人数明细, course_signs_pass-审核通过人数明细, course_signs_pass_unique-审核通过人数去重明细, courseTypesSum-课程分类明细, areas-区域明细, company_market_total-上市公司明细, ganbu_total-跟班学员明细, company_market_year_total-今年上市公司明细, company_market_after_enrollment_total-入学后上市公司明细, company_invested_after_enrollment_total-入学后被投企业明细, course_total-开课场次明细, course_day_total-开课天数明细, company_join_total-元和员工参与企业明细, company_ganbu_total-全市干部参与企业明细, cover_head_total-苏州头部企业明细, cover_rencai_total-高层次人才明细, cover_stock_total-重点上市公司明细"),
* @OA\Parameter(name="start_date", in="query", @OA\Schema(type="string"), required=false, description="开始日期"),
* @OA\Parameter(name="end_date", in="query", @OA\Schema(type="string"), required=false, description="结束日期"),
* @OA\Parameter(name="course_type_id", in="query", @OA\Schema(type="string"), required=false, description="课程体系id多个英文逗号"),
@ -629,6 +635,39 @@ class OtherController extends CommonController
$filename = '入学后上市公司明细';
break;
case 'company_invested_after_enrollment_total':
// 入学后被投企业明细 - 使用模型方法
$companiesAfterEnrollment = CourseSign::companyInvestedAfterEnrollment($start_date, $end_date, $course_ids, true);
foreach ($companiesAfterEnrollment as $item) {
$company = $item['company'];
$userNames = collect($item['users'])->pluck('name')->filter()->unique()->implode("\n\r");
$data[] = [
'company_name' => $company->company_name,
'company_legal_representative' => $company->company_legal_representative ?? '',
'company_date' => $company->company_date ?? '',
'invest_date' => $item['invest_date'] ?? '',
'first_sign_date' => $item['first_sign_date'],
'user_names' => $userNames,
'company_address' => $company->company_address ?? '',
'company_city' => $company->company_city ?? '',
'company_area' => $company->company_area ?? '',
];
}
$fields = [
'company_name' => '企业名称',
'company_legal_representative' => '法人',
'company_date' => '成立时间',
'invest_date' => '被投日期',
'first_sign_date' => '首次报名时间',
'user_names' => '学员姓名',
'company_address' => '地址',
'company_city' => '所在城市',
'company_area' => '所在区域',
];
$filename = '入学后被投企业明细';
break;
case 'course_total':
// 开课场次明细 - 与coursesHome算法一致
$calendars = Calendar::whereIn('course_id', $course_ids)

@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Cache;
class Calendar extends SoftDeletesModel
{
protected $appends = ['is_publish_text','type_text'];
protected $appends = ['is_publish_text', 'type_text', 'is_count_days_text', 'is_count_people_text'];
public function getIsPublishTextAttribute()
{
@ -22,6 +22,16 @@ class Calendar extends SoftDeletesModel
return $array[$this->attributes['type']] ?? '';
}
public function getIsCountDaysTextAttribute()
{
return ($this->attributes['is_count_days'] ?? 1) == 1 ? '是' : '否';
}
public function getIsCountPeopleTextAttribute()
{
return ($this->attributes['is_count_people'] ?? 1) == 1 ? '是' : '否';
}
public function course()
{
return $this->hasOne(Course::class, 'id', 'course_id');
@ -32,5 +42,10 @@ class Calendar extends SoftDeletesModel
return $this->hasMany(CourseContent::class, 'id', 'course_content_id');
}
public function historyCourses()
{
return $this->hasMany(HistoryCourse::class, 'calendar_id', 'id');
}
}

@ -223,6 +223,59 @@ class CourseSign extends SoftDeletesModel
}
}
/**
* 入学后被投企业数量(在指定时间范围内报名的学员所在公司中,在入学后被投的公司数量)
* @param string $start_date 开始日期
* @param string $end_date 结束日期
* @param array|null $course_ids 课程ID数组不传则统计所有课程
* @param bool $retList 是否返回列表false返回数量true返回列表
* @return int|array
*/
public static function companyInvestedAfterEnrollment($start_date, $end_date, $course_ids = null, $retList = false)
{
$courseSignsQuery = self::getStudentList($start_date, $end_date, 1, $course_ids);
$courseSignsForInvest = $courseSignsQuery->with('user.company')->get();
$companiesAfterEnrollment = [];
foreach ($courseSignsForInvest as $sign) {
if ($sign->user && $sign->user->company && $sign->user->company->is_yh_invested == 1) {
$signDate = \Carbon\Carbon::parse($sign->created_at)->format('Y-m-d');
// 从 project_users 中获取最早的被投时间
$projectUsers = $sign->user->company->project_users;
$investDate = null;
if (!empty($projectUsers) && is_array($projectUsers)) {
foreach ($projectUsers as $projectUser) {
if (!empty($projectUser['investDate'])) {
if ($investDate === null || $projectUser['investDate'] < $investDate) {
$investDate = $projectUser['investDate'];
}
}
}
}
// 被投时间 >= 报名时间,说明是入学后被投
if ($investDate && $investDate >= $signDate) {
$companyId = $sign->user->company->id;
if (!isset($companiesAfterEnrollment[$companyId])) {
$companiesAfterEnrollment[$companyId] = [
'company' => $sign->user->company,
'first_sign_date' => $signDate,
'invest_date' => $investDate,
'users' => [],
];
}
if ($retList) {
$companiesAfterEnrollment[$companyId]['users'][] = $sign->user;
}
}
}
}
if ($retList) {
return $companiesAfterEnrollment;
} else {
return count($companiesAfterEnrollment);
}
}
/**
* 区域统计
* @param string $start_date 开始日期

@ -9,5 +9,10 @@ class HistoryCourse extends SoftDeletesModel
{
return $this->hasOne(CourseType::class, 'id', 'type');
}
public function calendar()
{
return $this->belongsTo(Calendar::class, 'calendar_id', 'id');
}
}

@ -1,306 +0,0 @@
# 课程统计数据说明文档
## 一、文档说明
本文档用于说明课程统计系统中各项数据的含义和统计方式,帮助您更好地理解和使用统计数据。
---
## 二、如何查询统计数据
### 2.1 查询方式
通过系统后台的"课程统计"功能,您可以查询以下信息:
- **时间范围**:选择需要统计的开始日期和结束日期
- **课程类型**:可以选择全部课程类型,也可以选择特定的课程类型进行统计
### 2.2 统计时间说明
- 统计数据基于**报名记录的创建时间**,而不是课程开课时间
- 例如选择2024年1月1日到2024年12月31日会统计在这个时间段内创建的所有报名记录
---
## 三、核心统计数据说明
### 3.1 被投企业数
**含义**:在统计时间段内,有多少家元禾已投资的企业有员工报名参加了课程。
**统计方式**
- 统计所有在指定时间段内报名的用户
- 查看这些用户所在的公司
- 统计其中被元禾投资的公司数量
- **重要**:如果一家公司有多个员工报名,这家公司只统计一次
**使用场景**:了解元禾投资企业的参与情况
---
### 3.2 报名人数
**含义**:在统计时间段内,总共有多少条报名记录。
**统计方式**
- 统计所有在指定时间段内创建的报名记录
- 包括所有状态的报名(待审核、审核通过、审核不通过等)
- 不包括已取消和主动放弃的报名
- **重要**:如果同一个人报名了多个课程,会按报名次数分别计算
**使用场景**:了解课程的整体报名热度
**举例**
- 张三报名了3个课程 → 统计为3人
- 李四报名了1个课程 → 统计为1人
- 总计4人
---
### 3.3 审核通过人数
**含义**:在统计时间段内,有多少条报名记录通过了审核。
**统计方式**
- 只统计状态为"审核通过"的报名记录
- 不包括已取消和主动放弃的报名
- **重要**:如果同一个人报名了多个课程且都通过了,会按通过次数分别计算
**使用场景**:了解实际可以参加课程的人数
**举例**
- 张三报名了3个课程都通过了 → 统计为3人
- 李四报名了1个课程通过了 → 统计为1人
- 总计4人
---
### 3.4 审核通过人数(去重)
**含义**:在统计时间段内,有多少个不同的用户通过了审核。
**统计方式**
- 只统计状态为"审核通过"的报名记录
- 根据用户的手机号进行去重
- 如果同一个手机号报名了多个课程,只统计一次
- **重要**:这是真实的培养人数,不会重复计算同一个人
**使用场景**:了解实际培养了多少个不同的学员
**举例**
- 张三手机号13800138000报名了3个课程都通过了 → 统计为1人
- 李四手机号13900139000报名了1个课程通过了 → 统计为1人
- 总计2人去重后
---
### 3.5 开课场次
**含义**:在统计时间段内,总共开了多少场课程。
**统计方式**
- 统计指定课程类型下,在指定时间范围内的所有开课记录
- 统计数据源来自于课程日历上的数据
- **重要**:统计的是开课场次,不是课程数量
**使用场景**:了解课程开展的频率和规模
---
### 3.6 开课天数
**含义**:在统计时间段内,所有课程总共开了多少天。
**统计方式**
- 对每场课程计算开课天数(从开始日期到结束日期)
- 包含开始日期和结束日期
- 将所有场次的天数加起来
- 统计数据源来自于课程日历上的数据
**使用场景**:了解课程的总时长
**举例**
- 某场课程从2024年1月1日到1月3日 → 3天包含1日、2日、3日
- 某场课程从2024年2月10日到2月12日 → 3天
- 总计6天
---
## 四、课程分类明细统计说明
### 4.1 统计内容
系统会按照每个课程类型,详细统计以下信息:
#### 课程类型名称
显示课程体系的名称,例如:"高研班"、"初创班"等。
#### 培养人数(未去重,按课程类型)
**含义**:该课程类型下所有课程的审核通过报名人数总和。
**说明**
- 如果同一个学员在该课程类型下报名了多个课程,会按报名次数分别计算
- 例如:张三在"高研班"类型下报名了2个课程都通过了 → 统计为2人
#### 培养人数(去重,按课程类型)
**含义**:该课程类型下有多少个不同的学员通过了审核。
**说明**
- 根据学员的手机号去重
- 同一个手机号在该课程类型下只统计一次
- 例如张三手机号13800138000在"高研班"类型下报名了2个课程都通过了 → 统计为1人
#### 课程名称
显示具体课程的名称,例如:"2024年第一期高研班"。
#### 培养人数(按单个课程)
**含义**:该单个课程的审核通过报名人数。
**说明**
- 只统计该课程的审核通过人数
- 不包括其他课程的数据
---
## 五、区域明细统计说明
### 5.1 统计内容
系统会按照每个区域,详细统计以下信息:
#### 区域名称
显示区域名称,例如:"工业园区"、"高新区"、"相城区"等。
**特殊区域**"苏州市外"表示不在苏州所有区域内的用户。
#### 审核通过人数(未去重,按区域)
**含义**:该区域有多少条审核通过的报名记录。
**说明**
- 根据学员所在公司的区域进行统计
- 如果同一个学员在该区域报名了多个课程,会按报名次数分别计算
- 例如张三公司位于工业园区报名了2个课程都通过了 → 统计为2人
#### 审核通过人数(去重,按区域)
**含义**:该区域有多少个不同的学员通过了审核。
**说明**
- 根据学员的手机号去重
- 同一个手机号在该区域只统计一次
- 例如张三手机号13800138000公司位于工业园区报名了2个课程都通过了 → 统计为1人
---
## 七、数据关系说明
### 7.1 人数关系
通常情况下,数据之间存在以下关系:
**报名人数** ≥ **审核通过人数** ≥ **审核通过人数(去重)**
**解释**
- 报名人数最多,因为包括所有状态的报名
- 审核通过人数次之,因为只包括审核通过的
- 去重人数最少,因为同一个学员只统计一次
**举例**
- 报名人数500人包括待审核、审核通过、审核不通过等
- 审核通过人数450人只包括审核通过的
- 审核通过人数去重380人同一个学员只统计一次
### 7.2 区域汇总与全局去重的关系
**区域汇总** 可能大于 **全局去重人数**
**原因**
- 区域汇总时,每个区域是分别统计的
- 而全局去重是按所有区域统一去重的
---
## 八、报名状态说明
### 8.1 报名状态类型
报名记录有以下几种状态:
| 状态名称 | 说明 | 是否参与统计 |
|---------|------|------------|
| 待审核 | 报名已提交,等待审核 | 参与"报名人数"统计,不参与"审核通过人数"统计 |
| 审核通过 | 报名已通过审核,可以参加课程 | 参与所有统计 |
| 审核不通过 | 报名未通过审核 | 参与"报名人数"统计,不参与"审核通过人数"统计 |
| 备选 | 报名作为备选 | 参与"报名人数"统计,不参与"审核通过人数"统计 |
| 已取消 | 报名已取消 | 不参与任何统计 |
| 主动放弃 | 学员主动放弃报名 | 不参与任何统计 |
| 黑名单 | 学员在黑名单中 | 参与"报名人数"统计,不参与"审核通过人数"统计 |
### 8.2 统计规则
- **报名人数**:统计除"已取消"和"主动放弃"外的所有状态
- **审核通过人数**:只统计"审核通过"状态的记录
- **所有统计**:都不包括"已取消"和"主动放弃"的记录
---
## 九、常见问题解答
### Q1: 为什么"报名人数"比"审核通过人数"多?
**A**: 因为"报名人数"包括所有状态的报名记录(待审核、审核通过、审核不通过、备选等),而"审核通过人数"只包括审核通过的记录。所以报名人数会更多。
### Q2: 为什么"审核通过人数"比"审核通过人数(去重)"多?
**A**: 因为"审核通过人数"是报名记录数,如果同一个学员报名了多个课程,会按报名次数分别计算。而"审核通过人数(去重)"是按学员手机号去重的,同一个学员只统计一次。
### Q4: "被投企业数"是如何统计的?
**A**: 先统计所有报名的学员,然后查看这些学员所在的公司,统计其中被元禾投资的公司数量。如果一家公司有多个员工报名,这家公司只统计一次。
### Q5: "开课天数"是如何计算的?
**A**: 对每场课程计算从开始日期到结束日期的天数包含开始日期和结束日期然后将所有场次的天数加起来。例如某场课程从1月1日到1月3日算3天。
### Q6: 统计时间范围是什么意思?
**A**: 统计时间范围是指报名记录的创建时间不是课程的开课时间。例如选择2024年1月1日到12月31日会统计在这个时间段内创建的所有报名记录即使这些课程可能在2025年才开课。
### Q7: 为什么选择不同的课程类型,统计数据会不同?
**A**: 因为统计数据是基于选择的课程类型进行筛选的。如果选择全部课程类型,会统计所有课程的数据;如果选择特定的课程类型,只统计该类型下的课程数据。
---
## 十、使用建议
### 10.1 查看整体情况
- 使用"报名人数"了解课程的整体报名热度
- 使用"审核通过人数(去重)"了解实际培养了多少个不同的学员
- 使用"开课场次"和"开课天数"了解课程开展的频率和时长
### 10.2 查看分类情况
- 使用"课程分类明细统计"了解不同课程类型的培养情况
- 使用"区域明细统计"了解不同区域的参与情况
### 10.3 数据对比
- 对比"报名人数"和"审核通过人数",了解审核通过率
- 对比"审核通过人数"和"审核通过人数(去重)",了解学员的参与深度
- 对比不同课程类型的数据,了解各类课程的受欢迎程度
---
## 十一、注意事项
1. **时间范围选择**:统计数据基于报名记录的创建时间,不是课程开课时间
2. **去重逻辑**:去重统计使用学员的手机号作为依据
3. **区域统计**:区域统计基于学员所在公司的区域
4. **数据关系**:不同统计数据之间可能存在包含关系,请注意理解
5. **汇总数据**:区域汇总可能包含跨区域的重复学员,请注意区分

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('history_courses', function (Blueprint $table) {
$table->unsignedBigInteger('calendar_id')->nullable()->after('id')->comment('日历ID');
$table->index('calendar_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('history_courses', function (Blueprint $table) {
$table->dropIndex(['calendar_id']);
$table->dropColumn('calendar_id');
});
}
};

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('calendars', function (Blueprint $table) {
$table->tinyInteger('is_count_days')->default(1)->comment('是否统计天数 0否 1是');
$table->tinyInteger('is_count_people')->default(1)->comment('是否统计人数 0否 1是');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('calendars', function (Blueprint $table) {
$table->dropColumn(['is_count_days', 'is_count_people']);
});
}
};

@ -0,0 +1,300 @@
# 课程统计系统数据说明文档
## 🎯 一、基础统计数据
### 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人
---
## 🔍 四、统计的时间范围
所有统计数据都基于您选择的时间范围:
- **开始日期**:统计从这个日期开始的报名或开课
- **结束日期**:统计到这个日期为止的报名或开课
**重要说明**
- 对于报名人数:看的是学员的报名时间是否在这个范围内
- 对于开课场次:看的是课程的实际开课日期是否在这个范围内
- 对于企业统计:看的是学员的报名时间是否在这个范围内
Loading…
Cancel
Save