Files
leaudit-platform-frontend/auth_doc/home_statistics_api_详细对接文档.md
T
2025-12-05 00:09:32 +08:00

28 KiB
Raw Blame History

首页统计数据接口 - 前端对接文档

📋 目录


接口概述

基本信息

项目 内容
接口名称 获取首页统计数据
接口路径 /admin/statistics/home-data
请求方法 GET
需要认证 是(需要JWT Token
权限要求 所有登录用户可访问
版本 v3

功能说明

获取首页展示的统计数据,包括:

  • 今日待审核文件数
  • 当前周期已审核文件数
  • 审核文件同比增长
  • 当前周期审核通过率
  • 通过率同比增长
  • 检测到的问题总数
  • 问题数量同比增长

核心特性

  1. 三级权限隔离

    • 省级管理员:查看全省数据
    • 地市管理员:查看本地市数据
    • 普通用户:只能查看自己的数据
  2. 灵活时间范围

    • 支持今天、近3天、近7天、近30天、本月
    • 自动对比上一个相同周期
  3. 类型筛选

    • 支持按文档类型ID筛选
    • 支持多个类型ID组合
  4. 性能优化

    • Redis缓存20分钟
    • 响应时间 < 200ms

快速开始

最简单的调用

// 获取本月统计数据(默认)
fetch('/admin/statistics/home-data', {
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
})
.then(res => res.json())
.then(data => console.log(data));

Vue 3 示例

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'

const stats = ref({})
const loading = ref(false)

const fetchHomeStats = async (timeRange = null, typeIds = null) => {
  loading.value = true
  try {
    const params = {}
    if (timeRange) params.time_range = timeRange
    if (typeIds) params.type_ids = typeIds

    const { data } = await axios.get('/admin/statistics/home-data', { params })
    stats.value = data
  } catch (error) {
    console.error('获取统计数据失败:', error)
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  fetchHomeStats()
})
</script>

<template>
  <div v-if="!loading">
    <h2>今日待审核: {{ stats.today_pending_files }}</h2>
    <h2>已审核: {{ stats.monthly_reviewed_files }}</h2>
    <h2>通过率: {{ stats.monthly_pass_rate }}%</h2>
  </div>
</template>

React 示例

import { useState, useEffect } from 'react'
import axios from 'axios'

function HomeStatistics() {
  const [stats, setStats] = useState({})
  const [loading, setLoading] = useState(false)

  const fetchHomeStats = async (timeRange = null, typeIds = null) => {
    setLoading(true)
    try {
      const params = {}
      if (timeRange) params.time_range = timeRange
      if (typeIds) params.type_ids = typeIds

      const { data } = await axios.get('/admin/statistics/home-data', { params })
      setStats(data)
    } catch (error) {
      console.error('获取统计数据失败:', error)
    } finally {
      setLoading(false)
    }
  }

  useEffect(() => {
    fetchHomeStats()
  }, [])

  return (
    <div>
      {!loading && (
        <>
          <h2>今日待审核: {stats.today_pending_files}</h2>
          <h2>已审核: {stats.monthly_reviewed_files}</h2>
          <h2>通过率: {stats.monthly_pass_rate}%</h2>
        </>
      )}
    </div>
  )
}

接口详情

完整URL

GET {BASE_URL}/admin/statistics/home-data

示例:

https://api.example.com/admin/statistics/home-data
http://localhost:8073/admin/statistics/home-data

请求头

Header 必填 类型 说明 示例
Authorization String JWT Token Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type String 内容类型 application/json

请求参数

Query 参数

参数名 必填 类型 默认值 说明
type_ids String null 文档类型ID,多个用逗号分隔
time_range String null 时间范围,见下表

time_range 参数详解

说明 当前周期 对比周期 使用场景
不传参数 本月数据(默认) 本月1日 ~ 当前时间 上月1日 ~ 上月最后一天 默认首页统计
today 今天 今天00:00 ~ 当前时间 昨天00:00 ~ 昨天23:59 查看今日数据
3days 近3天 当前时间向前72小时 再往前72小时 短期趋势
7days 近7天 当前时间向前7天 再往前7天 周统计
30days 近30天 当前时间向前30天 再往前30天 月统计

type_ids 参数详解

格式: 逗号分隔的整数

示例:

  • 单个类型: type_ids=1
  • 多个类型: type_ids=1,2,3
  • 所有类型: 不传参数或传 null

有效值 (根据你的业务配置):

1  // 类型1
2  // 类型2
3  // 类型3
// ... 更多类型ID

响应数据

响应结构

interface HomeStatisticsResponse {
  today_pending_files: number;           // 今日待审核文件数
  monthly_reviewed_files: number;        // 当前周期已审核文件数
  monthly_review_growth: GrowthData;     // 审核数同比增长
  monthly_pass_rate: number;             // 当前周期通过率(百分比)
  pass_rate_growth: GrowthData;          // 通过率同比增长
  issues_detected: number;               // 检测到的问题总数
  issues_growth: GrowthData;             // 问题数同比增长
}

interface GrowthData {
  value: number;   // 增长百分比(绝对值)
  is_up: boolean;  // true=增长, false=下降
}

完整响应示例

{
  "today_pending_files": 15,
  "monthly_reviewed_files": 120,
  "monthly_review_growth": {
    "value": 25.5,
    "is_up": true
  },
  "monthly_pass_rate": 85.3,
  "pass_rate_growth": {
    "value": 3.2,
    "is_up": true
  },
  "issues_detected": 42,
  "issues_growth": {
    "value": 12.8,
    "is_up": false
  }
}

字段说明

1. today_pending_files(今日待审核文件数)

  • 类型: number
  • 说明: 今天新增的待审核文件数(audit_status = 0 或 null
  • 范围: >= 0
  • 示例: 15

2. monthly_reviewed_files(当前周期已审核文件数)

  • 类型: number
  • 说明: 当前时间范围内已审核的文件数
  • 包括状态:
    • audit_status = 1 (通过)
    • 🔄 audit_status = 2 (审核中)
    • audit_status = -1 (拒绝)
    • ⚠️ audit_status = -2 (警告)
  • 范围: >= 0
  • 示例: 120

3. monthly_review_growth(审核数同比增长)

  • 类型: GrowthData
  • 说明: 当前周期 vs 上个周期的审核数量变化
  • 计算公式: |(当前周期 - 上个周期) / 上个周期| * 100
  • 示例:
    {
      "value": 25.5,   // 增长了25.5%
      "is_up": true    // 增长趋势
    }
    

4. monthly_pass_rate(当前周期通过率)

  • 类型: number
  • 说明: 当前周期审核通过的文件占比
  • 计算公式: (通过数量 / 已审核总数) * 100
  • 范围: 0.0 ~ 100.0
  • 保留: 小数点后1位
  • 示例: 85.3 (表示85.3%)

5. pass_rate_growth(通过率同比增长)

  • 类型: GrowthData
  • 说明: 当前周期 vs 上个周期的通过率变化
  • 计算公式: |(当前通过率 - 上期通过率) / 上期通过率| * 100
  • 示例:
    {
      "value": 3.2,    // 通过率提升了3.2%
      "is_up": true
    }
    

6. issues_detected(检测到的问题总数)

  • 类型: number
  • 说明: 当前周期内所有评查点发现的问题总数
  • 来源: evaluation_results 表中 result='false' 的记录
  • 范围: >= 0
  • 示例: 42

7. issues_growth(问题数同比增长)

  • 类型: GrowthData
  • 说明: 当前周期 vs 上个周期的问题数变化
  • 示例:
    {
      "value": 12.8,   // 问题数减少了12.8%
      "is_up": false   // 下降趋势(好事)
    }
    

请求示例

1. 获取本月统计数据(默认)

请求:

curl -X GET "http://localhost:8073/admin/statistics/home-data" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

JavaScript:

const response = await fetch('/admin/statistics/home-data', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
const data = await response.json()

响应:

{
  "today_pending_files": 10,
  "monthly_reviewed_files": 150,
  "monthly_review_growth": {
    "value": 15.5,
    "is_up": true
  },
  "monthly_pass_rate": 82.0,
  "pass_rate_growth": {
    "value": 2.5,
    "is_up": true
  },
  "issues_detected": 35,
  "issues_growth": {
    "value": 8.0,
    "is_up": false
  }
}

2. 获取今天的统计数据

请求:

curl -X GET "http://localhost:8073/admin/statistics/home-data?time_range=today" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

JavaScript:

const response = await fetch('/admin/statistics/home-data?time_range=today', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
const data = await response.json()

Axios:

const { data } = await axios.get('/admin/statistics/home-data', {
  params: { time_range: 'today' }
})

响应:

{
  "today_pending_files": 5,
  "monthly_reviewed_files": 12,
  "monthly_review_growth": {
    "value": 20.0,
    "is_up": true
  },
  "monthly_pass_rate": 75.0,
  "pass_rate_growth": {
    "value": 5.0,
    "is_up": false
  },
  "issues_detected": 8,
  "issues_growth": {
    "value": 10.0,
    "is_up": false
  }
}

3. 获取近7天统计数据

请求:

curl -X GET "http://localhost:8073/admin/statistics/home-data?time_range=7days" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

JavaScript:

const response = await fetch('/admin/statistics/home-data?time_range=7days', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
const data = await response.json()

响应:

{
  "today_pending_files": 8,
  "monthly_reviewed_files": 85,
  "monthly_review_growth": {
    "value": 30.5,
    "is_up": true
  },
  "monthly_pass_rate": 88.2,
  "pass_rate_growth": {
    "value": 4.0,
    "is_up": true
  },
  "issues_detected": 25,
  "issues_growth": {
    "value": 15.0,
    "is_up": false
  }
}

4. 按文档类型筛选(单个类型)

请求:

curl -X GET "http://localhost:8073/admin/statistics/home-data?type_ids=1" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

JavaScript:

const response = await fetch('/admin/statistics/home-data?type_ids=1', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
const data = await response.json()

Axios:

const { data } = await axios.get('/admin/statistics/home-data', {
  params: { type_ids: '1' }
})

5. 按文档类型筛选(多个类型)

请求:

curl -X GET "http://localhost:8073/admin/statistics/home-data?type_ids=1,2,3" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

JavaScript:

const typeIds = [1, 2, 3]
const response = await fetch(`/admin/statistics/home-data?type_ids=${typeIds.join(',')}`, {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
const data = await response.json()

Axios:

const { data } = await axios.get('/admin/statistics/home-data', {
  params: { type_ids: '1,2,3' }
})

6. 组合筛选:时间范围 + 文档类型

请求:

curl -X GET "http://localhost:8073/admin/statistics/home-data?time_range=7days&type_ids=1,2" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

JavaScript:

const params = new URLSearchParams({
  time_range: '7days',
  type_ids: '1,2'
})

const response = await fetch(`/admin/statistics/home-data?${params}`, {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
const data = await response.json()

Axios:

const { data } = await axios.get('/admin/statistics/home-data', {
  params: {
    time_range: '7days',
    type_ids: '1,2'
  }
})

7. 完整的Vue组件示例(带下拉选择器)

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'

const stats = ref({})
const loading = ref(false)
const timeRange = ref(null)
const typeIds = ref(null)

// 时间范围选项
const timeRangeOptions = [
  { label: '本月', value: null },
  { label: '今天', value: 'today' },
  { label: '近3天', value: '3days' },
  { label: '近7天', value: '7days' },
  { label: '近30天', value: '30days' }
]

// 文档类型选项(根据实际业务调整)
const typeOptions = [
  { label: '全部类型', value: null },
  { label: '类型1', value: '1' },
  { label: '类型2', value: '2' },
  { label: '类型1+2', value: '1,2' }
]

const fetchStats = async () => {
  loading.value = true
  try {
    const params = {}
    if (timeRange.value) params.time_range = timeRange.value
    if (typeIds.value) params.type_ids = typeIds.value

    const { data } = await axios.get('/admin/statistics/home-data', { params })
    stats.value = data
  } catch (error) {
    console.error('获取统计数据失败:', error)
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  fetchStats()
})
</script>

<template>
  <div class="home-statistics">
    <!-- 筛选器 -->
    <div class="filters">
      <select v-model="timeRange" @change="fetchStats">
        <option v-for="opt in timeRangeOptions" :key="opt.value" :value="opt.value">
          {{ opt.label }}
        </option>
      </select>

      <select v-model="typeIds" @change="fetchStats">
        <option v-for="opt in typeOptions" :key="opt.value" :value="opt.value">
          {{ opt.label }}
        </option>
      </select>
    </div>

    <!-- 统计数据展示 -->
    <div v-if="!loading" class="stats-cards">
      <div class="stat-card">
        <h3>今日待审核</h3>
        <p class="number">{{ stats.today_pending_files }}</p>
      </div>

      <div class="stat-card">
        <h3>已审核文件</h3>
        <p class="number">{{ stats.monthly_reviewed_files }}</p>
        <p class="growth" :class="{ up: stats.monthly_review_growth?.is_up }">
          {{ stats.monthly_review_growth?.is_up ? '↑' : '↓' }}
          {{ stats.monthly_review_growth?.value }}%
        </p>
      </div>

      <div class="stat-card">
        <h3>审核通过率</h3>
        <p class="number">{{ stats.monthly_pass_rate }}%</p>
        <p class="growth" :class="{ up: stats.pass_rate_growth?.is_up }">
          {{ stats.pass_rate_growth?.is_up ? '↑' : '↓' }}
          {{ stats.pass_rate_growth?.value }}%
        </p>
      </div>

      <div class="stat-card">
        <h3>检测到的问题</h3>
        <p class="number">{{ stats.issues_detected }}</p>
        <p class="growth" :class="{ down: !stats.issues_growth?.is_up }">
          {{ stats.issues_growth?.is_up ? '↑' : '↓' }}
          {{ stats.issues_growth?.value }}%
        </p>
      </div>
    </div>

    <div v-else class="loading">加载中...</div>
  </div>
</template>

<style scoped>
.filters {
  display: flex;
  gap: 16px;
  margin-bottom: 24px;
}

.stats-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

.stat-card {
  padding: 20px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.number {
  font-size: 32px;
  font-weight: bold;
  margin: 8px 0;
}

.growth {
  font-size: 14px;
  color: #666;
}

.growth.up {
  color: #52c41a;
}

.growth.down {
  color: #f5222d;
}
</style>

8. 完整的React组件示例(带Ant Design

import { useState, useEffect } from 'react'
import { Card, Select, Spin, Statistic, Row, Col } from 'antd'
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons'
import axios from 'axios'

const { Option } = Select

function HomeStatistics() {
  const [stats, setStats] = useState({})
  const [loading, setLoading] = useState(false)
  const [timeRange, setTimeRange] = useState(null)
  const [typeIds, setTypeIds] = useState(null)

  // 时间范围选项
  const timeRangeOptions = [
    { label: '本月', value: null },
    { label: '今天', value: 'today' },
    { label: '近3天', value: '3days' },
    { label: '近7天', value: '7days' },
    { label: '近30天', value: '30days' }
  ]

  const fetchStats = async () => {
    setLoading(true)
    try {
      const params = {}
      if (timeRange) params.time_range = timeRange
      if (typeIds) params.type_ids = typeIds

      const { data } = await axios.get('/admin/statistics/home-data', { params })
      setStats(data)
    } catch (error) {
      console.error('获取统计数据失败:', error)
    } finally {
      setLoading(false)
    }
  }

  useEffect(() => {
    fetchStats()
  }, [timeRange, typeIds])

  return (
    <div style={{ padding: '24px' }}>
      {/* 筛选器 */}
      <div style={{ marginBottom: '24px', display: 'flex', gap: '16px' }}>
        <Select
          placeholder="选择时间范围"
          value={timeRange}
          onChange={setTimeRange}
          style={{ width: 200 }}
        >
          {timeRangeOptions.map(opt => (
            <Option key={opt.value} value={opt.value}>{opt.label}</Option>
          ))}
        </Select>

        <Select
          placeholder="选择文档类型"
          value={typeIds}
          onChange={setTypeIds}
          style={{ width: 200 }}
        >
          <Option value={null}>全部类型</Option>
          <Option value="1">类型1</Option>
          <Option value="2">类型2</Option>
          <Option value="1,2">类型1+2</Option>
        </Select>
      </div>

      {/* 统计卡片 */}
      <Spin spinning={loading}>
        <Row gutter={16}>
          <Col span={6}>
            <Card>
              <Statistic
                title="今日待审核"
                value={stats.today_pending_files}
              />
            </Card>
          </Col>

          <Col span={6}>
            <Card>
              <Statistic
                title="已审核文件"
                value={stats.monthly_reviewed_files}
                suffix={
                  stats.monthly_review_growth && (
                    <span style={{
                      color: stats.monthly_review_growth.is_up ? '#52c41a' : '#f5222d',
                      fontSize: '14px'
                    }}>
                      {stats.monthly_review_growth.is_up ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
                      {stats.monthly_review_growth.value}%
                    </span>
                  )
                }
              />
            </Card>
          </Col>

          <Col span={6}>
            <Card>
              <Statistic
                title="审核通过率"
                value={stats.monthly_pass_rate}
                precision={1}
                suffix="%"
                valueStyle={{ color: '#3f8600' }}
              />
            </Card>
          </Col>

          <Col span={6}>
            <Card>
              <Statistic
                title="检测到的问题"
                value={stats.issues_detected}
                valueStyle={{ color: '#cf1322' }}
              />
            </Card>
          </Col>
        </Row>
      </Spin>
    </div>
  )
}

export default HomeStatistics

错误处理

常见错误码

HTTP状态码 错误场景 响应示例 处理建议
200 成功 正常数据 -
400 参数错误 {"detail": "time_range 参数值无效..."} 检查参数格式
401 未认证 {"detail": "未提供认证凭据"} 检查Token是否传递
403 权限不足 {"detail": "此接口仅限管理员访问"} 检查用户权限
500 服务器错误 {"detail": "获取首页统计数据失败..."} 联系后端排查

错误响应格式

{
  "detail": "错误描述信息"
}

错误示例

1. time_range 参数无效

请求:

curl "http://localhost:8073/admin/statistics/home-data?time_range=invalid"

响应: HTTP 400

{
  "detail": "time_range 参数值无效,应为: today, 3days, 7days, 30days,实际为: invalid"
}

2. type_ids 参数格式错误

请求:

curl "http://localhost:8073/admin/statistics/home-data?type_ids=abc"

响应: HTTP 400

{
  "detail": "type_ids 参数格式错误,应为逗号分隔的整数,实际为: abc"
}

3. Token过期或无效

请求:

curl "http://localhost:8073/admin/statistics/home-data" \
  -H "Authorization: Bearer INVALID_TOKEN"

响应: HTTP 401

{
  "detail": "认证凭据无效或已过期"
}

前端错误处理示例

const fetchStats = async () => {
  try {
    const { data } = await axios.get('/admin/statistics/home-data', {
      params: { time_range: 'today' }
    })
    return data
  } catch (error) {
    // 处理不同的错误情况
    if (error.response) {
      switch (error.response.status) {
        case 400:
          console.error('参数错误:', error.response.data.detail)
          // 提示用户检查筛选条件
          break
        case 401:
          console.error('未认证,请重新登录')
          // 跳转到登录页
          window.location.href = '/login'
          break
        case 403:
          console.error('权限不足')
          // 提示用户权限不足
          break
        case 500:
          console.error('服务器错误:', error.response.data.detail)
          // 显示错误提示
          break
        default:
          console.error('未知错误:', error)
      }
    } else if (error.request) {
      console.error('网络错误,请检查网络连接')
    } else {
      console.error('请求配置错误:', error.message)
    }
  }
}

权限说明

三级权限体系

用户角色 权限范围 能看到的数据 示例
省级管理员 全省 所有地市的数据汇总 可以看到广州+深圳+梅州...所有地市
地市管理员 本地市 所在地市的所有用户数据 梅州管理员只能看梅州的数据
普通用户 个人 仅自己创建的文档数据 张三只能看张三的数据

权限判断逻辑

系统通过以下步骤判断用户权限:

  1. 查询用户角色

    SELECT role_key FROM user_role
    JOIN roles ON user_role.role_id = roles.id
    WHERE user_role.user_id = {current_user_id}
    
  2. 权限分配

    • role_key = 'province_admin' → 省级管理员
    • user_role = 'admin'is_leader = true → 地市管理员
    • 其他 → 普通用户
  3. 数据过滤

    • 省级管理员:不过滤地市(area = null
    • 地市管理员:过滤地市(area = '梅州'
    • 普通用户:过滤用户IDuser_id = 123

权限验证示例

省级管理员查询:

// Token中包含: { user_role: 'provincial_admin', area: '省级' }
const { data } = await axios.get('/admin/statistics/home-data')
// 返回全省所有数据

地市管理员查询:

// Token中包含: { user_role: 'admin', area: '梅州' }
const { data } = await axios.get('/admin/statistics/home-data')
// 只返回梅州的数据

普通用户查询:

// Token中包含: { user_id: 123, user_role: 'common', area: '梅州' }
const { data } = await axios.get('/admin/statistics/home-data')
// 只返回用户123的数据

注意事项

⚠️ 重要提示

  1. Token必须有效

    • Token必须包含在Authorization头中
    • Token格式: Bearer {token}
    • Token过期需要重新登录
  2. 参数大小写敏感

    • time_range=today
    • time_range=Today
    • time_range=TODAY
  3. type_ids必须是整数

    • type_ids=1,2,3
    • type_ids=1, 2, 3 (自动trim空格)
    • type_ids=a,b,c
  4. 缓存机制

    • 数据缓存20分钟
    • 相同参数20分钟内返回缓存数据
    • 不同参数会触发新的查询
  5. 时间范围对比

    • today vs 昨天
    • 3days vs 前3天
    • 7days vs 前7天
    • 30days vs 前30天
    • 默认(本月)vs 上月

📊 数据统计说明

  1. "已审核"的定义

    • 包括以下状态的文档:
      • audit_status = 1 (通过)
      • 🔄 audit_status = 2 (审核中)
      • audit_status = -1 (拒绝)
      • ⚠️ audit_status = -2 (警告)
    • 不包括:
      • audit_status = 0 (待审核)
      • audit_status = null (未知)
  2. "今日待审核"的定义

    • 仅统计今天00:00之后创建的文档
    • audit_status = 0audit_status = null
  3. 通过率计算

    • 公式: (通过数量 / 已审核总数) * 100
    • 只有audit_status = 1算通过
    • 如果已审核总数为0,通过率为0.0%
  4. 增长率计算

    • 使用绝对值(不带正负号)
    • 通过is_up判断增长/下降
    • 如果上期数据为0,增长率固定为100%

🔄 刷新策略建议

// 推荐的刷新策略
// 1. 首次加载
onMounted(() => fetchStats())

// 2. 定时刷新(5分钟)
setInterval(() => fetchStats(), 5 * 60 * 1000)

// 3. 用户切换筛选条件时立即刷新
watch([timeRange, typeIds], () => fetchStats())

// 4. 用户手动刷新
const handleRefresh = () => {
  // 清除缓存的方式:随机添加时间戳(不推荐,会绕过缓存)
  // const timestamp = Date.now()
  // fetchStats({ _t: timestamp })

  // 推荐:直接调用,利用缓存
  fetchStats()
}

🎨 UI展示建议

  1. 增长指标展示

    // 使用颜色区分增长/下降
    const getGrowthColor = (growth) => {
      return growth.is_up ? '#52c41a' : '#f5222d'
    }
    
    // 使用图标
    const getGrowthIcon = (growth) => {
      return growth.is_up ? '↑' : '↓'
    }
    
  2. 数值格式化

    // 大数值使用千分位
    const formatNumber = (num) => {
      return num.toLocaleString('zh-CN')
    }
    
    // 示例: 1234 → 1,234
    
  3. 空数据处理

    // 当数据为0时的友好提示
    const displayValue = (value) => {
      return value > 0 ? value : '暂无数据'
    }
    

🐛 常见问题 FAQ

Q1: 为什么返回的数据都是0

A: 可能的原因:

  • 时间范围内没有文档(如选择"今天"但今天没创建文档)
  • 文档类型筛选过滤掉了所有数据
  • 用户权限限制(普通用户只能看自己的数据)
  • 数据库中确实没有符合条件的数据

Q2: 为什么切换时间范围后数据没变化?

A: 可能是Redis缓存,等待20分钟缓存过期或者联系后端清除缓存。

Q3: 增长率为什么总是100%

A: 当上个周期数据为0,当前周期有数据时,增长率固定显示100%。

Q4: 省级管理员和地市管理员看到的数据一样?

A: 如果省级管理员的地市也是"梅州",且数据库中只有梅州的数据,两者看到的会一样。省级管理员应该看到所有地市的汇总。

Q5: 通过率计算是否包含"审核中"的文档?

A: 通过率 = 通过数量 / 已审核总数。

  • 已审核总数包含"审核中"status=2
  • 但通过数量只计算status=1的文档
  • 所以"审核中"会降低通过率

版本历史

版本 日期 更新内容
v3.0 2025-11-22 🎉 新增时间范围筛选功能(today/3days/7days/30days
v2.1 2025-11-22 "已审核"统计包含"审核中"状态(audit_status=2
v2.0 2025-11-20 🔒 完善三级权限隔离机制
v1.0 2025-11-15 🚀 初始版本发布

技术支持

如有疑问,请联系后端开发团队或查看:

  • 📖 完整API文档: /docs/api/
  • 🐛 问题反馈: GitHub Issues
  • 💬 技术讨论: 开发团队微信群

文档生成时间: 2025-11-22 文档维护: 后端开发团队 最后更新: Claude Code 🤖