西藏巴青项目

大屏交易销售技术方案.md 20KB

大屏 — 交易销售统计 — 技术方案

依据:同目录 大屏交易销售功能需求.md v1.3
牦牛交易:交易市场平台既有表(订单、明细、供应商、承销商、日度行情、等级重量配置等),本服务 MyBatis 只读聚合。
农资商城不直连商城库表;经 MallStatsOpenApiClient 调用农资商城 Open API GET /api/open/stats/overview(口径见商城项目 doc/平台后台/外部接口/商城数据统计技术方案.md)。
本模块为大屏只读看板;看板主接口仍为单接口 GET /bigScreen/tradeSales/dashboard


1. 技术架构

说明
后端 RuoYi v3.9.2springboot2 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid
数据库 MySQL 5.7.39,InnoDB,utf8mb4
前端 大屏交易销售专题页;切换 statYear 拉取看板主数据(单接口
代码包(牦牛) com.ruoyi.web.modules.screenTradeSalesScreenController / ITradeSalesScreenService / vo / support
代码包(商城) com.ruoyi.web.modules.screen.mallstatsMallStatsOpenApiClient / MallStatsOpenProperties / Token / HTTP)

分层(牦牛)ControllerITradeSalesScreenService(口径编排、statDate 计算)→ TradeSalesScreenMapper(只读 SQL)+ TradeSalesScreenSupport(行情 7 日聚合、滚动 12 月趋势、占比)。

分层(商城)TradeSalesScreenServiceImpl.mergeMallStatsMallStatsOpenApiClient.fetchOverview(HTTP + 本地 Redis 缓存)→ 写入 TradeSalesDashboardVo 农资字段;失败降级空态不阻断牦牛区块。

场景 行为
进入 / 切换 statYear 调用 §3.1(牦牛 SQL + 商城 overview 一次 HTTP
商城未启用 / 调用失败 mallStatsAvailable=false;右栏空态;左栏正常
商城性能 优先 overview 一次拉六项;本地 Redis TTL 默认 5 minbigscreen.mall-stats.cache-ttl-minutes);商城侧另有 Redis
数据权限 全县聚合;不按供应商/用户过滤
统计日 statDate Y = 当前年 → 今日(Asia/Shanghai);Y 为历史年 → Y-12-31
年内订单上界 finish_timestatDate 23:59:59(当前年);历史年 ≤ Y-12-31 23:59:59
入驻商铺数 biz_supplier 实时快照statYear
产地行情近 7 日 statDate 为终点 [statDate−6, statDate]与 Y 无关
按月趋势图 滚动最近 12 月(牦牛交易、商城订单);锚点 HomeScreenSupport.currentYearMonth()statYear
性能 牦牛:默认实时聚合;可选 §2.3 缓存表;商城:overview + 本地 Redis

依赖表(只读 — 牦牛侧)

用途
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 等级编码 → 展示名称

外部依赖(只读 — 农资商城 Open API)

