master
cody 2 weeks ago
parent 64c7ea07f7
commit df3a614bd8

@ -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]),
// 跟班学员数量

@ -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);

@ -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
Loading…
Cancel
Save