[]]], tags: ['Dashboard'], responses: [ new OA\Response( response: 200, description: '获取成功', content: new OA\JsonContent(properties: [ new OA\Property(property: 'code', type: 'integer', example: 0), new OA\Property(property: 'message', type: 'string', example: '获取成功'), new OA\Property(property: 'data', ref: '#/components/schemas/DashboardOverview'), ]) ), new OA\Response(response: 401, description: '未认证', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), new OA\Response(response: 403, description: '无权限', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), ] )] #[RequestMapping(path: "overview", methods: "GET")] public function overview(): ResponseInterface|array { $scope_type = $this->request->getAttribute('scope_type', 'all'); $scope_ids = $this->request->getAttribute('scope_ids', []); $data = $this->statsService->getOverview($scope_type, $scope_ids); return [ 'code' => 0, 'message' => '获取成功', 'data' => $data, ]; } /** * 趋势数据 */ #[OA\Get( path: '/api/v1/dashboard/trend', summary: '获取趋势数据', description: '返回指定时间范围内按日/周/月聚合的成功和失败同步趋势。', security: [['bearerAuth' => []]], tags: ['Dashboard'], parameters: [ new OA\Parameter(name: 'from', in: 'query', required: false, description: '起始日期(默认30天前)', schema: new OA\Schema(type: 'string', format: 'date', example: '2026-02-15')), new OA\Parameter(name: 'to', in: 'query', required: false, description: '结束日期(默认今天)', schema: new OA\Schema(type: 'string', format: 'date', example: '2026-03-17')), new OA\Parameter(name: 'group_by', in: 'query', required: false, description: '聚合粒度', schema: new OA\Schema(type: 'string', enum: ['day', 'week', 'month'], default: 'day')), new OA\Parameter(name: 'data_type', in: 'query', required: false, description: '数据类型筛选', schema: new OA\Schema(type: 'string', enum: ['order', 'product', 'refund', 'inventory'])), ], responses: [ new OA\Response( response: 200, description: '获取成功', content: new OA\JsonContent(properties: [ new OA\Property(property: 'code', type: 'integer', example: 0), new OA\Property(property: 'message', type: 'string', example: '获取成功'), new OA\Property(property: 'data', type: 'array', items: new OA\Items(ref: '#/components/schemas/DashboardTrendItem')), ]) ), new OA\Response(response: 400, description: '参数错误', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), new OA\Response(response: 401, description: '未认证', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), new OA\Response(response: 403, description: '无权限', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), ] )] #[RequestMapping(path: "trend", methods: "GET")] public function trend(): ResponseInterface|array { $from = $this->request->input('from', date('Y-m-d', strtotime('-30 days'))); $to = $this->request->input('to', date('Y-m-d')); $group_by = $this->request->input('group_by', 'day'); $data_type = $this->request->input('data_type'); // 校验 group_by if (!in_array($group_by, DashboardStatsService::VALID_GROUP_BY, true)) { return $this->response->json([ 'code' => 400, 'message' => "无效的 group_by 参数: {$group_by},可选值: " . implode(', ', DashboardStatsService::VALID_GROUP_BY), 'data' => null, ])->withStatus(400); } // 校验 data_type if ($data_type !== null && !in_array($data_type, DashboardStatsService::VALID_DATA_TYPES, true)) { return $this->response->json([ 'code' => 400, 'message' => "无效的 data_type 参数: {$data_type},可选值: " . implode(', ', DashboardStatsService::VALID_DATA_TYPES), 'data' => null, ])->withStatus(400); } // 校验日期格式 if (!$this->isValidDate($from) || !$this->isValidDate($to)) { return $this->response->json([ 'code' => 400, 'message' => '无效的日期格式,请使用 YYYY-MM-DD', 'data' => null, ])->withStatus(400); } // 校验 from <= to if ($from > $to) { return $this->response->json([ 'code' => 400, 'message' => 'from 日期不能晚于 to 日期', 'data' => null, ])->withStatus(400); } $scope_type = $this->request->getAttribute('scope_type', 'all'); $scope_ids = $this->request->getAttribute('scope_ids', []); $data = $this->statsService->getTrend($from, $to, $group_by, $data_type, $scope_type, $scope_ids); return [ 'code' => 0, 'message' => '获取成功', 'data' => $data, ]; } /** * 分组统计 */ #[OA\Get( path: '/api/v1/dashboard/breakdown', summary: '获取分组统计', description: '按公司/平台/店铺维度统计成功和失败同步数。', security: [['bearerAuth' => []]], tags: ['Dashboard'], parameters: [ new OA\Parameter(name: 'dimension', in: 'query', required: true, description: '分组维度', schema: new OA\Schema(type: 'string', enum: ['company', 'platform', 'store'])), new OA\Parameter(name: 'from', in: 'query', required: false, description: '起始日期(默认今天)', schema: new OA\Schema(type: 'string', format: 'date', example: '2026-03-17')), new OA\Parameter(name: 'to', in: 'query', required: false, description: '结束日期(默认今天)', schema: new OA\Schema(type: 'string', format: 'date', example: '2026-03-17')), new OA\Parameter(name: 'data_type', in: 'query', required: false, description: '数据类型筛选', schema: new OA\Schema(type: 'string', enum: ['order', 'product', 'refund', 'inventory'])), ], responses: [ new OA\Response( response: 200, description: '获取成功', content: new OA\JsonContent(properties: [ new OA\Property(property: 'code', type: 'integer', example: 0), new OA\Property(property: 'message', type: 'string', example: '获取成功'), new OA\Property(property: 'data', type: 'array', items: new OA\Items(ref: '#/components/schemas/DashboardBreakdownItem')), ]) ), new OA\Response(response: 400, description: '参数错误', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), new OA\Response(response: 401, description: '未认证', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), new OA\Response(response: 403, description: '无权限', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')), ] )] #[RequestMapping(path: "breakdown", methods: "GET")] public function breakdown(): ResponseInterface|array { $dimension = $this->request->input('dimension'); // 校验 dimension 必填 if ($dimension === null || $dimension === '') { return $this->response->json([ 'code' => 400, 'message' => '缺少必填参数: dimension', 'data' => null, ])->withStatus(400); } // 校验 dimension 枚举 if (!in_array($dimension, DashboardStatsService::VALID_DIMENSIONS, true)) { return $this->response->json([ 'code' => 400, 'message' => "无效的 dimension 参数: {$dimension},可选值: " . implode(', ', DashboardStatsService::VALID_DIMENSIONS), 'data' => null, ])->withStatus(400); } $from = $this->request->input('from', date('Y-m-d')); $to = $this->request->input('to', date('Y-m-d')); $data_type = $this->request->input('data_type'); // 校验 data_type if ($data_type !== null && !in_array($data_type, DashboardStatsService::VALID_DATA_TYPES, true)) { return $this->response->json([ 'code' => 400, 'message' => "无效的 data_type 参数: {$data_type},可选值: " . implode(', ', DashboardStatsService::VALID_DATA_TYPES), 'data' => null, ])->withStatus(400); } // 校验日期格式 if (!$this->isValidDate($from) || !$this->isValidDate($to)) { return $this->response->json([ 'code' => 400, 'message' => '无效的日期格式,请使用 YYYY-MM-DD', 'data' => null, ])->withStatus(400); } $scope_type = $this->request->getAttribute('scope_type', 'all'); $scope_ids = $this->request->getAttribute('scope_ids', []); $data = $this->statsService->getBreakdown($dimension, $from, $to, $data_type, $scope_type, $scope_ids); return [ 'code' => 0, 'message' => '获取成功', 'data' => $data, ]; } /** * 校验日期格式 YYYY-MM-DD */ private function isValidDate(string $date): bool { $d = \DateTime::createFromFormat('Y-m-d', $date); return $d !== false && $d->format('Y-m-d') === $date; } }