大屏 — 交易销售统计 — 技术方案
依据:同目录 大屏交易销售功能需求.md v1.0。数据源为交易市场平台既有表(订单、明细、供应商、承销商、日度行情、等级重量配置等)。本模块为大屏只读看板;农资商城相关表与指标本期不设计、不实现(对齐功能需求 §3.2)。
1. 技术架构
| 项 |
说明 |
| 后端 |
RuoYi v3.9.2(springboot2 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| 数据库 |
MySQL 5.7.39,InnoDB,utf8mb4 |
| 前端 |
大屏交易销售专题页;切换 statYear 拉取看板主数据(本期单接口) |
| 代码包 |
com.ruoyi.web.modules.screen(TradeSalesScreenController / ITradeSalesScreenService / vo / support) |
分层:Controller → ITradeSalesScreenService(口径编排、statDate 计算)→ TradeSalesScreenMapper(只读 SQL)+ TradeSalesScreenSupport(行情 7 日聚合、12 月补 0、占比)。
| 场景 |
行为 |
进入 / 切换 statYear |
调用 §3.1 |
| 数据权限 |
全县聚合;不按供应商/用户过滤 |
统计日 statDate |
Y = 当前年 → 今日(Asia/Shanghai);Y 为历史年 → Y-12-31 |
| 年内订单上界 |
finish_time ≤ statDate 23:59:59(当前年);历史年 ≤ Y-12-31 23:59:59 |
| 入驻商铺数 |
biz_supplier 实时快照,不随 statYear 变 |
| 产地行情近 7 日 |
以 statDate 为终点 [statDate−6, statDate],与 Y 无关 |
| 按月图表 |
横轴固定 12 月;缺月、当前年晚于 statDate 的月份均为 0 |
| 性能 |
默认实时聚合;可选 §2.3 缓存表 |
依赖表(只读,本期)
| 表 |
用途 |
biz_trade_order |
已完成订单汇总、按月趋势、销售去向关联 |
biz_trade_order_line |
品质等级(grade_code)按行统计 |
biz_supplier |
入驻商铺数(有效供应商) |
biz_distributor |
销售去向 sales_destination(1 本市 / 2 本省 / 3 外省) |
biz_market_daily_quote |
产地行情近 7 日 |
biz_trade_market |
解析默认行情市场(名称「巴青牦牛交易市场」) |
biz_grade_weight_config |
等级编码 → 展示名称 |
不包含:农资商城订单、店铺、品类、评价等任意表与字段。
2. 数据库设计
2.1 本模块业务表
无新增业务台账表。 业务 DDL 以 sql/biz_trade_order.sql、sql/biz_supplier.sql、sql/biz_distributor.sql、sql/biz_market_daily_quote.sql、sql/biz_trade_market.sql、sql/biz_grade_weight_config.sql 为准。
2.2 公共参数
记 statYear = Y,statDate 见 §1。
| 边界 |
表达式 |
| 年区间起 |
Y-01-01 00:00:00 |
| 年区间止(已完成订单) |
statDate 23:59:59(见 §1) |
| 行情 7 日起 |
statDate − 6 日(含) |
| 行情 7 日止 |
statDate(含) |
| 按月分组键 |
MONTH(finish_time),应用层补全 1~12 |
默认行情市场(配置)
| 项 |
说明 |
| 优先 |
application.yml:bigscreen.trade-sales.default-market-name(默认 巴青牦牛交易市场) |
| 解析 |
biz_trade_market:del_flag='0' 且 market_name 等于配置值 → 取 id 作为 trade_market_id 过滤条件 |
| 未匹配 |
hasQuoteData=false,行情区块空态;日志告警 |
2.3 可选表 biz_trade_sales_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 分钟(与功能需求 §7.3 一致)。
2.4 查询口径(按表)
交易订单 biz_trade_order(对齐功能需求 §2.5)
| 项 |
条件 |
| 纳入 |
del_flag='0' AND order_status='1'(已完成) |
| 按年 |
finish_time ∈ [年区间起, 年区间止] |
| 总览 |
COUNT(*) 订单笔数;SUM(total_heads);SUM(total_amount) |
| 按月 |
GROUP BY MONTH(finish_time) → 当月 SUM(total_heads)、SUM(total_amount) |
销售去向(对齐 §2.8)
| 项 |
条件 |
| 关联 |
biz_trade_order o JOIN biz_distributor d ON o.distributor_id = d.id AND d.del_flag='0' |
| 订单 |
同 §2.4 已完成且在 Y 年内 |
| 分组 |
d.sales_destination(1 / 2 / 3) |
| 指标 |
SUM(o.total_heads)(头);占比分母 = 当年已完成订单 SUM(total_heads) |
应用层固定返回 3 档(本市 / 本省 / 外省),无数据为 0 头、ratio=0。
品质等级 biz_trade_order_line(对齐 §2.9)
| 项 |
条件 |
| 关联 |
line JOIN biz_trade_order o(同上已完成、年内) |
| 分组 |
line.grade_code |
| 名称 |
聚合时 max(line.grade_name);无快照时回退 biz_grade_weight_config.grade_name |
| 指标 |
COUNT(*) 明细行(头);占比分母 = 当年已完成订单明细总行数 |
| 空等级 |
grade_code IS NULL 的行不计入品质占比;若分母为 0 → hasGradeData=false |
应用层按配置表 sort_order 输出各等级;配置表有而当年无数据的等级 count=0、ratio=0(可选,与产品约定;默认仅返回 count>0 的等级 + 固定四档补 0,推荐固定返回配置表全部等级便于饼图)。
入驻商铺 biz_supplier(对齐 §2.10)
| 项 |
条件 |
| 指标 |
COUNT(*) |
| 条件 |
del_flag='0' |
| 与 Y |
无年份条件 |
日度行情 biz_market_daily_quote(对齐 §2.6、§2.7)
| 项 |
条件 |
| 范围 |
del_flag='0' AND trade_market_id = {默认市场ID} AND quote_date BETWEEN 7 日起止 |
| 原始行 |
窗口内全部日度记录(含多维度:公母、重量档等) |
应用层按 quote_date 聚合(TradeSalesScreenSupport)
| 聚合项 |
规则 |
| 当日最低价 |
该日各条 price_min 的 MIN |
| 当日最高价 |
该日各条 price_max 的 MAX |
| 当日均价 |
该日各条 avg_price 的 算术平均 |
| 7 日卡片 |
7 个自然日「当日均价」有效值的平均;7 日内「当日最低价」的最小、「当日最高价」的最大;lastQuoteDate = 窗口内最大 quote_date |
| 价格单位 |
窗口内 quote_date 最大那天的记录中 create_time 最新一条的 price_unit(1 元/头 / 2 元/斤) |
| 缺日 |
7 日横轴仍输出该日;minPrice/maxPrice/avgPrice 均为 0(对齐功能需求 §5.3.2 默认) |
2.5 索引建议(既有表)
| 表 |
建议 |
biz_trade_order |
(order_status, del_flag, finish_time) 或 idx_finish_time |
biz_trade_order_line |
(order_id)(已有 idx_order_id) |
biz_market_daily_quote |
(trade_market_id, quote_date, del_flag) |
biz_distributor |
沿用 idx_sales_destination |
2.6 Mapper 扩展(建议)
| Mapper |
方法 |
说明 |
TradeSalesScreenMapper |
selectTradeOverviewByYear(y, endTime) |
总览三项 |
TradeSalesScreenMapper |
selectMonthlyTradeByYear(y, endTime) |
按月头数、金额 |
TradeSalesScreenMapper |
selectSalesDestinationByYear(y, endTime) |
三档头数 |
TradeSalesScreenMapper |
selectQualityGradeByYear(y, endTime) |
按 grade_code 计数 |
TradeSalesScreenMapper |
selectQuotesInWindow(marketId, startDate, endDate) |
行情原始行 |
TradeSalesScreenMapper |
selectDistinctFinishYears() |
可选年份 |
BizSupplierMapper |
countForScreen() |
可复用首页 countForScreen() |
XML:mapper/screen/TradeSalesScreenMapper.xml。
3. 接口设计
统一响应:AjaxResult(code / msg / data)。
权限标识(示例):bigScreen:tradeSales:query
Base Path:/bigScreen/tradeSales
| # |
说明 |
Method |
URI |
权限 |
| 3.1 |
看板主数据(五区块合一) |
GET |
/bigScreen/tradeSales/dashboard |
bigScreen:tradeSales:query |
前端:进入页 / 切换 statYear / 手动刷新 → 仅调用 3.1。
3.1 看板主数据
Query
| 参数 |
类型 |
必填 |
说明 |
statYear |
string |
Y |
四位年份;非法 → 业务异常(复用 AchievementReportValidation.parseStatYear) |
forceRefresh |
boolean |
N |
默认 false;true 时跳过 §2.3 缓存 |
响应 data(顶层)
| 字段 |
类型 |
说明 |
statYear |
int |
回显 |
statDate |
string |
统计时点 yyyy-MM-dd |
availableYears |
int[] |
biz_trade_order 已完成订单 YEAR(finish_time) 并集 + 当前年,降序 |
tradeOverview |
object |
交易总览 §3.1.1 |
originQuote |
object |
产地行情 §3.1.2 |
tradeMonthlyTrend |
array |
数额趋势 §3.1.3 |
salesDestination |
array |
销售去向 §3.1.4 |
qualityGrade |
object |
品质等级 §3.1.5 |
3.1.1 tradeOverview
| 字段 |
类型 |
说明 |
orderCount |
long |
牦牛交易订单数(笔) |
tradeHeads |
long |
牦牛交易量(头) |
tradeAmount |
decimal |
牦牛交易总额(元) |
supplierCount |
long |
入驻商铺数(家),实时 |
3.1.2 originQuote
| 字段 |
类型 |
说明 |
hasQuoteData |
boolean |
7 日内是否存在行情登记 |
avgPrice7d |
decimal |
7 日均价;无数据可为 null |
minPrice7d |
decimal |
7 日最低价 |
maxPrice7d |
decimal |
7 日最高价 |
lastQuoteDate |
string |
最近行情日 yyyy-MM-dd;无则 null |
priceUnit |
int |
1 元/头 / 2 元/斤 |
priceUnitLabel |
string |
如 元/头 |
dailyTrend |
array |
固定 7 条,按日期升序 |
dailyTrend[]:
| 字段 |
说明 |
quoteDate |
yyyy-MM-dd |
minPrice / maxPrice / avgPrice |
当日聚合价;缺日为 0 |
3.1.3 tradeMonthlyTrend[]
固定 12 条。
| 字段 |
说明 |
month |
1~12 |
tradeHeads |
该月已完成订单总头数 |
tradeAmount |
该月已完成订单总金额(元) |
month > statDate.getMonth() 且 Y 为当前年时两项为 0。
3.1.4 salesDestination[]
固定 3 条。
| 字段 |
说明 |
salesDestination |
1 / 2 / 3 |
salesDestinationName |
本市 / 本省 / 外省 |
tradeHeads |
头数 |
ratio |
占比 0~100 数值(如 33.3 表示 33.3%),保留 1 位小数 |
3.1.5 qualityGrade
| 字段 |
说明 |
hasGradeData |
当年是否存在 grade_code 非空的已完成明细 |
totalHeads |
参与占比计算的明细总行数 |
items |
数组 |
items[]:
| 字段 |
说明 |
gradeCode |
A / B / C / D |
gradeName |
一级 / 二级等 |
tradeHeads |
头数 |
ratio |
占比 0~100,保留 1 位小数 |
响应示例(节选)
{
"statYear": 2026,
"statDate": "2026-05-20",
"availableYears": [2026, 2025],
"tradeOverview": {
"orderCount": 12,
"tradeHeads": 48,
"tradeAmount": 960000.00,
"supplierCount": 5
},
"originQuote": {
"hasQuoteData": true,
"avgPrice7d": 32.5,
"minPrice7d": 30.0,
"maxPrice7d": 35.0,
"lastQuoteDate": "2026-05-20",
"priceUnit": 2,
"priceUnitLabel": "元/斤",
"dailyTrend": [
{ "quoteDate": "2026-05-14", "minPrice": 31.0, "maxPrice": 33.0, "avgPrice": 32.0 }
]
},
"tradeMonthlyTrend": [
{ "month": 1, "tradeHeads": 10, "tradeAmount": 200000.00 },
{ "month": 12, "tradeHeads": 0, "tradeAmount": 0 }
],
"salesDestination": [
{ "salesDestination": 1, "salesDestinationName": "本市", "tradeHeads": 20, "ratio": 41.7 }
],
"qualityGrade": {
"hasGradeData": true,
"totalHeads": 48,
"items": [
{ "gradeCode": "B", "gradeName": "二级", "tradeHeads": 30, "ratio": 62.5 }
]
}
}
服务端逻辑(摘要)
1. year = parseStatYear(statYear); statDate = resolveStatDate(year); endTime = statDate 23:59:59
2. tradeOverview = orderMapper 总览(year, endTime) + supplierMapper.countForScreen()
3. tradeMonthlyTrend = orderMapper 按月 + buildMonthlySeries(12, year, statDate)
4. salesDestination = orderMapper JOIN distributor + fill3Buckets + ratio
5. qualityGrade = lineMapper 分组 + join grade_config + ratio; hasGradeData
6. marketId = resolveDefaultMarketId(configName)
7. rows = quoteMapper 7日窗口(marketId); originQuote = Support.aggregateQuote7d(rows, statDate)
8. availableYears = distinct finish years ∪ current year
9. return AjaxResult.success(vo)
4. 菜单与权限(示例)
| 类型 |
名称 |
权限标识 |
| 菜单 |
大屏交易销售 |
bigScreen:tradeSales:query |
SQL 示例:sql/big_screen_trade_sales_perm.sql(挂载「大屏」父菜单下)。
5. 交付清单
6. 修订记录
| 版本 |
日期 |
说明 |
| 1.0 |
2026-05-20 |
初稿:无新表;单接口五区块;对齐 大屏交易销售功能需求.md v1.0;农资商城不纳入 |