说明
基路径 {bigscreen.mall-stats.base-url}/api/open/stats
聚合接口 GET /overview?statYear=(可选,默认当年; statDate 参数)
认证 请求头 X-Open-Token(AES-128-CBC 加密 UUID;Key/IV 见 §4.1
响应 商城 AjaxResultdata 含六项 VO(与单接口字段一致)
口径文档 商城项目 doc/平台后台/外部接口/商城数据统计技术方案.md v1.2(品类/热销/区域按 统计年 YEAR(finish_time)

不包含:本服务直连农资商城订单/店铺/品类/评价等任意表


2. 数据库设计

2.1 本模块业务表

无新增业务台账表。 业务 DDL 以 sql/biz_trade_order.sqlsql/biz_supplier.sqlsql/biz_distributor.sqlsql/biz_market_daily_quote.sqlsql/biz_trade_market.sqlsql/biz_grade_weight_config.sql 为准。

2.2 公共参数

statYear = YstatDate§1

边界 表达式
年区间起 Y-01-01 00:00:00
年区间止(已完成订单) statDate 23:59:59(见 §1
行情 7 日起 statDate − 6 日(含)
行情 7 日止 statDate(含)
按月分组键 MONTH(finish_time),应用层补全 1~12

默认行情市场(配置)

说明
优先 application.ymlbigscreen.trade-sales.default-market-name(默认 巴青牦牛交易市场
解析 biz_trade_marketdel_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=0ratio=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_minMIN
当日最高价 该日各条 price_maxMAX
当日均价 该日各条 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. 接口设计

统一响应AjaxResultcode / 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 默认 falsetrue 时跳过 §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
mallStatsAvailable boolean 是否成功接入商城统计
categorySales object 农资品类销售 §3.1.6
hotCategoryRank object 热销农资 Top5 §3.1.7
mallOrderTrend object 商城订单趋势 §3.1.8
shopEntry object 店铺入驻 §3.1.9
regionRank object 消费区域 Top5 §3.1.10
reviewWordCloud object 消费者评价词云 §3.1.11

3.1.1 tradeOverview

字段 类型 说明
orderCount long 牦牛交易订单数(笔)
tradeHeads long 牦牛交易量(头)
tradeAmount decimal 牦牛交易总额(元)
supplierCount long 入驻商铺数(家),实时(牦牛供应商快照)
agriOrderCount long 农资订单量(单);滚动 12 月 mallOrderTrend 各月 orderCount 求和;未接入时为 null
agriSalesAmount decimal 农资销售额(元);商城 Open API v1 未提供年度总额,未接入或未返回时为 null,前端显示

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 条;滚动最近 12 自然月buildRolling12MonthTradeTrend)。

字段 说明
statYear 自然年
month 1~12
tradeHeads 该月已完成订单总头数
tradeAmount 该月已完成订单总金额(元)

查询窗口:finish_time[anchor−11 月 1 日, rollingWindowEnd];按月 YEAR+MONTH 聚合。

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 位小数

3.1.6 categorySales(商城 Open API 透传)

字段 说明
statYear 统计年(当年已完成单按 finish_time 归属 Y
totalQty 当年总销量(件)
items[] categoryIdcategoryNameqtyratio

3.1.7 hotCategoryRank

字段 说明
statYear §3.1.6
items[] rankcategoryIdcategoryNameqty(Top5)

3.1.8 mallOrderTrend

字段 说明
statYear 锚点年(回显)
items[] 固定 12 条:滚动最近 12 月;每项含 statYearmonthorderCount

跨年时分别拉取商城 Open API 两年 orderTrend 并合并,再 buildRolling12MonthMallOrderTrend

3.1.9 shopEntry

字段 说明
statYear 统计年
yearTotal 当年入驻店铺总数
items[] monthshopCountratio(各月占 yearTotal 百分比)

3.1.10 regionRank

字段 说明
statYear 统计年
items[] rankcityamountWan(万元,2 位小数)

3.1.11 reviewWordCloud

字段 说明
items[] wordcount(Top50)

响应示例(节选)

{
  "statYear": 2026,
  "statDate": "2026-05-20",
  "availableYears": [2026, 2025],
  "tradeOverview": {
    "orderCount": 12,
    "tradeHeads": 48,
    "tradeAmount": 960000.00,
    "supplierCount": 5,
    "agriOrderCount": 320,
    "agriSalesAmount": null
  },
  "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 }
    ]
  },
  "mallStatsAvailable": true,
  "categorySales": { "statYear": 2026, "totalQty": 1200, "items": [] },
  "hotCategoryRank": { "statYear": 2026, "items": [] },
  "mallOrderTrend": { "statYear": 2026, "items": [{ "month": 1, "orderCount": 320 }] },
  "shopEntry": { "statYear": 2026, "yearTotal": 48, "items": [] },
  "regionRank": { "statYear": 2026, "items": [] },
  "reviewWordCloud": { "items": [{ "word": "质量好", "count": 86 }] }
}

服务端逻辑(摘要)

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. mall = MallStatsOpenApiClient.fetchOverview(year)
   → 请求商城 `GET /overview?statYear={year}`(**不传** statDate)
   → 成功:Support.applyMallStats(vo, mall) + enrichOverviewWithMall(tradeOverview, mall)
   → 失败/未启用:Support.applyEmptyMallStats(vo)
10. return AjaxResult.success(vo)

4. 农资商城对接配置

4.1 application.yml

bigscreen:
  trade-sales:
    default-market-name: 巴青牦牛交易市场
  mall-stats:
    enabled: false          # 生产改为 true
    base-url: ""            # 商城服务根地址,如 http://shop-host:8080
    aes-key: mdYJB5ENzTwEbql2
    aes-iv: FU2GR30Iw76PjXbO
    connect-timeout-ms: 5000
    read-timeout-ms: 15000
    cache-ttl-minutes: 5    # 本地 Redis:bigscreen:mallstats:overview:{statYear}
说明
Token 每次请求生成 UUID → AES-128-CBC 加密 → Base64 → 头 X-Open-Token
降级 enabled=falsebase-url 空或 HTTP/401/500 → mallStatsAvailable=false,不抛错
缓存 Cache-Aside;与商城侧 Redis 叠加,减少跨服务调用

5. 菜单与权限(示例)

类型 名称 权限标识
菜单 大屏交易销售 bigScreen:tradeSales:query

SQL 示例:sql/big_screen_trade_sales_perm.sql(挂载「大屏」父菜单下)。


6. 交付清单

  • TradeSalesScreenController + ITradeSalesScreenService + VO + TradeSalesScreenSupport
  • TradeSalesScreenMapper + XML(§2.6
  • application.yml 默认行情市场名 + §4.1 商城对接项
  • MallStatsOpenApiClient + Properties + Token + mallStatsRestTemplate
  • 前端 ruoyi-screen/src/views/tradeSales 右栏六区块图表
  • 单元测试:牦牛口径 + 商城合并(TradeSalesScreenServiceImplTestMallStatsOpenTokenSupportTest 等)
  • 集成测试:联调真实商城 Open API(可选)
  • 菜单权限 SQL;依赖 biz_trade_order_line.grade_codebiz_grade_weight_config 已执行

7. 修订记录

版本 日期 说明
1.0 2026-05-20 初稿:无新表;单接口五区块;农资商城不纳入
1.1 2026-05-20 增刊农资商城:Open API overview 对接 + 本地 Redis;dashboard 扩展六区块;agriOrderCountagriSalesAmount 待商城 API 补年度总额
1.2 2026-05-20 对齐商城 Open API v1.2:overview 仅传 statYearcategorySales / hotCategoryRank 响应字段改为 statYear;品类/热销/区域口径为统计年YEAR(finish_time)