You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

820 lines
37 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Http\Controllers\Admin;
use App\Exports\BaseExport;
use App\Helpers\ResponseCode;
use App\Models\AppointmentType;
use App\Models\Article;
use App\Models\Book;
use App\Models\Calendar;
use App\Models\Company;
use App\Models\CourseContentEvaluationAsk;
use App\Models\CourseContentEvaluationForm;
use App\Models\CustomForm;
use App\Models\CustomFormField;
use App\Models\EmailTemplate;
use App\Models\StatisticsConfig;
use App\Models\SupplyDemand;
use App\Models\TimeEvent;
use App\Models\User;
use App\Models\CourseSign;
use App\Models\Course;
use App\Models\CourseType;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel;
use Rap2hpoutre\FastExcel\FastExcel;
class StatisticsConfigController extends BaseController
{
/**
* 构造函数
*/
public function __construct()
{
parent::__construct(new StatisticsConfig());
}
/**
* @OA\Get(
* path="/api/admin/statistics-config/index",
* tags={"动态统计"},
* summary="列表",
* description="",
* @OA\Parameter(name="is_export", in="query", @OA\Schema(type="string"), required=false, description="是否导出0否1是"),
* @OA\Parameter(name="export_fields", in="query", @OA\Schema(type="string"), required=false, description="需要导出的字段数组"),
* @OA\Parameter(name="filter", in="query", @OA\Schema(type="string"), required=false, description="查询条件。数组"),
* @OA\Parameter(name="show_relation", in="query", @OA\Schema(type="string"), required=false, description="需要输出的关联关系数组包括teachercourseSettingscoursePeriods"),
* @OA\Parameter(name="page_size", in="query", @OA\Schema(type="string"), required=false, description="每页显示的条数"),
* @OA\Parameter(name="page", in="query", @OA\Schema(type="string"), required=false, description="页码"),
* @OA\Parameter(name="sort_name", in="query", @OA\Schema(type="string"), required=false, description="排序字段名字"),
* @OA\Parameter(name="sort_type", in="query", @OA\Schema(type="string"), required=false, description="排序类型"),
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"),
* @OA\Response(
* response="200",
* description="暂无"
* )
* )
*/
public function index()
{
$all = request()->all();
$list = $this->model->where(function ($query) use ($all) {
if (isset($all['filter']) && !empty($all['filter'])) {
foreach ($all['filter'] as $condition) {
$key = $condition['key'] ?? null;
$op = $condition['op'] ?? null;
$value = $condition['value'] ?? null;
if (!isset($key) || !isset($op) || !isset($value)) {
continue;
}
// 等于
if ($op == 'eq') {
$query->where($key, $value);
}
// 不等于
if ($op == 'neq') {
$query->where($key, '!=', $value);
}
// 大于
if ($op == 'gt') {
$query->where($key, '>', $value);
}
// 大于等于
if ($op == 'egt') {
$query->where($key, '>=', $value);
}
// 小于
if ($op == 'lt') {
$query->where($key, '<', $value);
}
// 小于等于
if ($op == 'elt') {
$query->where($key, '<=', $value);
}
// 模糊搜索
if ($op == 'like') {
$query->where($key, 'like', '%' . $value . '%');
}
// 否定模糊搜索
if ($op == 'notlike') {
$query->where($key, 'not like', '%' . $value . '%');
}
// 范围搜索
if ($op == 'range') {
list($from, $to) = explode(',', $value);
if (empty($from) || empty($to)) {
continue;
}
$query->whereBetween($key, [$from, $to]);
}
}
}
})->orderBy($all['sort_name'] ?? 'id', $all['sort_type'] ?? 'desc');
if (isset($all['is_export']) && !empty($all['is_export'])) {
$list = $list->get()->toArray();
$export_fields = $all['export_fields'] ?? [];
// 导出文件名字
$tableName = $this->model->getTable();
$filename = (new CustomForm())->getTableComment($tableName);
return Excel::download(new BaseExport($export_fields, $list, $tableName), $filename . date('YmdHis') . '.xlsx');
} else {
// 输出
$list = $list->paginate($all['page_size'] ?? 20);
}
return $this->success($list);
}
/**
* @OA\Get(
* path="/api/admin/statistics-config/show",
* tags={"动态统计"},
* summary="详情",
* description="",
* @OA\Parameter(name="id", in="query", @OA\Schema(type="string"), required=true, description="id"),
* @OA\Parameter(name="show_relation", in="query", @OA\Schema(type="string"), required=false, description="需要输出的关联关系数组,填写输出指定数据"),
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"),
* @OA\Response(
* response="200",
* description="暂无"
* )
* )
*/
public function show()
{
$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())]);
}
$detail = $this->model->find($all['id']);
return $this->success($detail);
}
/**
* @OA\Post(
* path="/api/admin/statistics-config/save",
* tags={"动态统计"},
* summary="保存统计数据配置",
* description="根据传入的id决定是更新现有配置还是新增新的配置。",
* @OA\Parameter(name="id", in="query", @OA\Schema(type="integer"), required=false, description="配置ID存在则更新不存在则新增"),
* @OA\Parameter(name="name", in="query", @OA\Schema(type="string"), required=true, description="名字"),
* @OA\Parameter(name="key", in="query", @OA\Schema(type="string"), required=false, description="标识key"),
* @OA\Parameter(name="decimal_places", in="query", @OA\Schema(type="integer"), required=false, description="小数点位数默认0"),
* @OA\Parameter(name="description", in="query", @OA\Schema(type="string"), required=false, description="描述"),
* @OA\Parameter(name="config_json", in="query", @OA\Schema(type="string"), required=false, description="配置json包含数据来源、条件设置、统计方式等配置详见配置文档"),
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="认证token"),
* @OA\Response(
* response="200",
* description="操作成功"
* )
* )
*/
public function 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();
DB::commit();
return $this->success($model);
} catch (\Exception $exception) {
DB::rollBack();
return $this->fail([$exception->getCode(), $exception->getMessage()]);
}
}
/**
* @OA\Get(
* path="/api/admin/statistics-config/destroy",
* tags={"动态统计"},
* summary="删除",
* description="",
* @OA\Parameter(name="id", in="query", @OA\Schema(type="string"), required=true, description="id"),
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"),
* @OA\Response(
* response="200",
* description="暂无"
* )
* )
*/
public function destroy()
{
return parent::destroy();
}
/**
* @OA\Get(
* path="/api/admin/statistics-config/calculate",
* tags={"动态统计"},
* summary="根据配置key执行统计",
* description="根据配置的key获取对应的统计配置然后根据config_json的配置执行统计查询返回数据列表和统计结果",
* @OA\Parameter(name="key", in="query", @OA\Schema(type="string"), required=true, description="配置的key标识"),
* @OA\Parameter(name="page", in="query", @OA\Schema(type="integer"), required=false, description="页码默认1"),
* @OA\Parameter(name="page_size", in="query", @OA\Schema(type="integer"), required=false, description="每页显示的条数默认10"),
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="认证token"),
* @OA\Response(
* response="200",
* description="返回统计数据"
* )
* )
*/
public function calculate()
{
$all = \request()->all();
$messages = [
'key.required' => 'key必填',
];
$validator = Validator::make($all, [
'key' => 'required'
], $messages);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
// 获取分页参数
$page = isset($all['page']) ? (int) $all['page'] : 1;
$pageSize = isset($all['page_size']) ? (int) $all['page_size'] : 10;
// 根据key查找配置
$config = $this->model->where('key', $all['key'])->first();
if (empty($config)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '配置不存在']);
}
$configJson = $config->config_json;
if (empty($configJson)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '配置数据为空']);
}
// 如果 config_json 是字符串,尝试解析
if (is_string($configJson)) {
$configJson = json_decode($configJson, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '配置 JSON 格式错误:' . json_last_error_msg()]);
}
}
try {
// 检查数据结构
if (!isset($configJson['data_source'])) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '配置数据缺少 data_source 字段,当前配置键:' . implode(', ', array_keys($configJson))]);
}
// 获取主模型
$mainModelName = $configJson['data_source']['main_model'] ?? '';
if (empty($mainModelName)) {
$dataSourceKeys = isset($configJson['data_source']) ? implode(', ', array_keys($configJson['data_source'])) : '无';
return $this->fail([ResponseCode::ERROR_BUSINESS, '主模型名称未配置data_source 中的键:' . $dataSourceKeys]);
}
$mainModel = $this->getModel($mainModelName);
if (empty($mainModel)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '主模型不存在:' . $mainModelName . '可用的模型user, company, course_sign, course, course_type']);
}
$query = $mainModel::query();
// 获取主模型表名
$tableName = $mainModel::make()->getTable();
// 加载关联模型
$relations = $configJson['data_source']['relations'] ?? [];
if (!empty($relations)) {
foreach ($relations as $relation) {
$query->with($relation);
}
}
// 应用条件
$conditions = $configJson['conditions'] ?? [];
if (!empty($conditions['items'])) {
$logic = $conditions['logic'] ?? 'and';
if ($logic === 'or') {
$query->where(function ($q) use ($conditions, $tableName) {
foreach ($conditions['items'] as $index => $item) {
if ($index === 0) {
$this->applyCondition($q, $item, 'and', 0, $tableName);
} else {
$q->orWhere(function ($subQ) use ($item, $tableName) {
$this->applyCondition($subQ, $item, 'and', 0, $tableName);
});
}
}
});
} else {
foreach ($conditions['items'] as $item) {
$this->applyCondition($query, $item, 'and', 0, $tableName);
}
}
}
// 执行统计
$statistics = $configJson['statistics'] ?? [];
$statisticsType = $statistics['type'] ?? 'count';
$groupBy = $statistics['group_by'] ?? null;
// 确保空字符串也被视为不分组
$groupBy = !empty($groupBy) ? $groupBy : null;
// 保存原始查询用于获取列表
$listQuery = clone $query;
if ($groupBy) {
// 分组统计
$groupParts = explode('.', $groupBy);
if (count($groupParts) > 1) {
// 关联模型字段分组,需要 join
$relationName = $groupParts[0];
$fieldName = $groupParts[1];
$relationModel = $this->getRelationModel($mainModel, $relationName);
if ($relationModel) {
$relationTable = $relationModel::make()->getTable();
$relationKey = $this->getRelationKey($mainModel, $relationName);
$query->join($relationTable, $tableName . '.' . $relationKey, '=', $relationTable . '.id');
$selectFields = [$relationTable . '.' . $fieldName . ' as group_value'];
} else {
$selectFields = [$groupBy . ' as group_value'];
}
} else {
// 主模型字段分组
$selectFields = [$tableName . '.' . $groupBy . ' as group_value'];
}
// 根据统计类型构建 SQL
$statisticsField = $statistics['field'] ?? null;
if ($statisticsType === 'sum' && $statisticsField) {
// 处理关联模型字段
$fieldParts = explode('.', $statisticsField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
// 如果还没有 join需要 join
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$selectFields[] = DB::raw('SUM(' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
} else {
$selectFields[] = DB::raw('SUM(' . $tableName . '.' . $statisticsField . ') as total');
}
} else {
$selectFields[] = DB::raw('SUM(' . $tableName . '.' . $statisticsField . ') as total');
}
} elseif ($statisticsType === 'max' && $statisticsField) {
// 处理关联模型字段
$fieldParts = explode('.', $statisticsField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
// 如果还没有 join需要 join
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$selectFields[] = DB::raw('MAX(' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
} else {
$selectFields[] = DB::raw('MAX(' . $tableName . '.' . $statisticsField . ') as total');
}
} else {
$selectFields[] = DB::raw('MAX(' . $tableName . '.' . $statisticsField . ') as total');
}
} elseif ($statisticsType === 'min' && $statisticsField) {
// 处理关联模型字段
$fieldParts = explode('.', $statisticsField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
// 如果还没有 join需要 join
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$selectFields[] = DB::raw('MIN(' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
} else {
$selectFields[] = DB::raw('MIN(' . $tableName . '.' . $statisticsField . ') as total');
}
} else {
$selectFields[] = DB::raw('MIN(' . $tableName . '.' . $statisticsField . ') as total');
}
} elseif ($statisticsType === 'count_distinct' && isset($statistics['distinct_field'])) {
// 去重数量统计
$distinctField = $statistics['distinct_field'];
// 处理关联模型字段
$fieldParts = explode('.', $distinctField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
// 如果还没有 join需要 join
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$selectFields[] = DB::raw('COUNT(DISTINCT ' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
} else {
$selectFields[] = DB::raw('COUNT(DISTINCT ' . $tableName . '.' . $distinctField . ') as total');
}
} else {
$selectFields[] = DB::raw('COUNT(DISTINCT ' . $tableName . '.' . $distinctField . ') as total');
}
} else {
// count 统计总数量
$selectFields[] = DB::raw('COUNT(*) as total');
}
$query->select($selectFields);
if (count($groupParts) > 1) {
$query->groupBy($relationTable . '.' . $fieldName);
} else {
$query->groupBy($tableName . '.' . $groupBy);
}
} else {
// 不分组统计,获取所有数据列表
// 列表查询保持原样,获取所有符合条件的记录
}
// 排序(分组统计时对分组结果排序,不分组时对列表排序)
if (!empty($statistics['order_by'])) {
$orderField = $statistics['order_by']['field'] ?? ($groupBy ? 'total' : 'id');
$orderDirection = $statistics['order_by']['direction'] ?? 'desc';
$query->orderBy($orderField, $orderDirection);
if (!$groupBy) {
// 不分组时,列表查询也需要排序
$listQuery->orderBy($orderField, $orderDirection);
}
} else {
// 没有指定排序时,不分组情况使用默认排序
if (!$groupBy) {
$listQuery->orderBy('id', 'desc');
}
}
// 获取统计查询的 SQL 语句(在执行查询前)
$statisticsSql = $query->toSql();
$statisticsBindings = $query->getBindings();
$statisticsSqlFull = $this->getFullSql($statisticsSql, $statisticsBindings);
// 获取分组统计结果列表(分页)
// 先获取所有结果用于计算统计结果
$allResults = $query->get();
// 对结果进行分页处理
$totalCount = $allResults->count();
$pagedResults = $allResults->slice(($page - 1) * $pageSize, $pageSize);
// 格式化分组统计结果
$data = [];
foreach ($pagedResults as $item) {
$row = [
'group_value' => $item->group_value ?? null,
'total' => round($item->total, $config->decimal_places)
];
$data[] = $row;
}
// 计算统计结果
$statisticsResult = null;
$pagination = null;
if ($groupBy) {
// 分组统计:根据统计类型计算最终结果
$statisticsField = $statistics['field'] ?? null;
if ($statisticsType === 'sum' && $statisticsField) {
// 分组求和时,统计结果是所有分组的总和
$allTotalValue = 0;
foreach ($allResults as $item) {
$allTotalValue += $item->total;
}
$statisticsResult = round($allTotalValue, $config->decimal_places);
} elseif ($statisticsType === 'max' && $statisticsField) {
// 分组最大值时,统计结果是所有分组中的最大值
$maxValue = null;
foreach ($allResults as $item) {
if ($maxValue === null || $item->total > $maxValue) {
$maxValue = $item->total;
}
}
$statisticsResult = $maxValue !== null ? round($maxValue, $config->decimal_places) : 0;
} elseif ($statisticsType === 'min' && $statisticsField) {
// 分组最小值时,统计结果是所有分组中的最小值
$minValue = null;
foreach ($allResults as $item) {
if ($minValue === null || $item->total < $minValue) {
$minValue = $item->total;
}
}
$statisticsResult = $minValue !== null ? round($minValue, $config->decimal_places) : 0;
} else {
// 分组计数时,统计结果是所有分组的计数总和
$allTotalValue = 0;
foreach ($allResults as $item) {
$allTotalValue += $item->total;
}
$statisticsResult = $allTotalValue;
}
// 分页信息
$pagination = [
'current_page' => $page,
'page_size' => $pageSize,
'total' => $totalCount,
'total_pages' => ceil($totalCount / $pageSize)
];
} else {
// 不分组统计:先获取 SQL再执行查询
// 克隆查询用于获取 SQL避免执行后无法获取
$calcQuery = clone $listQuery;
$listQueryForData = clone $listQuery;
// 根据统计类型计算统计值
$statisticsField = $statistics['field'] ?? null;
if ($statisticsType === 'sum' && $statisticsField) {
// 处理关联模型字段
$fieldParts = explode('.', $statisticsField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$statisticsResult = round($calcQuery->sum($fieldRelationTable . '.' . $fieldFieldName), $config->decimal_places);
} else {
$statisticsResult = round($listQuery->sum($statisticsField), $config->decimal_places);
}
} else {
$statisticsResult = round($listQuery->sum($statisticsField), $config->decimal_places);
}
} elseif ($statisticsType === 'max' && $statisticsField) {
// 处理关联模型字段
$fieldParts = explode('.', $statisticsField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$statisticsResult = round($calcQuery->max($fieldRelationTable . '.' . $fieldFieldName), $config->decimal_places);
} else {
$statisticsResult = round($listQuery->max($statisticsField), $config->decimal_places);
}
} else {
$statisticsResult = round($listQuery->max($statisticsField), $config->decimal_places);
}
} elseif ($statisticsType === 'min' && $statisticsField) {
// 处理关联模型字段
$fieldParts = explode('.', $statisticsField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$statisticsResult = round($calcQuery->min($fieldRelationTable . '.' . $fieldFieldName), $config->decimal_places);
} else {
$statisticsResult = round($listQuery->min($statisticsField), $config->decimal_places);
}
} else {
$statisticsResult = round($listQuery->min($statisticsField), $config->decimal_places);
}
} elseif ($statisticsType === 'count_distinct' && isset($statistics['distinct_field'])) {
// 去重数量统计
$distinctField = $statistics['distinct_field'];
// 处理关联模型字段
$fieldParts = explode('.', $distinctField);
if (count($fieldParts) > 1) {
$fieldRelationName = $fieldParts[0];
$fieldFieldName = $fieldParts[1];
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
if ($fieldRelationModel) {
$fieldRelationTable = $fieldRelationModel::make()->getTable();
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
$statisticsResult = $calcQuery->selectRaw('COUNT(DISTINCT ' . $fieldRelationTable . '.' . $fieldFieldName . ') as total')->value('total') ?? 0;
} else {
$statisticsResult = $listQuery->selectRaw('COUNT(DISTINCT ' . $distinctField . ') as total')->value('total') ?? 0;
}
} else {
$statisticsResult = $listQuery->selectRaw('COUNT(DISTINCT ' . $tableName . '.' . $distinctField . ') as total')->value('total') ?? 0;
}
} else {
// count 统计总数量
$statisticsResult = $listQuery->count();
}
// 获取统计查询的 SQL在计算完统计值之后确保包含所有修改
$statisticsSqlForCalc = $calcQuery->toSql();
$statisticsBindingsForCalc = $calcQuery->getBindings();
$statisticsSqlFull = $this->getFullSql($statisticsSqlForCalc, $statisticsBindingsForCalc);
// 获取列表总数(用于分页)
$listTotalCount = $listQueryForData->count();
// 不分组时,获取分页数据作为列表
$listResults = $listQueryForData->skip(($page - 1) * $pageSize)->take($pageSize)->get();
$data = [];
foreach ($listResults as $item) {
$data[] = $item->toArray();
}
// 分页信息
$pagination = [
'current_page' => $page,
'page_size' => $pageSize,
'total' => $listTotalCount,
'total_pages' => ceil($listTotalCount / $pageSize)
];
}
return $this->success([
'sql' => $statisticsSqlFull,
'list' => $data,
'total' => $statisticsResult,
'pagination' => $pagination,
'config' => $config
]);
} catch (\Exception $exception) {
return $this->fail([$exception->getCode(), $exception->getMessage()]);
}
}
/**
* 获取模型实例
*/
private function getModel($modelName)
{
$models = [
'user' => User::class,
'company' => Company::class,
'course_sign' => CourseSign::class,
'course' => Course::class,
'course_type' => CourseType::class,
];
return $models[$modelName] ?? null;
}
/**
* 应用查询条件
*/
private function applyCondition($query, $condition, $logic, $index, $tableName = null)
{
$key = $condition['key'] ?? '';
$operator = $condition['operator'] ?? 'eq';
$value = $condition['value'] ?? '';
if (empty($key)) {
return;
}
// 处理关联模型的字段
$keyParts = explode('.', $key);
if (count($keyParts) > 1) {
// 关联模型字段,使用 whereHas
$relationName = $keyParts[0];
$fieldName = $keyParts[1];
$query->whereHas($relationName, function ($q) use ($fieldName, $operator, $value) {
$this->applyOperator($q, $fieldName, $operator, $value);
});
return;
}
// 主模型字段,添加表名前缀避免字段歧义
$qualifiedKey = $tableName ? $tableName . '.' . $key : $key;
$this->applyOperator($query, $qualifiedKey, $operator, $value);
}
/**
* 获取关联模型
*/
private function getRelationModel($mainModel, $relationName)
{
$model = new $mainModel();
if (method_exists($model, $relationName)) {
$relation = $model->$relationName();
return get_class($relation->getRelated());
}
return null;
}
/**
* 获取关联键名
*/
private function getRelationKey($mainModel, $relationName)
{
$model = new $mainModel();
if (method_exists($model, $relationName)) {
$relation = $model->$relationName();
return $relation->getForeignKeyName();
}
return 'id';
}
/**
* 获取完整的 SQL 语句(包含绑定参数)
*/
private function getFullSql($sql, $bindings)
{
if (empty($bindings)) {
return $sql;
}
$fullSql = $sql;
foreach ($bindings as $binding) {
$value = is_numeric($binding) ? $binding : "'" . addslashes($binding) . "'";
$fullSql = preg_replace('/\?/', $value, $fullSql, 1);
}
return $fullSql;
}
/**
* 应用操作符
*/
private function applyOperator($query, $key, $operator, $value)
{
switch ($operator) {
case 'eq':
$query->where($key, $value);
break;
case 'neq':
$query->where($key, '!=', $value);
break;
case 'gt':
$query->where($key, '>', $value);
break;
case 'egt':
$query->where($key, '>=', $value);
break;
case 'lt':
$query->where($key, '<', $value);
break;
case 'elt':
$query->where($key, '<=', $value);
break;
case 'like':
$query->where($key, 'like', '%' . $value . '%');
break;
case 'notlike':
$query->where($key, 'not like', '%' . $value . '%');
break;
case 'in':
$array = explode(',', $value);
$query->whereIn($key, $array);
break;
case 'notin':
$array = explode(',', $value);
$query->whereNotIn($key, $array);
break;
case 'between':
list($from, $to) = explode(',', $value);
if (!empty($from) && !empty($to)) {
$query->whereBetween($key, [$from, $to]);
}
break;
case 'notbetween':
list($from, $to) = explode(',', $value);
if (!empty($from) && !empty($to)) {
$query->whereNotBetween($key, [$from, $to]);
}
break;
case 'isnull':
$query->whereNull($key);
break;
case 'isnotnull':
$query->whereNotNull($key);
break;
}
}
}