diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index 2750550..3b44df9 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -293,9 +293,9 @@ class OtherController extends CommonController $courseTypesSum[] = [ 'course_type' => $courseType->name, // 培养人数 - 'course_type_signs_pass' => CourseSign::courseSignsTotal($start_date, $end_date, 1, $courses2->pluck('id')), + 'course_type_signs_pass' => CourseSign::courseSignsTotal($start_date, $end_date, 1, $courses2->pluck('id'), false, $courseType), // 去重培养人数 - 'course_type_signs_pass_unique' => CourseSign::courseSignsTotalByUnique($start_date, $end_date, 1, $courses2->pluck('id'), null), + 'course_type_signs_pass_unique' => CourseSign::courseSignsTotalByUnique($start_date, $end_date, 1, $courses2->pluck('id'), false, $courseType), 'course_name' => $course->name, 'course_signs_pass' => CourseSign::courseSignsTotal($start_date, $end_date, 1, [$course->id]), // 跟班学员数量 diff --git a/app/Models/CourseSign.php b/app/Models/CourseSign.php index 08eb36a..999684e 100755 --- a/app/Models/CourseSign.php +++ b/app/Models/CourseSign.php @@ -100,7 +100,7 @@ class CourseSign extends SoftDeletesModel /** * 指定时间内的报名信息(未去重) */ - public static function courseSignsTotal($start_date, $end_date, $status = null, $course_ids = null, $retList = false) + public static function courseSignsTotal($start_date, $end_date, $status = null, $course_ids = null, $retList = false, $courseType = null) { $totalQuery = self::getStudentList($start_date, $end_date, $status, $course_ids); if ($retList) { @@ -112,12 +112,14 @@ class CourseSign extends SoftDeletesModel // 历史数据 $historyTotal = HistoryCourse::whereHas('calendar', function ($query) { $query->where('is_count_people', 1); + })->where('typeDetail', function ($query) { + $query->where('is_history', 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 = $courseType ?? request('course_type_id'); if ($course_type_id) { $course_type_id = explode(',', $course_type_id); $query->whereIn('type', $course_type_id); @@ -131,7 +133,7 @@ class CourseSign extends SoftDeletesModel /** * 指定时间内的报名信息(去重) */ - public static function courseSignsTotalByUnique($start_date, $end_date, $status = null, $course_ids = null, $retList = false) + public static function courseSignsTotalByUnique($start_date, $end_date, $status = null, $course_ids = null, $retList = false, $courseType = null) { $totalQuery = self::getStudentList($start_date, $end_date, $status, $course_ids); $user = User::whereIn('id', $totalQuery->get()->pluck('user_id'))->groupBy('mobile')->get(); @@ -143,12 +145,14 @@ class CourseSign extends SoftDeletesModel // 历史数据 $historyTotal = HistoryCourse::whereHas('calendar', function ($query) { $query->where('is_count_people', 1); + })->where('typeDetail', function ($query) { + $query->where('is_history', 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 = $courseType ?? request('course_type_id'); if ($course_type_id) { $course_type_id = explode(',', $course_type_id); $query->whereIn('type', $course_type_id); diff --git a/user_statistics_config_json结构说明.md b/user_statistics_config_json结构说明.md deleted file mode 100644 index a42822e..0000000 --- a/user_statistics_config_json结构说明.md +++ /dev/null @@ -1,550 +0,0 @@ -# 用户统计数据配置 JSON 结构说明 - -## 概述 - -`user_statistics_configs` 表的 `config_json` 字段用于存储动态统计配置,包含三个主要部分:数据来源、条件设置、统计方式。 - -## JSON 结构 - -```json -{ - "data_source": { - "main_model": "user|company|course_sign|course|course_type", - "relations": ["user", "company", "course_sign", "course", "course_type"] - }, - "conditions": { - "logic": "and|or", - "items": [ - { - "key": "字段名", - "operator": "操作类型", - "value": "值" - } - ] - }, - "statistics": { - "type": "sum|max|min|count|count_distinct", - "field": "统计字段(sum/max/min 时使用,可选)", - "distinct_field": "去重字段(count_distinct 时使用,可选)", - "group_by": "分组字段(可选,不设置则不分组)", - "order_by": { - "field": "排序字段(可选)", - "direction": "asc|desc" - } - } -} -``` - ---- - -## 一、数据来源(data_source) - -### 1.1 主模型(main_model) - -**说明**:指定统计数据的主要来源模型。 - -**可选值**: -- `user` - 用户模型 -- `company` - 公司模型 -- `course_sign` - 报名模型 -- `course` - 课程模型 -- `course_type` - 课程分类模型 - -**示例**: -```json -{ - "main_model": "user" -} -``` - -### 1.2 关联模型(relations) - -**说明**:指定需要关联的其他模型,可以关联多个模型。 - -**可选值**(数组): -- `user` - 用户模型 -- `company` - 公司模型 -- `course_sign` - 报名模型 -- `course` - 课程模型 -- `course_type` - 课程分类模型 - -**注意**: -- 关联模型不能包含主模型本身 -- 可以关联多个模型 -- 数组可以为空 - -**示例**: -```json -{ - "relations": ["company", "course_sign"] -} -``` - ---- - -## 二、条件设置(conditions) - -### 2.1 逻辑关系(logic) - -**说明**:指定多个条件之间的逻辑关系。 - -**可选值**: -- `and` - 所有条件都必须满足(AND) -- `or` - 至少一个条件满足(OR) - -**示例**: -```json -{ - "logic": "and" -} -``` - -### 2.2 条件项(items) - -**说明**:条件数组,每个条件包含键名、操作类型和值。 - -**条件项结构**: -```json -{ - "key": "字段名", - "operator": "操作类型", - "value": "值" -} -``` - -#### 字段说明 - -- **key**(字符串):要查询的字段名 - - 可以是主模型的字段 - - 可以是关联模型的字段(使用点号分隔,如 `company.name`) - -- **operator**(字符串):操作类型 - - `eq` - 等于 - - `neq` - 不等于 - - `gt` - 大于 - - `egt` - 大于等于 - - `lt` - 小于 - - `elt` - 小于等于 - - `like` - 模糊匹配 - - `notlike` - 不匹配 - - `in` - 在范围内(值为逗号分隔的字符串) - - `notin` - 不在范围内 - - `between` - 在范围内(值为逗号分隔的两个值) - - `notbetween` - 不在范围内 - - `isnull` - 为空(value 可省略) - - `isnotnull` - 不为空(value 可省略) - -- **value**(字符串/数字/数组):条件值 - - 根据操作类型不同,值的形式也不同 - - `in` 操作:值为逗号分隔的字符串,如 `"1,2,3"` - - `between` 操作:值为逗号分隔的两个值,如 `"2024-01-01,2024-12-31"` - - `isnull` 和 `isnotnull` 操作:value 可以省略 - -**示例**: - -```json -{ - "logic": "and", - "items": [ - { - "key": "is_schoolmate", - "operator": "eq", - "value": "1" - }, - { - "key": "company.is_yh_invested", - "operator": "eq", - "value": "1" - }, - { - "key": "created_at", - "operator": "between", - "value": "2024-01-01,2024-12-31" - } - ] -} -``` - ---- - -## 三、统计方式(statistics) - -### 3.1 统计类型(type) - -**说明**:指定统计的方式。 - -**可选值**: -- `sum` - 求和(需要指定 `field` 字段) -- `max` - 最大值(需要指定 `field` 字段) -- `min` - 最小值(需要指定 `field` 字段) -- `count` - 统计总数量(不需要指定 `field` 字段) -- `count_distinct` - 统计去重数量(需要指定 `distinct_field` 字段) - -**示例**: -```json -{ - "type": "count" -} -``` - -### 3.2 统计字段(field) - -**说明**:当统计类型为 `sum`、`max` 或 `min` 时,指定要统计的字段名。 - -**注意**: -- `type` 为 `sum`、`max`、`min` 时必须指定 `field` -- `type` 为 `count` 时可以省略 `field` -- 可以是主模型的字段 -- 可以是关联模型的字段(使用点号分隔,如 `company.company_fund`) - -**示例**: -```json -{ - "type": "sum", - "field": "company_fund" -} -``` - -```json -{ - "type": "max", - "field": "company.company_fund" -} -``` - -```json -{ - "type": "min", - "field": "created_at" -} -``` - -### 3.3 去重字段(distinct_field) - -**说明**:当统计类型为 `count_distinct` 时,指定要去重的字段名。 - -**注意**: -- `type` 为 `count_distinct` 时必须指定 `distinct_field` -- 可以是主模型的字段 -- 可以是关联模型的字段(使用点号分隔,如 `user.mobile`) -- **可以与 `group_by` 同时使用**:可以按某个字段分组,然后统计每个分组的去重数量 - -**示例1:不分组去重统计** -```json -{ - "type": "count_distinct", - "distinct_field": "mobile" -} -``` - -**示例2:关联模型字段去重** -```json -{ - "type": "count_distinct", - "distinct_field": "user.mobile" -} -``` - -**示例3:分组 + 去重统计(组合使用)** -```json -{ - "type": "count_distinct", - "distinct_field": "user.mobile", - "group_by": "course.type" -} -``` - -### 3.4 分组字段(group_by) - -**说明**:指定按哪个字段进行分组统计。这是一个可选配置,可以选择不分组或选择具体的分组字段。 - -**配置选项**: -- **不分组**:不设置 `group_by` 字段,或设置为 `null`,将返回所有符合条件的记录列表 -- **按字段分组**:设置具体的分组字段,将按该字段进行分组统计 - -**分组字段格式**: -- 可以是主模型的字段(如:`company_area`) -- 可以是关联模型的字段(使用点号分隔,如 `company.company_area`) - -**示例1:不分组统计** -```json -{ - "statistics": { - "type": "count" - // 不设置 group_by,表示不分组 - } -} -``` - -**示例2:按主模型字段分组** -```json -{ - "statistics": { - "type": "count", - "group_by": "company_area" - } -} -``` - -**示例3:按关联模型字段分组** -```json -{ - "statistics": { - "type": "count", - "group_by": "company.company_area" - } -} -``` - -**示例4:分组 + 去重统计(组合使用)** -```json -{ - "statistics": { - "type": "count_distinct", - "distinct_field": "user.mobile", - "group_by": "course.type" - } -} -``` - -### 3.4 排序方式(order_by) - -**说明**:指定结果的排序方式。 - -**结构**: -```json -{ - "field": "排序字段", - "direction": "asc|desc" -} -``` - -**字段说明**: -- **field**(字符串):排序字段名 - - 可以是主模型的字段 - - 可以是关联模型的字段(使用点号分隔) - - 可以是统计结果字段(如 `total`、`count`) - -- **direction**(字符串):排序方向 - - `asc` - 升序 - - `desc` - 降序 - -**示例**: -```json -{ - "order_by": { - "field": "total", - "direction": "desc" - } -} -``` - ---- - -## 完整示例 - -### 示例1:统计各区域的校友人数 - -```json -{ - "data_source": { - "main_model": "user", - "relations": ["company"] - }, - "conditions": { - "logic": "and", - "items": [ - { - "key": "is_schoolmate", - "operator": "eq", - "value": "1" - }, - { - "key": "created_at", - "operator": "between", - "value": "2024-01-01,2024-12-31" - } - ] - }, - "statistics": { - "type": "count", - "group_by": "company.company_area", - "order_by": { - "field": "count", - "direction": "desc" - } - } -} -``` - -### 示例2:统计各课程类型的报名人数 - -```json -{ - "data_source": { - "main_model": "course_sign", - "relations": ["course", "user"] - }, - "conditions": { - "logic": "and", - "items": [ - { - "key": "status", - "operator": "eq", - "value": "1" - }, - { - "key": "created_at", - "operator": "between", - "value": "2024-01-01,2024-12-31" - } - ] - }, - "statistics": { - "type": "count", - "group_by": "course.type", - "order_by": { - "field": "count", - "direction": "desc" - } - } -} -``` - -### 示例3:统计各公司的融资总额 - -```json -{ - "data_source": { - "main_model": "company", - "relations": ["user"] - }, - "conditions": { - "logic": "and", - "items": [ - { - "key": "is_yh_invested", - "operator": "eq", - "value": "1" - }, - { - "key": "company_fund", - "operator": "isnotnull" - } - ] - }, - "statistics": { - "type": "sum", - "field": "company_fund", - "group_by": "company_area", - "order_by": { - "field": "total", - "direction": "desc" - } - } -} -``` - -### 示例4:统计审核通过或待审核的报名人数 - -```json -{ - "data_source": { - "main_model": "course_sign", - "relations": [] - }, - "conditions": { - "logic": "or", - "items": [ - { - "key": "status", - "operator": "eq", - "value": "0" - }, - { - "key": "status", - "operator": "eq", - "value": "1" - } - ] - }, - "statistics": { - "type": "count", - "order_by": { - "field": "created_at", - "direction": "desc" - } - } -} -``` - -### 示例5:统计各课程类型的去重培养人数(按手机号去重) - -```json -{ - "data_source": { - "main_model": "course_sign", - "relations": ["user", "course"] - }, - "conditions": { - "logic": "and", - "items": [ - { - "key": "status", - "operator": "eq", - "value": "1" - }, - { - "key": "created_at", - "operator": "between", - "value": "2020-01-01," . date('Y-m-d') - } - ] - }, - "statistics": { - "type": "count_distinct", - "distinct_field": "user.mobile", - "group_by": "course.type", - "order_by": { - "field": "total", - "direction": "desc" - } - } -} -``` - ---- - -## 注意事项 - -1. **字段引用**: - - 主模型字段直接使用字段名 - - 关联模型字段使用 `模型名.字段名` 格式 - - 例如:`company.name`、`course.type` - -2. **数据类型**: - - 所有值在 JSON 中都存储为字符串 - - 系统会根据字段类型自动转换 - -3. **条件逻辑**: - - `and` 表示所有条件都必须满足 - - `or` 表示至少一个条件满足 - - 条件数组可以为空(表示无条件) - -4. **统计字段**: - - `sum` 类型必须指定 `field` - - `count` 类型不需要 `field` - - 分组字段可以为空(表示不分组) - -5. **排序字段**: - - 可以按任意字段排序 - - 可以按统计结果字段排序(如 `total`、`count`) - - 排序字段可以为空(使用默认排序) - ---- - -## 文档版本 - -- **创建日期**:2025-11-19 -- **最后更新**:2025-11-19 -