| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- <?php
- namespace app\admin\controller;
- use app\common\util\AppMsg;
- use think\Db;
- use think\Exception;
- use think\exception\DbException;
- class AiStatistics extends Auth
- {
- static $types = [
- 1 =>'报修',
- 2 =>'保洁',
- 3 =>'运送',
- 4 =>'安保',
- ];
- static $colorMap = [
- 1 => '#0FB36A',
- 2 => '#168BFF',
- 3 => '#F79B10',
- 4 => '#6F3DFF',
- ];
- public function statisticsData()
- {
- $today = date('Ymd');
- $records = Db::name('ai_send_success')
- ->where('org_id', $this->orgId)
- ->field('mode')
- ->group('mode')
- ->column('COUNT(*)', 'mode');
- $done = [
- 'total' => array_sum($records),
- 'bx' => $records[1] ?? 0,
- 'bj' => $records[2] ?? 0,
- 'ys' => $records[3] ?? 0,
- 'ab' => $records[4] ?? 0
- ];
- $orders = Db::name('orders')
- ->where('org_id',$this->orgId)
- ->where('del',0)
- ->where('create_yyyymmdd',$today)
- ->where('order_mode',1)
- ->whereNotExists(function ($query) {
- $query->table('ai_send_fail')
- ->whereColumn('ai_send_fail.order_id', 'orders.id');
- })
- ->field('work_type_mode')
- ->group('work_type_mode')
- ->column('COUNT(*)', 'work_type_mode');
- $undo = [
- 'total' => array_sum($orders),
- 'bx' => $orders[1] ?? 0,
- 'bj' => $orders[2] ?? 0,
- 'ys' => $orders[3] ?? 0,
- 'ab' => $orders[4] ?? 0,
- ];
- $data = [
- 'done'=>$done,
- 'undo'=>$undo,
- ];
- return json($data);
- }
- public function chartData()
- {
- $type = input('type', 'day', 'trim');
- $today = date('Ymd');
- // 根据类型设置查询条件
- switch ($type) {
- case 'month':
- $startDate = date('Ymd', strtotime('first day of this month'));
- $endDate = $today;
- $dateFormat = 'Ymd';
- $displayFormat = 'm-d';
- $dateRange = $this->getDateRange($startDate, $endDate, $dateFormat);
- break;
- case 'year':
- $startDate = date('Ymd', strtotime('first day of January'));
- $endDate = $today;
- $displayFormat = 'Y-m';
- $dateRange = $this->getMonthRange($startDate, $endDate);
- break;
- case 'day':
- default:
- $startDate = date('Ymd', strtotime('-7 days'));
- $endDate = $today;
- $dateFormat = 'Ymd';
- $displayFormat = 'm-d';
- $dateRange = $this->getDateRange($startDate, $endDate, $dateFormat);
- break;
- }
- $echart = Db::name('todo')
- ->where('org_id', $this->orgId)
- ->where('del', 0)
- ->where('create_yyyymmdd', '>=', $startDate)
- ->where('create_yyyymmdd', '<=', $endDate)
- ->field("create_yyyymmdd, SUM(create_user_id = -1) as ai, SUM(create_user_id != -1) as user")
- ->group('create_yyyymmdd')
- ->order('create_yyyymmdd')
- ->select();
- $statsByDate = [];
- foreach ($echart as $item) {
- if ($type == 'year') {
- $key = substr($item['create_yyyymmdd'], 0, 6);
- } else {
- $key = $item['create_yyyymmdd'];
- }
- if (!isset($statsByDate[$key])) {
- $statsByDate[$key] = ['ai' => 0, 'user' => 0];
- }
- $statsByDate[$key]['ai'] += (int)$item['ai'];
- $statsByDate[$key]['user'] += (int)$item['user'];
- }
- $aiData = [];
- $userData = [];
- $dates = [];
- foreach ($dateRange as $date) {
- $key = $type == 'year' ? substr($date, 0, 6) : $date;
- $aiData[] = isset($statsByDate[$key]) ? (int)$statsByDate[$key]['ai'] : 0;
- $userData[] = isset($statsByDate[$key]) ? (int)$statsByDate[$key]['user'] : 0;
- if ($type == 'year') {
- $dates[] = date($displayFormat, strtotime($key . '01'));
- } else {
- $dates[] = date($displayFormat, strtotime($date));
- }
- }
- $data = [
- 'ai' => $aiData,
- 'user' => $userData,
- 'date' => $dates,
- 'type' => $type
- ];
- return json($data);
- }
- public function countData()
- {
- $dispatched = Db::name('ai_send_success')
- ->where('org_id', $this->orgId)
- ->whereTime('create_time', 'today')
- ->count();
- $error = Db::name('ai_send_fail')
- ->where('org_id', $this->orgId)
- ->where('status', 0)
- ->whereTime('create_time', 'today')
- ->count();
- $order = Db::name('orders')
- ->where('org_id',$this->orgId)
- ->where('order_mode',1)
- ->where('del',0)
- ->whereIn('work_type_mode',[1,2,3,4])
- ->where('create_yyyymmdd',date('Ymd'))
- ->whereNotExists(function ($query) {
- $query->table('ai_send_fail')
- ->whereColumn('ai_send_fail.order_id', 'orders.id');
- })
- ->count();
- $data = [
- 'today' => $dispatched,
- 'error' => $error,
- 'wait' => $order
- ];
- return json($data);
- }
- public function dispatchedData()
- {
- $size = input('size',10,'intval');
- $page = input('page',1,'intval');
- $mode= input('mode',0,'intval');
- $query = Db::name('ai_send_success')
- ->alias('a')
- ->join('orders o','a.order_id = o.id')
- ->where('a.org_id', $this->orgId)
- ->whereTime('a.create_time', 'today')
- ->when($mode > 0,function ($query) use($mode){
- $query->where('a.mode', $mode);
- })
- ->field('a.user_id,a.todo_id,a.mode,a.create_time,o.sn,o.user_id as uid,o.id');
- $todoList = $query
- ->order('a.create_time desc')
- ->limit(($page -1) * $size,$size)
- ->select();
- $total = $query->removeOption('limit')->count();
- $userIds = array_column($todoList,'uid');
- $toUserIds = array_column($todoList,'user_id');
- $allIds = array_unique(array_merge($toUserIds,$userIds));
- $users = Db::name('user')->whereIn('id',$allIds)->column('real_name','id');
- foreach ($todoList as &$v){
- $v['user'] = $users[$v['uid']]??'';
- $v['toUser'] = $users[$v['user_id']]??'';
- $v['type'] = self::$types[$v['mode']]??'';
- }
- unset($v);
- return json(['data'=>$todoList,'total'=>$total]);
- }
- public function unDispatchedData()
- {
- $size = input('size',10,'intval');
- $page = input('page',1,'intval');
- $type = input('type','','trim');
- if ($type == 'wait'){
- $query = Db::name('orders')
- ->where('org_id',$this->orgId)
- ->where('order_mode',1)
- ->where('del',0)
- ->whereIn('work_type_mode',[1,2,3,4])
- ->where('create_yyyymmdd',date('Ymd'))
- ->whereNotExists(function ($q) {
- $q->table('ai_send_fail')
- ->whereColumn('ai_send_fail.order_id', 'orders.id');
- })
- ->field('id,sn,work_type_mode,user_id');
- }else{
- $query = Db::name('ai_send_fail')
- ->alias('a')
- ->join('orders o','a.order_id = o.id')
- ->where('a.org_id', $this->orgId)
- ->where('a.status', 0)
- ->whereTime('a.create_time', 'today')
- ->field('o.id,o.sn,o.work_type_mode,o.user_id');
- }
- $orderList = $query
- ->limit(($page -1) * $size,$size)
- ->select();
- $total = $query->removeOption('limit')->count();
- $users = Db::name('user') ->whereIn('id',array_unique(array_column($orderList,'user_id'))) ->column('real_name','id');
- $i = 1;
- foreach ($orderList as &$v2){
- $v2['user'] = $users[$v2['user_id']]??'';
- $v2['type'] = self::$types[$v2['work_type_mode']]??'';
- $v2['flag'] = 0;
- if ($i <= 20){
- $v2['flag'] = 1;
- $i ++;
- }
- }
- unset($v2);
- return json(['data'=>$orderList,'total'=>$total]);
- }
- public function staffData()
- {
- $result = $this->getBatchModeStats([1,2,3,4], $this->orgId);
- $list = $result['list'];
- $summary = $result['summary'];
- $chartData = [];
- foreach ($list as $modeId => $data) {
- $chartData[] = [
- 'value' => $data['active'],
- 'total' => $data['total'],
- 'name' => $data['mode_name'],
- 'itemStyle' => [
- 'color' => self::$colorMap[$modeId] ?? '#999'
- ]
- ];
- }
- $data = [
- 'bxInfo' => $list[1],
- 'bjInfo' => $list[2],
- 'ysInfo' => $list[3],
- 'abInfo' => $list[4],
- 'staffData' =>$chartData,
- 'staffSummary' =>$summary
- ];
- return json($data);
- }
- public function orderData()
- {
- $type = input('type', 'day', 'trim');
- $today = date('Ymd');
- // 根据类型设置查询条件
- switch ($type) {
- case 'month':
- $startDate = date('Ymd', strtotime('first day of this month'));
- $endDate = $today;
- break;
- case 'year':
- $startDate = date('Ymd', strtotime('first day of January'));
- $endDate = $today;
- break;
- case 'day':
- default:
- $startDate = date('Ymd', strtotime('-7 days'));
- $endDate = $today;
- break;
- }
- $modeMap = self::$types;
- $modeIds = array_keys($modeMap);
- // 查询数据,按mode、来源分组统计总数
- $echart = Db::name('todo')
- ->where('org_id', $this->orgId)
- ->where('del', 0)
- ->where('create_yyyymmdd', '>=', $startDate)
- ->where('create_yyyymmdd', '<=', $endDate)
- ->whereIn('work_type_mode', $modeIds)
- ->field("work_type_mode, SUM(create_user_id = -1) as ai, SUM(create_user_id != -1) as user")
- ->group('work_type_mode')
- ->order('work_type_mode')
- ->select();
- // 将查询结果转为以mode为key的数组
- $statsByMode = [];
- foreach ($echart as $item) {
- $statsByMode[$item['work_type_mode']] = [
- 'ai' => (int)$item['ai'],
- 'user' => (int)$item['user']
- ];
- }
- // 按mode顺序构建一维数组
- $aiData = [];
- $userData = [];
- $modeNames = [];
- foreach ($modeMap as $modeId => $modeName) {
- $aiData[] = isset($statsByMode[$modeId]) ? $statsByMode[$modeId]['ai'] : 0;
- $userData[] = isset($statsByMode[$modeId]) ? $statsByMode[$modeId]['user'] : 0;
- $modeNames[] = $modeName;
- }
- $data = [
- 'ai' => $aiData,
- 'user' => $userData,
- 'modes' => $modeNames,
- ];
- return json($data);
- }
- public function userData()
- {
- $size = input('size',7,'intval');
- $page = input('page',1,'intval');
- $start = ($page - 1) * $size;
- $mode= input('mode',0,'intval');
- $roleData = $mode > 0 ? $this->getRoleIdsByModeIds([$mode],$this->orgId) : $this->getRoleIdsByModeIds([1,2,3,4],$this->orgId);
- $map[] = ['u.del','=',0];
- $map[] = ['u.enable','=',1];
- $map[] = ['u.type','=',0];
- $map[] = ['ur.roles_id','in',$roleData['all_role_ids']];
- $query = Db::name('user')
- ->field('u.id,u.real_name,u.work,ur.roles_id')
- ->alias('u')
- ->join('user_roles ur','u.id=ur.user_id')
- ->where($map);
- $lists = $query->select();
- $total = count($lists);
- $roleIds = array_unique(array_column($lists, 'roles_id'));
- $roleNames = Db::name('roles')
- ->where('id', 'in', $roleIds)
- ->column('name', 'id');
- $userIds = array_column($lists, 'id');
- $todoCounts = Db::name('todo')
- ->field("CONCAT(to_user_id, '_', todo_mode) as mode_key, COUNT(*) as count")
- ->where('del', 0)
- ->whereIn('to_user_id', $userIds)
- ->where('todo_mode', 'in', [1, 2])
- ->group('to_user_id, todo_mode')
- ->select();
- $todoCountMap = [];
- foreach ($todoCounts as $item) {
- $todoCountMap[$item['mode_key']] = (int)$item['count'];
- }
- $avgTimes = Db::name('todo')
- ->field('to_user_id, AVG(xy_time) as receive, AVG(wc_time) as finish')
- ->where('del', 0)
- ->whereIn('to_user_id', $userIds)
- // ->where('todo_mode', 1)
- ->group('to_user_id')
- ->select();
- $avgTimeMap = [];
- foreach ($avgTimes as $item) {
- $avgTimeMap[$item['to_user_id']] = $item;
- }
- foreach ($lists as &$list) {
- $list['role_name'] = $roleNames[$list['roles_id']] ?? '未知角色';
- $list['work'] = (int)$list['work'];
- $list['doing'] = $todoCountMap[$list['id'] . '_2'] ?? 0;
- $list['wait'] = $todoCountMap[$list['id'] . '_1'] ?? 0;
- $avgData = $avgTimeMap[$list['id']] ?? null;
- $list['receive'] = $avgData ? $this->formatDuration($avgData['receive']) : '--';
- $list['finish'] = $avgData ? $this->formatDuration($avgData['finish']) : '--';
- }
- unset($list);
- usort($lists, function($a, $b) {
- if ($a['doing'] != $b['doing']) {
- return $b['doing'] - $a['doing'];
- }
- return $b['wait'] - $a['wait'];
- });
- $lists = array_slice($lists, $start, $size);
- return json(['data'=>$lists,'total'=>$total]);
- }
- /**
- * 获取日期范围(按天)
- */
- private function getDateRange($startDate, $endDate, $format = 'Ymd')
- {
- $dates = [];
- $current = strtotime($startDate);
- $end = strtotime($endDate);
- while ($current <= $end) {
- $dates[] = date($format, $current);
- $current = strtotime('+1 day', $current);
- }
- return $dates;
- }
- /**
- * 获取月份范围(按月)
- */
- private function getMonthRange($startDate, $endDate)
- {
- $dates = [];
- $current = strtotime($startDate);
- $end = strtotime($endDate);
- // 调整到月初
- $current = strtotime(date('Y-m-01', $current));
- while ($current <= $end) {
- $dates[] = date('Ymd', $current);
- $current = strtotime('+1 month', $current);
- }
- return $dates;
- }
- /**
- * 批量统计多个工作类型模式的用户数量(包含汇总)
- * @param array $modeIds 工作类型模式ID列表
- * @param int $orgId 组织ID
- * @return array
- */
- public function getBatchModeStats($modeIds, $orgId)
- {
- if (empty($modeIds)) {
- return [
- 'list' => [],
- 'summary' => [
- 'total' => 0,
- 'active' => 0,
- 'offline' => 0,
- 'active_rate' => 0,
- ]
- ];
- }
- // 1. 获取模式名称映射
- $modeNames = [];
- $modeConfigs = Db::name('work_type_mode')
- ->where('id', 'in', $modeIds)
- ->field('id, name')
- ->select();
- foreach ($modeConfigs as $config) {
- $modeNames[$config['id']] = $config['name'];
- }
- // 2. 获取所有角色ID和模式角色映射(使用优化后的方法)
- $roleData = $this->getRoleIdsByModeIds($modeIds, $orgId);
- $allRoleIds = $roleData['all_role_ids'];
- $modeRolesMap = $roleData['mode_roles_map'];
- // 如果没有角色,返回空数据
- $emptyResult = [
- 'list' => [],
- 'summary' => [
- 'total' => 0,
- 'active' => 0,
- 'offline' => 0,
- 'active_rate' => 0,
- ]
- ];
- if (empty($allRoleIds)) {
- foreach ($modeIds as $modeId) {
- $emptyResult['list'][$modeId] = [
- 'mode_id' => $modeId,
- 'mode_name' => $modeNames[$modeId] ?? '未知模式',
- 'total' => 0,
- 'active' => 0,
- 'offline' => 0,
- 'active_rate' => 0,
- ];
- }
- return $emptyResult;
- }
- // 3. 一次性查询所有用户
- $users = Db::name('user_roles')
- ->alias('ur')
- ->join('user u', 'u.id = ur.user_id')
- ->where('u.del', '=', 0)
- ->where('u.enable', '=', 1)
- ->where('ur.roles_id', 'in', $allRoleIds)
- ->field('u.id, u.work, ur.roles_id')
- ->select();
- // 4. 构建角色用户映射
- $roleUserMap = [];
- foreach ($users as $user) {
- $roleId = $user['roles_id'];
- $userId = $user['id'];
- $work = $user['work'];
- if (!isset($roleUserMap[$roleId])) {
- $roleUserMap[$roleId] = [
- 'total' => [],
- 'active' => [],
- ];
- }
- $roleUserMap[$roleId]['total'][$userId] = true;
- if ($work == 1) {
- $roleUserMap[$roleId]['active'][$userId] = true;
- }
- }
- // 5. 计算每个模式的统计数据(直接使用 $modeRolesMap)
- $result = [];
- foreach ($modeIds as $modeId) {
- $roleIds = $modeRolesMap[$modeId] ?? [];
- $totalUsers = [];
- $activeUsers = [];
- foreach ($roleIds as $roleId) {
- if (isset($roleUserMap[$roleId])) {
- $totalUsers += $roleUserMap[$roleId]['total'];
- $activeUsers += $roleUserMap[$roleId]['active'];
- }
- }
- $total = count($totalUsers);
- $active = count($activeUsers);
- $result[$modeId] = [
- 'mode_id' => $modeId,
- 'mode_name' => $modeNames[$modeId] ?? '未知模式',
- 'total' => $total,
- 'active' => $active,
- 'offline' => $total - $active,
- 'active_rate' => $total > 0 ? round(($active / $total) * 100, 2) : 0,
- ];
- }
- // 6. 计算汇总数据
- $summaryTotal = count($users);
- $summaryActive = count(array_filter($users, function($user) {
- return $user['work'] == 1;
- }));
- return [
- 'list' => $result,
- 'summary' => [
- 'total' => $summaryTotal,
- 'active' => $summaryActive,
- 'offline' => $summaryTotal - $summaryActive,
- 'active_rate' => $summaryTotal > 0 ? round(($summaryActive / $summaryTotal) * 100, 2) : 0,
- ]
- ];
- }
- /**
- * 根据模式ID列表获取所有关联的角色ID(包含每个模式的角色映射)
- * @param array $modeIds 模式ID列表
- * @param int $orgId 组织ID
- * @return array 返回 [
- * 'all_role_ids' => 所有角色ID(已去重),
- * 'mode_roles_map' => [模式ID => 角色ID列表]
- * ]
- */
- public function getRoleIdsByModeIds($modeIds, $orgId)
- {
- if (empty($modeIds)) {
- return [
- 'all_role_ids' => [],
- 'mode_roles_map' => []
- ];
- }
- $allRoleIds = [];
- $modeRolesMap = [];
- // 1. 获取模式配置
- $modeConfigs = Db::name('work_type_mode')
- ->where('id', 'in', $modeIds)
- ->field('id, roles')
- ->select();
- // 2. 获取组织配置
- $orgConfigs = Db::name('work_type_mode_org')
- ->where('org_id', $orgId)
- ->where('value', 'in', $modeIds)
- ->select();
- $orgConfigMap = [];
- foreach ($orgConfigs as $config) {
- $orgConfigMap[$config['value']] = $config;
- }
- // 3. 遍历模式配置,收集所有角色ID
- foreach ($modeConfigs as $config) {
- $modeId = $config['id'];
- // 确定角色ID列表(优先使用组织配置)
- $roleIdsStr = '';
- if (isset($orgConfigMap[$modeId])) {
- $orgConfig = $orgConfigMap[$modeId];
- $roleIdsStr = !empty($orgConfig['org_roles'])
- ? $orgConfig['org_roles']
- : ($orgConfig['roles'] ?? '');
- } else {
- $roleIdsStr = $config['roles'] ?? '';
- }
- // 解析角色ID
- $roleIds = $roleIdsStr ? array_filter(array_map('trim', explode(',', $roleIdsStr))) : [];
- if (empty($roleIds)) {
- $modeRolesMap[$modeId] = [];
- continue;
- }
- // 获取每个角色的所有子角色
- $allChildrenIds = [];
- foreach ($roleIds as $roleId) {
- $children = model('Roles')->getChildrenIds($roleId, $orgId) ?? [];
- $allChildrenIds = array_merge($allChildrenIds, $children, [$roleId]);
- }
- $allChildrenIds = array_unique($allChildrenIds);
- $modeRolesMap[$modeId] = $allChildrenIds;
- $allRoleIds = array_merge($allRoleIds, $allChildrenIds);
- }
- // 去重返回
- return [
- 'all_role_ids' => array_unique($allRoleIds),
- 'mode_roles_map' => $modeRolesMap
- ];
- }
- /**
- * 格式化时长(秒数转为 x时x分x秒 格式)
- * @param float|int|null $seconds 秒数
- * @return string 格式化后的时长,如 "1h2min30s" 或 "--"
- */
- private function formatDuration($seconds)
- {
- if (empty($seconds) || $seconds <= 0) {
- return '--';
- }
- $seconds = (int)round($seconds);
- $hours = floor($seconds / 3600);
- $minutes = floor(($seconds % 3600) / 60);
- $remainingSeconds = $seconds % 60;
- $parts = [];
- if ($hours > 0) {
- $parts[] = $hours . 'h';
- }
- if ($minutes > 0) {
- $parts[] = $minutes . 'min';
- }
- if ($remainingSeconds > 0 || empty($parts)) {
- $parts[] = $remainingSeconds . 's';
- }
- return implode('', $parts);
- }
- }
|