依据:同目录
大屏畜牧资源功能需求.mdv1.3。数据源为 AI 诊断 既有表(会话、消息、使用用户台账)。本模块为大屏只读看板;不统计移动端「畜牧资源」资讯视图v_livestock_resource。
| 项 | 说明 |
|---|---|
| 后端 | RuoYi v3.9.2(springboot2 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| 数据库 | MySQL 5.7.39,InnoDB,utf8mb4 |
| 前端 | ruoyi-screen 畜牧资源专题页(/livestock-resources);切换 statYear 拉取看板主数据(本期单接口) |
| 代码包 | com.ruoyi.web.modules.screen(LivestockResourceScreenController / ILivestockResourceScreenService / vo / support) |
分层:Controller → ILivestockResourceScreenService(口径编排、statDate/statDateTime)→ LivestockResourceScreenMapper(只读 SQL)+ LivestockResourceScreenSupport(7 日/24 小时补点、分类归一、占比、分档、关键词)。
| 场景 | 行为 |
|---|---|
进入 / 切换 statYear |
调用 §3.1 |
| 数据权限 | 全县聚合;不按提问人/机构过滤 |
统计日 statDate |
复用 HomeScreenSupport.resolveStatDate(Y):Y=当前年 → 今日(Asia/Shanghai);历史年 → Y-12-31 |
统计时刻 statDateTime |
请求时 LocalDateTime.now(Asia/Shanghai);响应回显用 |
| 年内上界 | 会话 create_time;AI 消息 create_time ≤ statDate 23:59:59(当前年) |
| 「今日」四项 | 仅当 Y = 当前自然年时按 statDate 自然日统计;否则固定 0 |
| 用户结构 | biz_consult_user 全表快照,不随 Y 变 |
| 7 日 / 分时 | 7 日以 statDate 滚动;分时为 距 statDateTime 最近 24 整点小时,与 Y 无关 |
| 已隐藏会话 | 不过滤 vet_visible(对齐功能需求 §2.18#2) |
| 性能 | 默认实时聚合;可选 §2.3 缓存表 |
依赖表(只读)
| 表 | 用途 |
|---|---|
biz_consult_session |
AI 会话数、用户去重、7 日/24 小时会话、分档分母 |
biz_consult_message |
提问量(AI 回复)、耗时、分类、状态、关键词语料 |
biz_consult_user |
新老用户结构 |
业务常量(与 ConsultSessionRules / AiConsultConstants 一致)
| 常量 | 值 |
|---|---|
consult_type AI 问诊 |
2 |
sender_role 提问人 / AI |
1 / 3 |
AI 消息 sender_user_id |
0 |
msg_type 文本 |
1 |
| 提问分类编码 | 1~4(养殖技巧/疾病诊疗/用药指导/设备操作) |
无新增业务台账表。 DDL:sql/biz_consult_session.sql、sql/biz_consult_message.sql、sql/biz_consult_user.sql。
当前问诊表无
del_flag;纳入条件为:consult_type=2的会话及其消息。已隐藏(vet_visible=0)仍统计。
记 statYear = Y,statDate、statDateTime 见 §1。
| 边界 | 表达式 |
|---|---|
| 年区间起 | Y-01-01 00:00:00 |
年区间止(会话 create_time、AI 消息 create_time) |
statDate 23:59:59 |
| 统计日区间 | statDate 00:00:00 ~ statDate 23:59:59 |
| 近 7 日 | [statDate − 6 日 00:00:00, statDate 23:59:59] |
| 最近 24 小时 | [floor(statDateTime 到整点) − 23 小时, floor(statDateTime 到整点) + 59 分 59 秒];SQL 按 DATE_FORMAT(create_time,'%Y%m%d%H') 分组 |
会话别名(下文):s = biz_consult_session,条件 s.consult_type = 2。
AI 消息别名:m_ai = biz_consult_message,m_ai.sender_role = 3 AND m_ai.sender_user_id = 0,且 EXISTS 关联 s.id = m_ai.session_id。
提问人消息别名:m_ask = biz_consult_message,m_ask.sender_role = 1,且关联 AI 会话 s。
biz_livestock_resource_screen_cache(性能优化)非本期必建。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
bigint(20) |
主键 |
stat_year |
int(11) |
统计年份 |
payload_json |
mediumtext |
§3.1 响应 JSON |
expire_time |
datetime |
过期时间 |
create_time |
datetime |
写入时间 |
索引:UNIQUE uk_stat_year (stat_year)。TTL 1~5 分钟。
| 指标 | SQL 要点 |
|---|---|
| 累计使用用户 | COUNT(DISTINCT s.asker_user_id),s.create_time ∈ 年区间 |
| 今日活跃用户 | Y=当前年:COUNT(DISTINCT asker_user_id),s.update_time ∈ 统计日;否则 0 |
| 日新增用户 | Y=当前年:create_time 与 update_time 均在统计日,且 asker_user_id 不在 create_time < 统计日 的会话中;否则 0 |
| 累计会话数 | COUNT(s.id),s.create_time ∈ 年区间 |
| 今日会话数 | Y=当前年:COUNT(s.id),s.create_time ∈ 统计日区间;否则 0 |
| 累计提问量 | COUNT(m_ai.id),m_ai.create_time ∈ 年区间 |
| 今日提问量 | Y=当前年:COUNT(m_ai.id),m_ai.create_time ∈ 统计日;否则 0 |
| 平均响应时长(秒) | AVG(m_ai.cost_time) / 1000.0,年区间内且 m_ai.cost_time IS NOT NULL(含 0);无记录 → 0 |
| 维度 | SQL 要点 |
|---|---|
| 会话数/日 | DATE(s.create_time) 分组,COUNT(s.id) |
| 提问量/日 | DATE(m_ai.create_time) 分组,COUNT(m_ai.id) |
应用层:固定输出 7 个点(statDate−6 … statDate),缺日补 0。
biz_consult_user| 类型 | 条件 |
|---|---|
| 新用户 | DATEDIFF(last_use_time, first_use_time) <= 180 |
| 老用户 | > 180 |
COUNT(*) 各一类;占比 = 人数 / (新+老),不加 Y 条件。
| 项 | 条件 |
|---|---|
| 统计行 | m_ai(sender_role=3、sender_user_id=0),create_time ∈ 年区间 |
| 分类键 | 同会话内 send_time ≤ m_ai.send_time 的最近提问人消息 ai_category;空 → UNCAT |
| 条数 | GROUP BY 分类键 COUNT(*);MAX(m_ai.create_time) 供热点二次排序 |
| 占比 | 分类 AI 回复条数 / 当年全部 AI 回复 × 100 |
| 热点 Top5 | 条数降序;同分按 lastEventTime 降序;取 5 |
| 维度 | SQL 要点 |
|---|---|
| 会话 | s.create_time ∈ 24h 窗口,GROUP BY DATE_FORMAT(create_time,'%Y%m%d%H') |
| 提问量 | m_ai.create_time ∈ 24h 窗口,同上分组 |
应用层:固定 24 桶(锚点整点向前 23 小时);bucketStart=HH:mm;bucketDateTime=yyyy-MM-dd HH:mm;hourlyHeat.windowEnd = 锚点整点 yyyy-MM-dd HH:mm。
子查询:按 session_id 统计年内 m_ai 条数 reply_cnt,再分档:
| 档位 code | reply_cnt |
|---|---|
LIGHT |
1~2 |
NORMAL |
3~4 |
ACTIVE |
5~10 |
DEEP |
>10 |
COUNT(session_id) / 年内会话总数 → 占比;四档固定返回,无数据为 0。
| 状态 | 条件 |
|---|---|
OK 正常应答 |
m_ai.cost_time IS NOT NULL(含 0) |
FAIL 调用失败 |
m_ai.cost_time IS NULL |
年内 COUNT(*) 及占比;两者之和 = 年内 AI 消息总数。
| 项 | 条件 |
|---|---|
| 语料 | m_ask.msg_type = 1,m_ask.content 非空,m_ask.send_time ∈ 年区间 |
| 处理 | LivestockResourceScreenSupport.extractTopKeywords(texts, 30):分词 + 停用词(application.yml 可配路径或内置短表) |
| 表 | 建议 |
|---|---|
biz_consult_session |
(consult_type, create_time)、(consult_type, update_time) |
biz_consult_message |
(session_id, sender_role, create_time)、(create_time, sender_role) |
| Mapper | 方法 | 说明 |
|---|---|---|
LivestockResourceScreenMapper |
selectOverview(...) |
总览 8 项(可拆 2~3 条 SQL) |
selectDailySessionCounts(start, end) |
7 日会话 | |
selectDailyAiReplyCounts(start, end) |
7 日提问量 | |
selectUserStructure() |
新老用户 | |
selectAskerCategoryCounts(yearStart, yearEnd) |
AI 回复按分类键聚合 | |
selectAiReplyTotal(yearStart, yearEnd) |
分类占比分母(年内 AI 回复总数) | |
selectHourlySessionCounts(windowStart, windowEnd) |
最近 24h 会话 | |
selectHourlyAiReplyCounts(windowStart, windowEnd) |
最近 24h 提问量 | |
selectSessionReplyBuckets(yearStart, yearEnd) |
四档会话数 | |
selectYearSessionTotal(yearStart, yearEnd) |
年内会话总数 | |
selectAiReplyStatusCounts(yearStart, yearEnd) |
正常/失败 | |
selectTextAskerContents(yearStart, yearEnd) |
关键词语料 | |
selectDistinctSessionYears() |
可选年份下拉 |
XML:mapper/screen/LivestockResourceScreenMapper.xml。
统一响应:AjaxResult(code / msg / data)。
Base Path:/bigScreen/livestockResource
权限标识(示例):bigScreen:livestockResource:query
| # | 说明 | Method | URI |
|---|---|---|---|
| 3.1 | 看板主数据(十区块合一) | GET | /bigScreen/livestockResource/dashboard |
前端:进入页 / 切换 statYear / 刷新 → 仅 3.1。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
statYear |
string | Y | 四位年份;非法 → 业务异常(AchievementReportValidation.parseStatYear) |
forceRefresh |
boolean | N | 默认 false;true 时跳过 §2.3 缓存 |
data(顶层)| 字段 | 类型 | 说明 |
|---|---|---|
statYear |
int | 回显 |
statDate |
string | yyyy-MM-dd |
statDateTime |
string | yyyy-MM-dd HH:mm:ss,24h 窗口终点 |
availableYears |
int[] | biz_consult_session 在 consult_type=2 下 YEAR(create_time) 并集 + 当前年,降序 |
overview |
object | 核心总览 §3.1.1 |
activityTrend |
object | 活跃度趋势 §3.1.2 |
userStructure |
object | 用户结构 §3.1.3 |
categoryShare |
object | 提问分类占比 §3.1.4 |
modelCallAnalysis |
array | 模型调用分析(与分类同源) §3.1.5 |
hourlyHeat |
object | 分时热度 §3.1.6 |
sessionReplyDistribution |
array | 会话提问数量分布 §3.1.7 |
replyStatusDistribution |
object | 会话状态分布 §3.1.8 |
hotTopics |
array | 热点问题榜单 Top5 §3.1.9 |
topKeywords |
array | 高频关键词 Top30 §3.1.10 |
overview| 字段 | 类型 | 说明 |
|---|---|---|
totalUsers |
long | 累计使用用户 |
todayActiveUsers |
long | 今日活跃用户 |
todayNewUsers |
long | 日新增用户 |
totalSessions |
long | 累计会话数 |
todaySessions |
long | 今日会话数 |
totalQuestionCount |
long | 累计提问量(AI 回复条数) |
todayQuestionCount |
long | 今日提问量 |
avgResponseSeconds |
decimal | 平均响应时长(秒),1 位小数 |
activityTrend| 字段 | 类型 | 说明 |
|---|---|---|
dailySeries |
array | 固定 7 条 |
dailySeries[]:
| 字段 | 说明 |
|---|---|
date |
yyyy-MM-dd |
sessionCount |
会话数 |
questionCount |
提问量(AI 回复) |
userStructure| 字段 | 类型 | 说明 |
|---|---|---|
newUserCount |
long | 新用户 |
oldUserCount |
long | 老用户 |
newUserRatio |
decimal | 占比 0~100 |
oldUserRatio |
decimal | 占比 0~100 |
categoryShare| 字段 | 类型 | 说明 |
|---|---|---|
totalAskerMessages |
long | 分母:年内全部 AI 回复(字段名沿用,与提问人无关) |
items |
array | 分类项 |
items[]:categoryCode、categoryName、count、ratio(0~100,1 位小数)。固定含 1~4 + UNCAT(未分类),无数据 count=0。
modelCallAnalysis[]与 categoryShare.items 同源(可复用引用):categoryCode、categoryName、count。展示为柱状图时前端取 count。
hourlyHeat| 字段 | 类型 | 说明 |
|---|---|---|
windowEnd |
string | 锚点整点 yyyy-MM-dd HH:mm |
hourlySeries |
array | 固定 24 条 |
hourlySeries[]:bucketStart(HH:mm)、bucketDateTime(yyyy-MM-dd HH:mm,悬停)、sessionCount、questionCount。
sessionReplyDistribution[]固定 4 条。
| 字段 | 说明 |
|---|---|
bandCode |
LIGHT / NORMAL / ACTIVE / DEEP |
bandName |
轻度 / 一般 / 活跃 / 深度 |
sessionCount |
会话数 |
ratio |
占年内会话总数 % |
replyStatusDistribution| 字段 | 说明 |
|---|---|
totalAiReplies |
年内 AI 回复总数 |
items |
固定 2 条:OK(正常应答)、FAIL(调用失败),含 count、ratio |
hotTopics[]最多 5 条:categoryCode、categoryName、count、ratio,按 ratio 或 count 降序。
topKeywords[]最多 30 条:keyword、count。
{
"statYear": 2026,
"statDate": "2026-05-20",
"statDateTime": "2026-05-20 15:30:00",
"overview": {
"totalUsers": 120,
"todayActiveUsers": 8,
"todayNewUsers": 2,
"totalSessions": 350,
"todaySessions": 5,
"totalQuestionCount": 980,
"todayQuestionCount": 12,
"avgResponseSeconds": 3.2
},
"activityTrend": {
"dailySeries": [
{ "date": "2026-05-14", "sessionCount": 3, "questionCount": 10 }
]
},
"categoryShare": {
"totalAskerMessages": 500,
"items": [
{ "categoryCode": "2", "categoryName": "疾病诊疗", "count": 200, "ratio": 40.0 }
]
}
}
1. year = parseStatYear(statYear)
2. statDate = resolveStatDate(year); statDateTime = now(Shanghai)
3. yearStart/end, dayStart/end, sevenStart, window24Start = Support.bounds(...)
4. overview = Mapper 总览;avgResponseSeconds = Support.roundSeconds(avgMs/1000)
5. activityTrend = Mapper 7日 + buildDailySeries(7, statDate)
6. userStructure = Mapper 新老 + ratio
7. category = Mapper 分类 + total;categoryShare/modelCall/hotTopics = Support.buildCategoryViews(...)
8. hourlyHeat = Mapper 24h + buildHourlySeries(24, statDateTime)
9. sessionReplyDistribution = Mapper 分档 + ratio
10. replyStatusDistribution = Mapper OK/FAIL + ratio
11. topKeywords = Support.extractTopKeywords(Mapper 文本语料, 30)
12. availableYears = distinct session years ∪ current year
13. return AjaxResult.success(vo)
| 类型 | 名称 | 权限标识 |
|---|---|---|
| 菜单 | 大屏畜牧资源 | bigScreen:livestockResource:query |
SQL 示例:sql/big_screen_livestock_resource_perm.sql(挂载「大屏」父菜单下)。
LivestockResourceScreenController + ILivestockResourceScreenService + VO + LivestockResourceScreenSupportLivestockResourceScreenMapper + XML(§2.6)ruoyi-screen 对接 GET /bigScreen/livestockResource/dashboard(本期不改前端)dashboardbiz_consult_user、消息 cost_time/ai_category 字段已上线| 版本 | 日期 | 说明 |
|---|---|---|
| 1.0 | 2026-05-20 | 初稿:无新表;单接口十区块;对齐 大屏畜牧资源功能需求.md v1.1 |
| 1.2 | 2026-05-20 | 对齐草稿:今日活跃 update_time、日新增双时间、AI 回复+分类归因、统计日 0~23 时分、create_time 消息归属 |