Refactor dashboard API endpoints and update data sources. Changed data retrieval from `yak_sn_order` to `gov_count_order_total` for various statistics, including trading data, sales types, and market monitoring. Updated SQL queries and descriptions to reflect new data structure and metrics. Enhanced response formats and added new fields for better data representation.

main
Swanky 1 week ago
parent b503d19ef6
commit 4202adf80c
  1. 91
      data/magic-api/api/大屏数据/交易中心实时服务信息.ms
  2. 48
      data/magic-api/api/大屏数据/交易所牦牛成交数据.ms
  3. 72
      data/magic-api/api/大屏数据/地图迁徙数据.ms
  4. 125
      data/magic-api/api/大屏数据/实时交易统计.ms
  5. 82
      data/magic-api/api/大屏数据/市场实时监控.ms
  6. 62
      data/magic-api/api/大屏数据/市场实时监控播放配置.ms
  7. 114
      data/magic-api/api/大屏数据/市场环境监控.ms
  8. 57
      data/magic-api/api/大屏数据/活牛鲜肉价格趋势.ms
  9. 43
      data/magic-api/api/大屏数据/牦牛销售类型统计.ms
  10. 147
      data/magic-api/api/大屏数据/综合销售统计.ms
  11. 30
      data/magic-api/api/大屏数据/采购商户来源分析.ms
  12. 130
      magic-api-dashboard-interfaces.txt

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "交易中心实时服务信息", "name" : "交易中心实时服务信息",
"createTime" : 1780877300000, "createTime" : 1780877300000,
"updateTime" : null, "updateTime" : 1781373000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,62 +17,63 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "交易中心实时服务信息:牦牛供应/待售/已售、剩余车位、进场车辆、供应商数量(只读)。牦牛=yak_trade_ear_tag_inventory;车位=yak_car_parking_zone;车辆=yak_car_record+yak_trade_entry_record;供应商=yak_sn_customer(SELLER)。", "description" : "交易中心实时服务信息:gov_count_real_info,group_item=jygy,source_system=yzt。key=mngyzl/dsmn/ysmn/sycw/jccl/gyssl。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读查询,不修改任何数据 // gov_count_real_info(group_item=jygy, source_system=yzt)
// mngyzl 牦牛供应总量
// dsmn 待售牦牛
// ysmn 已售牦牛
// sycw 剩余车位
// jccl 进场车辆
// gyssl 供应商数量
var sql = """ var sql = """
SELECT SELECT key, name, value, tag, unit
(SELECT COUNT(*) FROM gov_count_real_info
FROM yak_trade_ear_tag_inventory) AS total_supply, WHERE group_item = 'jygy'
(SELECT COUNT(*) AND source_system = 'yzt'
FROM yak_trade_ear_tag_inventory
WHERE status IN ('AVAILABLE', 'LOCKED')) AS for_sale_yaks,
(SELECT COUNT(*)
FROM yak_trade_ear_tag_inventory
WHERE status = 'SOLD') AS sold_yaks,
(SELECT COALESCE(SUM(total_capacity - current_count), 0)
FROM yak_car_parking_zone
WHERE status = 'NORMAL') AS remaining_parking,
(SELECT COUNT(*)
FROM (
SELECT plate_no AS vehicle_no
FROM yak_car_record
WHERE del_flag = '0'
AND entry_time >= CURRENT_DATE
UNION
SELECT vehicle_no
FROM yak_trade_entry_record
WHERE entered_at >= CURRENT_DATE
) v) AS entering_vehicles,
(SELECT COUNT(*)
FROM yak_sn_customer
WHERE del_flag = '0'
AND customer_type = 'SELLER') AS supplier_count
""" """
var rows = db.select(sql) var rows = db.select(sql)
var row = rows && rows.length > 0 ? rows[0] : null
if (!row) { var valueMap = {}
return { var tagMap = {}
totalSupply: 0, var unitMap = {}
forSaleYaks: 0,
soldYaks: 0, for (row in rows) {
remainingParking: 0, valueMap[row.key] = row.value != null ? row.value : 0
enteringVehicles: 0, if (row.tag) {
supplierCount: 0 tagMap[row.key] = row.tag
} }
if (row.unit) {
unitMap[row.key] = row.unit
} }
}
var pick = (key) => valueMap[key] != null ? valueMap[key] : 0
var pickTag = (key) => tagMap[key]
var pickUnit = (key) => unitMap[key]
return { return {
totalSupply: row.totalSupply ? row.totalSupply : 0, totalSupply: pick('mngyzl'),
forSaleYaks: row.forSaleYaks ? row.forSaleYaks : 0, forSaleYaks: pick('dsmn'),
soldYaks: row.soldYaks ? row.soldYaks : 0, soldYaks: pick('ysmn'),
remainingParking: row.remainingParking ? row.remainingParking : 0, remainingParking: pick('sycw'),
enteringVehicles: row.enteringVehicles ? row.enteringVehicles : 0, enteringVehicles: pick('jccl'),
supplierCount: row.supplierCount ? row.supplierCount : 0 supplierCount: pick('gyssl'),
totalSupplyTag: pickTag('mngyzl'),
forSaleYaksTag: pickTag('dsmn'),
soldYaksTag: pickTag('ysmn'),
remainingParkingTag: pickTag('sycw'),
enteringVehiclesTag: pickTag('jccl'),
supplierCountTag: pickTag('gyssl'),
totalSupplyUnit: pickUnit('mngyzl'),
forSaleYaksUnit: pickUnit('dsmn'),
soldYaksUnit: pickUnit('ysmn'),
remainingParkingUnit: pickUnit('sycw'),
enteringVehiclesUnit: pickUnit('jccl'),
supplierCountUnit: pickUnit('gyssl')
} }

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "交易所牦牛成交数据", "name" : "交易所牦牛成交数据",
"createTime" : 1780877000000, "createTime" : 1780877000000,
"updateTime" : 1780817847278, "updateTime" : 1781369000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -28,27 +28,31 @@
"requestBody" : "", "requestBody" : "",
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : "{\n \"code\": -1,\n \"message\": \"系统内部出现错误\",\n \"data\": null,\n \"timestamp\": 1780817836665,\n \"executeTime\": 4\n}", "responseBody" : null,
"description" : "交易所牦牛成交数据:近6个时间段的牦牛交易数量(头)与成交订单数量(单)趋势,数据源 yak_sn_order 已完成订单(只读查询)", "description" : "交易所牦牛成交数据:gov_count_order_total,一条记录一单,SUM(yak_number) 为成交头数,COUNT(*) 为订单数。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// gov_count_order_total:一条记录 = 一笔订单
// 牦牛成交头数:SUM(yak_number)
// 成交订单数:COUNT(*)
// 时间维度按 update_time 分桶(count_date 多为空)
var buildSeries = (rows) => { var buildSeries = (rows) => {
var labels = [] var labels = []
var yakTradingVolume = [] var yakNumber = []
var orderCount = [] var orderCount = []
for (row in rows) { for (row in rows) {
labels.push(row.label) labels.push(row.label)
yakTradingVolume.push(row.yakVolume ? row.yakVolume : 0) yakNumber.push(row.yakNumber != null ? row.yakNumber : 0)
orderCount.push(row.orderCount ? row.orderCount : 0) orderCount.push(row.orderCount != null ? row.orderCount : 0)
} }
return { return {
labels: labels, labels: labels,
yakTradingVolume: yakTradingVolume, yakNumber: yakNumber,
orderCount: orderCount orderCount: orderCount
} }
} }
@ -56,17 +60,15 @@ var buildSeries = (rows) => {
var daySql = """ var daySql = """
SELECT SELECT
TO_CHAR(d.bucket_date, 'FMMM/FMDD') AS label, TO_CHAR(d.bucket_date, 'FMMM/FMDD') AS label,
COALESCE(SUM(o.quantity), 0) AS yak_volume, COALESCE(SUM(o.yak_number), 0) AS yak_number,
COUNT(o.id) AS order_count COUNT(o.id) AS order_count
FROM ( FROM (
SELECT (CURRENT_DATE - offs)::date AS bucket_date, offs SELECT (CURRENT_DATE - offs)::date AS bucket_date, offs
FROM generate_series(5, 0, -1) AS offs FROM generate_series(5, 0, -1) AS offs
) d ) d
LEFT JOIN yak_sn_order o LEFT JOIN gov_count_order_total o
ON o.del_flag = '0' ON o.update_time >= d.bucket_date
AND o.status = 'COMPLETED' AND o.update_time < d.bucket_date + INTERVAL '1 day'
AND o.transaction_time >= d.bucket_date
AND o.transaction_time < d.bucket_date + INTERVAL '1 day'
GROUP BY d.bucket_date, d.offs GROUP BY d.bucket_date, d.offs
ORDER BY d.offs DESC ORDER BY d.offs DESC
""" """
@ -74,17 +76,15 @@ ORDER BY d.offs DESC
var weekSql = """ var weekSql = """
SELECT SELECT
TO_CHAR(d.anchor_date, 'FMMM/FMDD') || '-' || TO_CHAR(d.anchor_date + 6, 'FMMM/FMDD') AS label, TO_CHAR(d.anchor_date, 'FMMM/FMDD') || '-' || TO_CHAR(d.anchor_date + 6, 'FMMM/FMDD') AS label,
COALESCE(SUM(o.quantity), 0) AS yak_volume, COALESCE(SUM(o.yak_number), 0) AS yak_number,
COUNT(o.id) AS order_count COUNT(o.id) AS order_count
FROM ( FROM (
SELECT (CURRENT_DATE - offs * 7)::date AS anchor_date, offs SELECT (CURRENT_DATE - offs * 7)::date AS anchor_date, offs
FROM generate_series(5, 0, -1) AS offs FROM generate_series(5, 0, -1) AS offs
) d ) d
LEFT JOIN yak_sn_order o LEFT JOIN gov_count_order_total o
ON o.del_flag = '0' ON o.update_time >= d.anchor_date
AND o.status = 'COMPLETED' AND o.update_time < d.anchor_date + INTERVAL '7 days'
AND o.transaction_time >= d.anchor_date
AND o.transaction_time < d.anchor_date + INTERVAL '7 days'
GROUP BY d.anchor_date, d.offs GROUP BY d.anchor_date, d.offs
ORDER BY d.offs DESC ORDER BY d.offs DESC
""" """
@ -92,17 +92,15 @@ ORDER BY d.offs DESC
var monthSql = """ var monthSql = """
SELECT SELECT
TO_CHAR(d.month_start, 'YYYY/FMMM') AS label, TO_CHAR(d.month_start, 'YYYY/FMMM') AS label,
COALESCE(SUM(o.quantity), 0) AS yak_volume, COALESCE(SUM(o.yak_number), 0) AS yak_number,
COUNT(o.id) AS order_count COUNT(o.id) AS order_count
FROM ( FROM (
SELECT (date_trunc('month', CURRENT_DATE) - (offs || ' months')::interval)::date AS month_start, offs SELECT (date_trunc('month', CURRENT_DATE) - (offs || ' months')::interval)::date AS month_start, offs
FROM generate_series(5, 0, -1) AS offs FROM generate_series(5, 0, -1) AS offs
) d ) d
LEFT JOIN yak_sn_order o LEFT JOIN gov_count_order_total o
ON o.del_flag = '0' ON o.update_time >= d.month_start
AND o.status = 'COMPLETED' AND o.update_time < d.month_start + INTERVAL '1 month'
AND o.transaction_time >= d.month_start
AND o.transaction_time < d.month_start + INTERVAL '1 month'
GROUP BY d.month_start, d.offs GROUP BY d.month_start, d.offs
ORDER BY d.offs DESC ORDER BY d.offs DESC
""" """

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "地图迁徙数据", "name" : "地图迁徙数据",
"createTime" : 1780882000000, "createTime" : 1780882000000,
"updateTime" : 1781360000000, "updateTime" : 1781377000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,12 +17,16 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "中央地图迁徙数据:销售网络/源地供应/红原本地出栏,数据源 yak_sn_order + yak_sn_customer(只读)。枢纽名称需与系统配置 mapHub.name 一致。", "description" : "中央地图迁徙数据:gov_count_order_total。销售网络=origin_place含红原;源地供应=destination_place含红原;红原出栏=起终点均含红原。头数SUM(yak_number)。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读统计已完成订单;hubName 需与系统配置 mapHub.name 保持一致 // gov_count_order_total:一条记录 = 一笔订单,头数 SUM(yak_number)
// outflow 销售网络:origin_place 含「红原」,按 destination_place 聚合
// inflow 源地供应:destination_place 含「红原」,按 origin_place 聚合
// local 红原出栏:destination_place 含「红原」且 origin_place 含「红原」,按 origin_place 乡镇聚合
// hubName 需与系统配置 mapHub.name 保持一致
var hubName = '红原县' var hubName = '红原县'
@ -140,51 +144,55 @@ var buildDirectionalFlows = (rows, direction) => {
var outflowSql = """ var outflowSql = """
SELECT SELECT
COALESCE(NULLIF(TRIM(c.region_name), ''), NULLIF(TRIM(o.destination_place), ''), '未知') AS place_name, COALESCE(NULLIF(TRIM(destination_place), ''), '未知') AS place_name,
COALESCE(SUM(o.quantity), 0) AS value, COALESCE(SUM(yak_number), 0) AS value,
MAX(COALESCE(NULLIF(TRIM(c.region_name), ''), NULLIF(TRIM(o.destination_place), ''), '未知')) AS sample_place MAX(destination_place) AS sample_place
FROM yak_sn_order o FROM gov_count_order_total
LEFT JOIN yak_sn_customer c ON c.id = o.buyer_id AND c.del_flag = '0' WHERE origin_place IS NOT NULL
WHERE o.del_flag = '0' AND position('红原' in origin_place) > 0
AND o.status = 'COMPLETED' AND destination_place IS NOT NULL
GROUP BY COALESCE(NULLIF(TRIM(c.region_name), ''), NULLIF(TRIM(o.destination_place), ''), '未知') AND TRIM(destination_place) <> ''
HAVING COALESCE(SUM(o.quantity), 0) > 0 GROUP BY COALESCE(NULLIF(TRIM(destination_place), ''), '未知')
HAVING COALESCE(SUM(yak_number), 0) > 0
ORDER BY value DESC, place_name ORDER BY value DESC, place_name
""" """
var inflowSql = """ var inflowSql = """
SELECT SELECT
COALESCE(NULLIF(TRIM(c.region_name), ''), NULLIF(TRIM(o.origin_place), ''), '未知') AS place_name, COALESCE(NULLIF(TRIM(origin_place), ''), '未知') AS place_name,
COALESCE(SUM(o.quantity), 0) AS value, COALESCE(SUM(yak_number), 0) AS value,
MAX(COALESCE(NULLIF(TRIM(c.region_name), ''), NULLIF(TRIM(o.origin_place), ''), '未知')) AS sample_place MAX(origin_place) AS sample_place
FROM yak_sn_order o FROM gov_count_order_total
LEFT JOIN yak_sn_customer c ON c.id = o.seller_id AND c.del_flag = '0' WHERE destination_place IS NOT NULL
WHERE o.del_flag = '0' AND position('红原' in destination_place) > 0
AND o.status = 'COMPLETED' AND origin_place IS NOT NULL
GROUP BY COALESCE(NULLIF(TRIM(c.region_name), ''), NULLIF(TRIM(o.origin_place), ''), '未知') AND TRIM(origin_place) <> ''
HAVING COALESCE(SUM(o.quantity), 0) > 0 GROUP BY COALESCE(NULLIF(TRIM(origin_place), ''), '未知')
HAVING COALESCE(SUM(yak_number), 0) > 0
ORDER BY value DESC, place_name ORDER BY value DESC, place_name
""" """
var localSql = """ var localSql = """
SELECT SELECT
COALESCE( COALESCE(
NULLIF((regexp_match(o.origin_place, '([^省市区县]+(?:镇|乡|街道))'))[1], ''), NULLIF((regexp_match(origin_place, '([^省市区县]+(?:镇|乡|街道))'))[1], ''),
NULLIF(TRIM(o.origin_place), ''), NULLIF(TRIM(origin_place), ''),
'未知' '未知'
) AS place_name, ) AS place_name,
COALESCE(SUM(o.quantity), 0) AS value, COALESCE(SUM(yak_number), 0) AS value,
MAX(o.origin_place) AS sample_place MAX(origin_place) AS sample_place
FROM yak_sn_order o FROM gov_count_order_total
WHERE o.del_flag = '0' WHERE destination_place IS NOT NULL
AND o.status = 'COMPLETED' AND position('红原' in destination_place) > 0
AND position('红原' in o.origin_place) > 0 AND origin_place IS NOT NULL
AND position('红原' in origin_place) > 0
AND TRIM(origin_place) <> ''
GROUP BY COALESCE( GROUP BY COALESCE(
NULLIF((regexp_match(o.origin_place, '([^省市区县]+(?:镇|乡|街道))'))[1], ''), NULLIF((regexp_match(origin_place, '([^省市区县]+(?:镇|乡|街道))'))[1], ''),
NULLIF(TRIM(o.origin_place), ''), NULLIF(TRIM(origin_place), ''),
'未知' '未知'
) )
HAVING COALESCE(SUM(o.quantity), 0) > 0 HAVING COALESCE(SUM(yak_number), 0) > 0
ORDER BY value DESC, place_name ORDER BY value DESC, place_name
""" """

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "实时交易统计", "name" : "实时交易统计",
"createTime" : 1780876800000, "createTime" : 1780876800000,
"updateTime" : 1780803706100, "updateTime" : 1781365000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -14,7 +14,7 @@
"parameters" : [ { "parameters" : [ {
"name" : "dimension", "name" : "dimension",
"value" : null, "value" : null,
"description" : "时间维度:day(当天)/week(近一周)/month(近一月)/year(当年),不传则返回全部", "description" : "时间维度:day(最新统计日)/week(近7天)/month(近30天),不传则返回全部",
"required" : false, "required" : false,
"dataType" : "String", "dataType" : "String",
"type" : null, "type" : null,
@ -29,82 +29,97 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "实时交易统计:牦牛交易总量、订单交易总量、销售商户数量、采购商户数量,按当天/近一周/近一月/当年统计(只读查询)", "description" : "实时交易统计:数据源 gov_count_item(只读)。key=mnjyzl/jdjyzl/xsshsl/cgshsl,按 count_date 汇总。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读统计已完成订单,不修改任何数据 // gov_count_item 指标 key:
// mnjyzl 牦牛交易总量
// jdjyzl 订单交易总量
// xsshsl 销售商户数量
// cgshsl 采购商户数量
// 同一 key + count_date 可能有多条,取 MAX(value);
// 交易量类(mnjyzl/jdjyzl)在 week/month 内按日求和,商户数类取区间内最大值。
var sql = """ var sql = """
WITH daily AS (
SELECT SELECT
dim, key,
COALESCE(SUM(quantity), 0) AS yak_total_volume, count_date,
COUNT(*) AS order_total_volume, MAX(value) AS value
COUNT(DISTINCT seller_id) AS seller_count, FROM gov_count_item
COUNT(DISTINCT buyer_id) AS buyer_count WHERE TRIM(name) IN (
FROM ( '牦牛交易总量',
SELECT 'day' AS dim, quantity, seller_id, buyer_id '订单交易总量',
FROM yak_sn_order '销售商户数量',
WHERE del_flag = '0' '采购商户数量'
AND status = 'COMPLETED' )
AND transaction_time >= CURRENT_DATE GROUP BY key, count_date
AND transaction_time < CURRENT_DATE + INTERVAL '1 day' ),
anchor AS (
UNION ALL SELECT MAX(count_date) AS latest_date FROM daily
),
SELECT 'week' AS dim, quantity, seller_id, buyer_id day_stats AS (
FROM yak_sn_order SELECT d.key, d.value
WHERE del_flag = '0' FROM daily d
AND status = 'COMPLETED' CROSS JOIN anchor a
AND transaction_time >= CURRENT_DATE - INTERVAL '6 days' WHERE d.count_date = a.latest_date
AND transaction_time < CURRENT_DATE + INTERVAL '1 day' ),
week_stats AS (
SELECT
d.key,
CASE
WHEN d.key IN ('mnjyzl', 'jdjyzl') THEN SUM(d.value)
ELSE MAX(d.value)
END AS value
FROM daily d
CROSS JOIN anchor a
WHERE d.count_date >= a.latest_date - INTERVAL '6 days'
GROUP BY d.key
),
month_stats AS (
SELECT
d.key,
CASE
WHEN d.key IN ('mnjyzl', 'jdjyzl') THEN SUM(d.value)
ELSE MAX(d.value)
END AS value
FROM daily d
CROSS JOIN anchor a
WHERE d.count_date >= a.latest_date - INTERVAL '29 days'
GROUP BY d.key
)
SELECT 'day' AS dim, key, value FROM day_stats
UNION ALL UNION ALL
SELECT 'week' AS dim, key, value FROM week_stats
SELECT 'month' AS dim, quantity, seller_id, buyer_id
FROM yak_sn_order
WHERE del_flag = '0'
AND status = 'COMPLETED'
AND transaction_time >= CURRENT_DATE - INTERVAL '29 days'
AND transaction_time < CURRENT_DATE + INTERVAL '1 day'
UNION ALL UNION ALL
SELECT 'month' AS dim, key, value FROM month_stats
SELECT 'year' AS dim, quantity, seller_id, buyer_id ORDER BY dim, key
FROM yak_sn_order
WHERE del_flag = '0'
AND status = 'COMPLETED'
AND transaction_time >= DATE_TRUNC('year', CURRENT_DATE)
AND transaction_time < CURRENT_DATE + INTERVAL '1 day'
) t
GROUP BY dim
ORDER BY dim
""" """
var rows = db.select(sql) var rows = db.select(sql)
var createEmptyStats = () => { var createEmptyStats = () => {
return { return {
yakTotalVolume: 0, mnjyzl: 0,
orderTotalVolume: 0, jdjyzl: 0,
sellerCount: 0, xsshsl: 0,
buyerCount: 0 cgshsl: 0
} }
} }
var result = { var result = {
day: createEmptyStats(), day: createEmptyStats(),
week: createEmptyStats(), week: createEmptyStats(),
month: createEmptyStats(), month: createEmptyStats()
year: createEmptyStats()
} }
for (row in rows) { for (row in rows) {
result[row.dim] = { var dim = row.dim
yakTotalVolume: row.yakTotalVolume, var metricKey = row.key
orderTotalVolume: row.orderTotalVolume, if (result[dim]) {
sellerCount: row.sellerCount, result[dim][metricKey] = row.value != null ? row.value : 0
buyerCount: row.buyerCount
} }
} }

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "市场实时监控", "name" : "市场实时监控",
"createTime" : 1781199600000, "createTime" : 1781199600000,
"updateTime" : null, "updateTime" : 1781378000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,12 +17,14 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "市场实时监控:视频监控设备列表,数据源 iot_device_video + iot_device_video_data 最新一条(只读)。", "description" : "市场实时监控:iot_device_video + iot_video_account,萤石 ezopen 播放地址与 accessToken。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读查询,不修改任何数据 // iot_device_video + iot_video_account(萤石云)
// play_url / hd_play_url:ezopen://open.ys7.com/{serial}/{channel}.live
// accessToken:iot_video_account.token
var sql = """ var sql = """
SELECT SELECT
@ -32,31 +34,21 @@ SELECT
v.location, v.location,
v.address, v.address,
v.status, v.status,
v.serial_number,
v.channel_number,
v.play_url, v.play_url,
v.hd_play_url, v.hd_play_url,
v.preview_img_url, v.preview_img_url,
v.img, v.img,
v.channel_number, v.video_account_id,
v."index" AS sort_index, v."index" AS sort_index,
v.last_capture_time, v.last_capture_time,
d.stream_url AS latest_stream_url, a.app_key,
d.snapshot_url AS latest_snapshot_url, a.token AS access_token
d.online_status AS latest_online_status,
d.stream_status AS latest_stream_status,
d.fault_code AS latest_fault_code
FROM iot_device_video v FROM iot_device_video v
LEFT JOIN LATERAL ( LEFT JOIN iot_video_account a
SELECT ON a.id::text = v.video_account_id
stream_url, AND COALESCE(a.del_flag, '0') = '0'
snapshot_url,
online_status,
stream_status,
fault_code
FROM iot_device_video_data
WHERE device_id = v.id
ORDER BY collect_time DESC NULLS LAST
LIMIT 1
) d ON true
WHERE COALESCE(v.del_flag, '0') = '0' WHERE COALESCE(v.del_flag, '0') = '0'
AND COALESCE(v.is_show, true) = true AND COALESCE(v.is_show, true) = true
ORDER BY v."index" NULLS LAST, v.name ORDER BY v."index" NULLS LAST, v.name
@ -87,8 +79,6 @@ var toUpperText = (text) => {
var mapStatus = (row) => { var mapStatus = (row) => {
var deviceStatus = pickText(row, 'status', 'status') var deviceStatus = pickText(row, 'status', 'status')
var onlineStatus = pickText(row, 'latestOnlineStatus', 'latest_online_status')
var faultCode = pickText(row, 'latestFaultCode', 'latest_fault_code')
var deviceUpper = toUpperText(deviceStatus) var deviceUpper = toUpperText(deviceStatus)
if (deviceUpper === 'OFFLINE') { if (deviceUpper === 'OFFLINE') {
@ -97,22 +87,10 @@ var mapStatus = (row) => {
if (deviceUpper === 'ERROR' || deviceUpper === 'FAULT') { if (deviceUpper === 'ERROR' || deviceUpper === 'FAULT') {
return 'error' return 'error'
} }
if (faultCode) {
return 'error'
}
if (onlineStatus) {
var onlineUpper = toUpperText(onlineStatus)
if (onlineUpper === 'OFFLINE') {
return 'offline'
}
if (onlineUpper === 'ERROR' || onlineUpper === 'FAULT') {
return 'error'
}
}
return 'online' return 'online'
} }
var pickStreamUrl = (row) => { var pickEzopenUrl = (row) => {
var hd = pickText(row, 'hdPlayUrl', 'hd_play_url') var hd = pickText(row, 'hdPlayUrl', 'hd_play_url')
if (hd) { if (hd) {
return hd return hd
@ -121,15 +99,16 @@ var pickStreamUrl = (row) => {
if (play) { if (play) {
return play return play
} }
return pickText(row, 'latestStreamUrl', 'latest_stream_url')
}
var pickHdStreamUrl = (row) => { var serial = pickText(row, 'serialNumber', 'serial_number')
return pickText(row, 'hdPlayUrl', 'hd_play_url') var channel = row.channelNumber != null ? row.channelNumber : row.channel_number
if (channel == null) {
channel = 1
} }
if (serial) {
var pickPlayUrl = (row) => { return 'ezopen://open.ys7.com/' + serial + '/' + channel + '.hd.live'
return pickText(row, 'playUrl', 'play_url') }
return ''
} }
var pickPreviewUrl = (row) => { var pickPreviewUrl = (row) => {
@ -137,11 +116,7 @@ var pickPreviewUrl = (row) => {
if (preview) { if (preview) {
return preview return preview
} }
var img = pickText(row, 'img', 'img') return pickText(row, 'img', 'img')
if (img) {
return img
}
return pickText(row, 'latestSnapshotUrl', 'latest_snapshot_url')
} }
if (rows && rows.length > 0) { if (rows && rows.length > 0) {
@ -152,14 +127,19 @@ if (rows && rows.length > 0) {
number: pickText(row, 'number', 'number'), number: pickText(row, 'number', 'number'),
location: pickText(row, 'location', 'location'), location: pickText(row, 'location', 'location'),
address: pickText(row, 'address', 'address'), address: pickText(row, 'address', 'address'),
serialNumber: pickText(row, 'serialNumber', 'serial_number'),
channelNumber: row.channelNumber != null ? row.channelNumber : row.channel_number, channelNumber: row.channelNumber != null ? row.channelNumber : row.channel_number,
videoAccountId: pickText(row, 'videoAccountId', 'video_account_id'),
sortIndex: row.sortIndex != null ? row.sortIndex : row.sort_index, sortIndex: row.sortIndex != null ? row.sortIndex : row.sort_index,
status: mapStatus(row), status: mapStatus(row),
resolution: '1920x1080', resolution: '1920x1080',
url: pickPreviewUrl(row),
preview: pickPreviewUrl(row), preview: pickPreviewUrl(row),
hdStreamUrl: pickHdStreamUrl(row), playUrl: pickText(row, 'playUrl', 'play_url'),
playUrl: pickPlayUrl(row), hdPlayUrl: pickText(row, 'hdPlayUrl', 'hd_play_url'),
streamUrl: pickStreamUrl(row) ezopenUrl: pickEzopenUrl(row),
accessToken: pickText(row, 'accessToken', 'access_token'),
appKey: pickText(row, 'appKey', 'app_key')
}) })
} }
} }

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "市场实时监控播放配置", "name" : "市场实时监控播放配置",
"createTime" : 1781203200000, "createTime" : 1781203200000,
"updateTime" : null, "updateTime" : 1781378000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,61 +17,21 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "市场实时监控播放配置:直播流格式识别规则与播放器参数(HLS/FLV/MP4 等)。", "description" : "市场实时监控播放配置:萤石云 EZUIKit 播放器参数。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 直播流地址格式配置,供前端 hls.js / flv.js 自动选型 // 萤石云 EZUIKit 播放器配置
return { return {
defaultProtocol: 'hls', playerType: 'ezviz',
urlPriority: ['hdStreamUrl', 'streamUrl', 'playUrl'], staticPath: '/ezuikit_static',
formatRules: [ urlPriority: ['hdPlayUrl', 'playUrl', 'ezopenUrl'],
{ ezviz: {
type: 'hls', template: 'simple',
match: '.m3u8', scaleMode: 1,
library: 'hls.js', audio: false,
description: 'HLS 直播(萤石 open.ys7.com 等)' defaultQuality: 'hd.live'
},
{
type: 'flv',
match: '.flv',
library: 'flv.js',
description: 'HTTP-FLV 直播(萤石 rtmp*open.ys7.com 等)'
},
{
type: 'mp4',
match: '.mp4',
library: 'native',
description: 'MP4 点播/回放'
},
{
type: 'webm',
match: '.webm',
library: 'native',
description: 'WebM 点播'
}
],
playerOptions: {
hls: {
enableWorker: true,
lowLatencyMode: true
},
flv: {
isLive: true,
hasAudio: true,
hasVideo: true
},
flvMedia: {
enableWorker: true,
enableStashBuffer: false
}
},
ys7Templates: {
hdHls: 'https://open.ys7.com/v3/openlive/{deviceSerial}_{channelNo}_1.m3u8',
hls: 'https://open.ys7.com/v3/openlive/{deviceSerial}_{channelNo}_2.m3u8',
hdFlv: 'https://rtmp12open.ys7.com:9188/v3/openlive/{deviceSerial}_{channelNo}_1.flv',
flv: 'https://rtmp12open.ys7.com:9188/v3/openlive/{deviceSerial}_{channelNo}_2.flv'
} }
} }

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "市场环境监控", "name" : "市场环境监控",
"createTime" : 1781196000000, "createTime" : 1781196000000,
"updateTime" : null, "updateTime" : 1781374000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,79 +17,71 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "市场环境监控:温湿度(iot_device_env_data)+ 气象(iot_device_weather_data)最新一条,只读。", "description" : "市场环境监控:gov_count_real_info,group_item=env-monitor,source_system=yzt。温湿度/气象八项指标及 tag、unit。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读查询,不修改任何数据 // gov_count_real_info(group_item=env-monitor, source_system=yzt)
// temperature 温度
// humidity 湿度
// aqi 空气质量
// pressure 大气压强
// uv 紫外线
// rainfall 降雨量
// wind_direction 风向(value=角度,tag=风向名)
// wind_power 风力(value=级数)
var envSql = """ var sql = """
SELECT SELECT key, name, value, tag, unit
temperature, FROM gov_count_real_info
humidity, WHERE group_item = 'env-monitor'
to_char(collect_time, 'YYYY-MM-DD HH24:MI:SS') AS collect_time AND source_system = 'yzt'
FROM iot_device_env_data
ORDER BY collect_time DESC NULLS LAST
LIMIT 1
""" """
var weatherSql = """ var rows = db.select(sql)
SELECT
air_pressure,
pm25,
pm10,
uv_index,
rainfall,
wind_speed,
wind_direction,
to_char(collect_time, 'YYYY-MM-DD HH24:MI:SS') AS collect_time
FROM iot_device_weather_data
ORDER BY collect_time DESC NULLS LAST
LIMIT 1
"""
var envRows = db.select(envSql) var valueMap = {}
var weatherRows = db.select(weatherSql) var tagMap = {}
var env = envRows && envRows.length > 0 ? envRows[0] : null var unitMap = {}
var weather = weatherRows && weatherRows.length > 0 ? weatherRows[0] : null
var pickNum = (row, camelKey, snakeKey) => { for (row in rows) {
if (!row) { valueMap[row.key] = row.value != null ? row.value : null
return null if (row.tag) {
} tagMap[row.key] = row.tag
if (row[camelKey] != null) {
return row[camelKey]
} }
if (row[snakeKey] != null) { if (row.unit) {
return row[snakeKey] unitMap[row.key] = row.unit
} }
return null
} }
var pickText = (row, camelKey, snakeKey) => { var pick = (key) => valueMap[key] != null ? valueMap[key] : null
if (!row) { var pickTag = (key) => tagMap[key]
return '' var pickUnit = (key) => unitMap[key]
}
if (row[camelKey]) {
return row[camelKey]
}
if (row[snakeKey]) {
return row[snakeKey]
}
return ''
}
return { return {
temperature: pickNum(env, 'temperature', 'temperature'), temperature: pick('temperature'),
humidity: pickNum(env, 'humidity', 'humidity'), humidity: pick('humidity'),
envCollectTime: pickText(env, 'collectTime', 'collect_time'), aqi: pick('aqi'),
airPressure: pickNum(weather, 'airPressure', 'air_pressure'), airPressure: pick('pressure'),
pm25: pickNum(weather, 'pm25', 'pm25'), uvIndex: pick('uv'),
pm10: pickNum(weather, 'pm10', 'pm10'), rainfall: pick('rainfall'),
uvIndex: pickNum(weather, 'uvIndex', 'uv_index'), windDirection: pick('wind_direction'),
rainfall: pickNum(weather, 'rainfall', 'rainfall'), windPower: pick('wind_power'),
windSpeed: pickNum(weather, 'windSpeed', 'wind_speed'), temperatureTag: pickTag('temperature'),
windDirection: pickNum(weather, 'windDirection', 'wind_direction'), humidityTag: pickTag('humidity'),
weatherCollectTime: pickText(weather, 'collectTime', 'collect_time') aqiTag: pickTag('aqi'),
airPressureTag: pickTag('pressure'),
uvIndexTag: pickTag('uv'),
rainfallTag: pickTag('rainfall'),
windDirectionTag: pickTag('wind_direction'),
windPowerTag: pickTag('wind_power'),
temperatureUnit: pickUnit('temperature'),
humidityUnit: pickUnit('humidity'),
aqiUnit: pickUnit('aqi'),
airPressureUnit: pickUnit('pressure'),
uvIndexUnit: pickUnit('uv'),
rainfallUnit: pickUnit('rainfall'),
windDirectionUnit: pickUnit('wind_direction'),
windPowerUnit: pickUnit('wind_power')
} }

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "活牛鲜肉价格趋势", "name" : "活牛鲜肉价格趋势",
"createTime" : 1780877200000, "createTime" : 1780877200000,
"updateTime" : null, "updateTime" : 1781372000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,17 +17,20 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "活牛/鲜肉价格趋势:近8个月市场采集均价。数据源 yak_trade_market_price,活牛=LIVE_YAK,鲜肉=FRESH_MEAT,直接使用 price 字段及 unit 单位,不做重量折算。", "description" : "活牛/鲜肉价格趋势:近8个月采集均价。数据源 gov_count_price,活牛=price_key hx,鲜肉=mnr/mnt/mnp 月均,直接使用 value 与 unit,不做重量折算。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读查询,不修改任何数据 // gov_count_price 价格指标 price_key:
// 数据源:yak_trade_market_price(市场采集价格,已含 price + unit) // hx 活畜(活牛)
// price_type: LIVE_YAK=活牛, FRESH_MEAT=鲜肉/牛肉 // mnr/mnt/mnp 鲜肉(牦牛肉/腿/排)
// 按 year + month 对各区域采集价取 AVG(value);无数据月份返回 null
var monthBucketSql = """ var monthBucketSql = """
SELECT SELECT
EXTRACT(YEAR FROM d.month_start)::int AS year,
EXTRACT(MONTH FROM d.month_start)::int AS month,
d.month_start, d.month_start,
d.offs, d.offs,
TO_CHAR(d.month_start, 'FMMM') || '月' AS label TO_CHAR(d.month_start, 'FMMM') || '月' AS label
@ -42,30 +45,30 @@ ORDER BY d.offs DESC
var livePriceSql = """ var livePriceSql = """
SELECT SELECT
date_trunc('month', p.price_date)::date AS month_start, year,
ROUND(AVG(p.price)::numeric, 2) AS live_cattle_price month,
FROM yak_trade_market_price p ROUND(AVG(value)::numeric, 2) AS live_cattle_price
WHERE p.price_type = 'LIVE_YAK' FROM gov_count_price
AND p.price_date >= date_trunc('month', CURRENT_DATE) - INTERVAL '7 months' WHERE price_key = 'hx'
GROUP BY date_trunc('month', p.price_date)::date GROUP BY year, month
""" """
var beefPriceSql = """ var beefPriceSql = """
SELECT SELECT
date_trunc('month', p.price_date)::date AS month_start, year,
ROUND(AVG(p.price)::numeric, 2) AS beef_price month,
FROM yak_trade_market_price p ROUND(AVG(value)::numeric, 2) AS beef_price
WHERE p.price_type = 'FRESH_MEAT' FROM gov_count_price
AND p.price_date >= date_trunc('month', CURRENT_DATE) - INTERVAL '7 months' WHERE price_key IN ('mnr', 'mnt', 'mnp')
GROUP BY date_trunc('month', p.price_date)::date GROUP BY year, month
""" """
var unitSql = """ var unitSql = """
SELECT SELECT
MAX(CASE WHEN price_type = 'LIVE_YAK' THEN unit END) AS live_unit, MAX(CASE WHEN price_key = 'hx' THEN unit END) AS live_unit,
MAX(CASE WHEN price_type = 'FRESH_MEAT' THEN unit END) AS beef_unit MAX(CASE WHEN price_key IN ('mnr', 'mnt', 'mnp') THEN unit END) AS beef_unit
FROM yak_trade_market_price FROM gov_count_price
WHERE price_type IN ('LIVE_YAK', 'FRESH_MEAT') WHERE price_key IN ('hx', 'mnr', 'mnt', 'mnp')
""" """
var buckets = db.select(monthBucketSql) var buckets = db.select(monthBucketSql)
@ -77,15 +80,17 @@ var liveMap = {}
var beefMap = {} var beefMap = {}
for (row in liveRows) { for (row in liveRows) {
liveMap[row.monthStart] = row.liveCattlePrice var key = row.year + '-' + row.month
liveMap[key] = row.liveCattlePrice
} }
for (row in beefRows) { for (row in beefRows) {
beefMap[row.monthStart] = row.beefPrice var key = row.year + '-' + row.month
beefMap[key] = row.beefPrice
} }
var liveUnit = '元/公斤' var liveUnit = '元/kg'
var beefUnit = '元/公斤' var beefUnit = '元/kg'
if (unitRows && unitRows.length > 0) { if (unitRows && unitRows.length > 0) {
if (unitRows[0].liveUnit) { if (unitRows[0].liveUnit) {
liveUnit = unitRows[0].liveUnit liveUnit = unitRows[0].liveUnit
@ -100,7 +105,7 @@ var liveCattlePrice = []
var beefPrice = [] var beefPrice = []
for (bucket in buckets) { for (bucket in buckets) {
var monthKey = bucket.monthStart var monthKey = bucket.year + '-' + bucket.month
labels.push(bucket.label) labels.push(bucket.label)
liveCattlePrice.push(liveMap[monthKey] != null ? liveMap[monthKey] : null) liveCattlePrice.push(liveMap[monthKey] != null ? liveMap[monthKey] : null)
beefPrice.push(beefMap[monthKey] != null ? beefMap[monthKey] : null) beefPrice.push(beefMap[monthKey] != null ? beefMap[monthKey] : null)

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "牦牛销售类型统计", "name" : "牦牛销售类型统计",
"createTime" : 1780880400000, "createTime" : 1780880400000,
"updateTime" : null, "updateTime" : 1781375000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,54 +17,41 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "牦牛销售类型统计:按订单 purpose 归类为屠宰/养殖/其他,统计已完成订单牦牛头数与占比(只读)。", "description" : "牦牛销售类型统计:gov_count_order_total 按 purpose 分组,SUM(yak_number) 为成交头数,一条记录一单。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读查询,不修改任何数据 // gov_count_order_total:一条记录 = 一笔订单
// 数据源 yak_sn_order.purpose,按用途关键词归类 // 成交头数:SUM(yak_number)
// 销售类型:purpose(屠宰 / 养殖 / 其他 等)
var sql = """ var sql = """
SELECT category AS name, COALESCE(SUM(quantity), 0) AS value
FROM (
SELECT SELECT
o.quantity, COALESCE(NULLIF(TRIM(purpose), ''), '其他') AS name,
CASE COALESCE(SUM(yak_number), 0) AS value
WHEN TRIM(COALESCE(o.purpose, '')) ~* '(屠宰|餐饮|肉食|宰杀)' THEN '屠宰用途' FROM gov_count_order_total
WHEN TRIM(COALESCE(o.purpose, '')) ~* '(养殖|繁育|种|批发|转售)' THEN '养殖用途' GROUP BY COALESCE(NULLIF(TRIM(purpose), ''), '其他')
ELSE '其他用途' ORDER BY value DESC
END AS category
FROM yak_sn_order o
WHERE o.del_flag = '0'
AND o.status = 'COMPLETED'
) t
GROUP BY category
""" """
var rows = db.select(sql) var rows = db.select(sql)
var categories = ['屠宰用途', '养殖用途', '其他用途']
var valueMap = {}
for (row in rows) {
valueMap[row.name] = row.value ? row.value : 0
}
var total = 0 var total = 0
for (cat in categories) { for (row in rows) {
var count = valueMap[cat] ? valueMap[cat] : 0 var count = row.value != null ? row.value : 0
total = total + count total = total + count
} }
var result = [] var result = []
for (cat in categories) { for (row in rows) {
var count = valueMap[cat] ? valueMap[cat] : 0 var count = row.value != null ? row.value : 0
var percent = 0 var percent = 0
if (total > 0) { if (total > 0) {
percent = (count * 100.0) / total percent = (count * 100.0) / total
} }
result.push({ result.push({
name: cat, name: row.name,
value: count, value: count,
percent: percent percent: percent
}) })

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "综合销售统计", "name" : "综合销售统计",
"createTime" : 1780877100000, "createTime" : 1780877100000,
"updateTime" : null, "updateTime" : 1781368000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -29,31 +29,58 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "综合销售统计:本月销售/总体销售/采购地区三维饼图数据,数据源 yak_sn_order 已完成订单(只读)", "description" : "综合销售统计:数据源 gov_count_order_total(只读)。销售按 origin_place,采购地区按 buyer_place,头数 yak_number。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 数据源 yak_sn_order + yak_sn_customer // gov_count_order_total
// value = 牦牛交易头数 quantity // 本月/总体销售:origin_place + SUM(yak_number)
// 采购地区:buyer_place + SUM(yak_number)(库表字段 buyer_place)
var mapRows = (rows) => { var mapOriginRows = (rows) => {
var total = 0 var total = 0
var result = [] var result = []
for (row in rows) { for (row in rows) {
total = total + (row.value ? row.value : 0) var qty = row.yakNumber != null ? row.yakNumber : 0
total = total + qty
} }
for (row in rows) { for (row in rows) {
var value = row.value ? row.value : 0 var qty = row.yakNumber != null ? row.yakNumber : 0
var percent = 0 var percent = 0
if (total > 0) { if (total > 0) {
percent = (value * 100) / total percent = (qty * 100) / total
} }
result.push({ result.push({
name: row.name, originPlace: row.originPlace ? row.originPlace : '未知地区',
value: value, yakNumber: qty,
percent: percent
})
}
return result
}
var mapBuyerRows = (rows) => {
var total = 0
var result = []
for (row in rows) {
var qty = row.yakNumber != null ? row.yakNumber : 0
total = total + qty
}
for (row in rows) {
var qty = row.yakNumber != null ? row.yakNumber : 0
var percent = 0
if (total > 0) {
percent = (qty * 100) / total
}
result.push({
buyerPlace: row.buyerPlace ? row.buyerPlace : '未知地区',
yakNumber: qty,
percent: percent percent: percent
}) })
} }
@ -62,80 +89,48 @@ var mapRows = (rows) => {
} }
var monthlySql = """ var monthlySql = """
SELECT town AS name, COALESCE(SUM(quantity), 0) AS value WITH anchor AS (
FROM ( SELECT year, month
FROM gov_count_order_total
WHERE year IS NOT NULL AND month IS NOT NULL
ORDER BY year DESC, month DESC
LIMIT 1
)
SELECT SELECT
o.quantity, TRIM(o.origin_place) AS origin_place,
COALESCE( COALESCE(SUM(o.yak_number), 0) AS yak_number
NULLIF((regexp_match(o.origin_place, '([^省市区县]+(?:镇|乡|街道))'))[1], ''), FROM gov_count_order_total o
NULLIF(TRIM(o.origin_place), ''), CROSS JOIN anchor a
'未知地区' WHERE o.year = a.year
) AS town AND o.month = a.month
FROM yak_sn_order o AND o.origin_place IS NOT NULL
CROSS JOIN ( AND TRIM(o.origin_place) <> ''
SELECT CASE GROUP BY TRIM(o.origin_place)
WHEN EXISTS ( ORDER BY yak_number DESC, origin_place
SELECT 1 FROM yak_sn_order x
WHERE x.del_flag = '0'
AND x.status = 'COMPLETED'
AND x.transaction_time >= date_trunc('month', CURRENT_DATE)
AND x.transaction_time < date_trunc('month', CURRENT_DATE) + INTERVAL '1 month'
) THEN date_trunc('month', CURRENT_DATE)
ELSE date_trunc('month', (
SELECT MAX(transaction_time) FROM yak_sn_order
WHERE del_flag = '0' AND status = 'COMPLETED'
))
END AS month_start
) m
WHERE o.del_flag = '0'
AND o.status = 'COMPLETED'
AND o.transaction_time >= m.month_start
AND o.transaction_time < m.month_start + INTERVAL '1 month'
) t
GROUP BY town
ORDER BY value DESC
LIMIT 5 LIMIT 5
""" """
var overallSql = """ var overallSql = """
SELECT town AS name, COALESCE(SUM(quantity), 0) AS value
FROM (
SELECT SELECT
o.quantity, TRIM(origin_place) AS origin_place,
COALESCE( COALESCE(SUM(yak_number), 0) AS yak_number
NULLIF((regexp_match(o.origin_place, '([^省市区县]+(?:镇|乡|街道))'))[1], ''), FROM gov_count_order_total
NULLIF(TRIM(o.origin_place), ''), WHERE origin_place IS NOT NULL
'未知地区' AND TRIM(origin_place) <> ''
) AS town GROUP BY TRIM(origin_place)
FROM yak_sn_order o ORDER BY yak_number DESC, origin_place
WHERE o.del_flag = '0'
AND o.status = 'COMPLETED'
) t
GROUP BY town
ORDER BY value DESC
LIMIT 5 LIMIT 5
""" """
var purchaseSql = """ var purchaseSql = """
SELECT region AS name, COALESCE(SUM(quantity), 0) AS value
FROM (
SELECT SELECT
o.quantity, TRIM(buyer_place) AS buyer_place,
COALESCE( COALESCE(SUM(yak_number), 0) AS yak_number
NULLIF(TRIM(b.region_name), ''), FROM gov_count_order_total
NULLIF((regexp_match(o.destination_place, '([^省市区县]+(?:市|州|盟|县|区))'))[1], ''), WHERE buyer_place IS NOT NULL
NULLIF(TRIM(o.destination_place), ''), AND TRIM(buyer_place) <> ''
'未知地区' GROUP BY TRIM(buyer_place)
) AS region ORDER BY yak_number DESC, buyer_place
FROM yak_sn_order o
LEFT JOIN yak_sn_customer b
ON b.id = o.buyer_id
AND b.del_flag = '0'
WHERE o.del_flag = '0'
AND o.status = 'COMPLETED'
) t
GROUP BY region
ORDER BY value DESC
LIMIT 5 LIMIT 5
""" """
@ -144,9 +139,9 @@ var overallRows = db.select(overallSql)
var purchaseRows = db.select(purchaseSql) var purchaseRows = db.select(purchaseSql)
var result = { var result = {
monthlySales: mapRows(monthlyRows), monthlySales: mapOriginRows(monthlyRows),
overallSalesDistribution: mapRows(overallRows), overallSalesDistribution: mapOriginRows(overallRows),
purchaseRegionDistribution: mapRows(purchaseRows) purchaseRegionDistribution: mapBuyerRows(purchaseRows)
} }
var t = type var t = type

@ -5,7 +5,7 @@
"groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1", "groupId" : "f8e3d2c1b0a94e5f8a7b6c5d4e3f2a1",
"name" : "采购商户来源分析", "name" : "采购商户来源分析",
"createTime" : 1780881000000, "createTime" : 1780881000000,
"updateTime" : null, "updateTime" : 1781376000000,
"lock" : null, "lock" : null,
"createBy" : "admin", "createBy" : "admin",
"updateBy" : "admin", "updateBy" : "admin",
@ -17,26 +17,26 @@
"headers" : [ ], "headers" : [ ],
"paths" : [ ], "paths" : [ ],
"responseBody" : null, "responseBody" : null,
"description" : "采购商户来源分析:按地区统计采购商户(BUYER)数量 Top9,数据源 yak_sn_customer(只读)。", "description" : "采购商户来源分析:gov_count_order_total 按 buyer_place 统计采购商户数 Top9,一条记录一单。",
"requestBodyDefinition" : null, "requestBodyDefinition" : null,
"responseBodyDefinition" : null "responseBodyDefinition" : null
} }
================================ ================================
// 只读查询,不修改任何数据 // gov_count_order_total:一条记录 = 一笔订单
// 采购地区:buyer_place
// 采购商户数:COUNT(DISTINCT buyer_name),buyer_name 为空时按 id 计 1 户
var sql = """ var sql = """
SELECT SELECT
region_name AS name, buyer_place AS name,
region_name AS full_name, buyer_place AS full_name,
COUNT(*) AS value, COUNT(DISTINCT COALESCE(NULLIF(TRIM(buyer_name), ''), id)) AS value,
region_name || '采购商户' AS description buyer_place || '采购商户' AS description
FROM yak_sn_customer c FROM gov_count_order_total
WHERE c.del_flag = '0' WHERE buyer_place IS NOT NULL
AND c.customer_type = 'BUYER' AND TRIM(buyer_place) <> ''
AND c.region_name IS NOT NULL GROUP BY buyer_place
AND TRIM(c.region_name) <> '' ORDER BY value DESC, buyer_place
GROUP BY region_name
ORDER BY value DESC, region_name
LIMIT 9 LIMIT 9
""" """
@ -46,7 +46,7 @@ var result = []
for (row in rows) { for (row in rows) {
result.push({ result.push({
name: row.name, name: row.name,
value: row.value ? row.value : 0, value: row.value != null ? row.value : 0,
fullName: row.fullName ? row.fullName : (row.full_name ? row.full_name : ''), fullName: row.fullName ? row.fullName : (row.full_name ? row.full_name : ''),
description: row.description ? row.description : '采购商户' description: row.description ? row.description : '采购商户'
}) })

@ -23,13 +23,13 @@ GET /dashboard/system-config
GET /dashboard/real-time-stats GET /dashboard/real-time-stats
文件:实时交易统计.ms | 前端:RealTimeStats.vue 文件:实时交易统计.ms | 前端:RealTimeStats.vue
业务:按时间维度统计交易所已完成订单的交易量、订单数、买卖商户数 业务:按日/周/月读取政务统计指标(牦牛交易量、订单量、买卖商户数)
参数:dimension(可选)day / week / month / year,不传返回全部 参数:dimension(可选)day / week / month,不传返回全部
表:yak_sn_order 表:gov_count_item
字段:quantity, seller_id, buyer_id, transaction_time, status, del_flag 字段:key(mnjyzl/jdjyzl/xsshsl/cgshsl), name, value, count_date
条件:del_flag='0',status='COMPLETED' 口径:day=最新 count_date;week=近7天;month=近30天;mnjyzl/jdjyzl 按日求和,xsshsl/cgshsl 取区间内最大值
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -38,13 +38,14 @@ GET /dashboard/real-time-stats
GET /dashboard/yak-trading-data GET /dashboard/yak-trading-data
文件:交易所牦牛成交数据.ms | 前端:YakTradingData.vue 文件:交易所牦牛成交数据.ms | 前端:YakTradingData.vue
业务:近 6 个日/周/月时间桶的牦牛头数与订单数趋势。 业务:近 6 个日/周/月时间桶的牦牛成交头数与订单数趋势(一条记录 = 一单)
参数:period(可选)day / week / month,不传返回全部 参数:period(可选)day / week / month,不传返回全部
表:yak_sn_order 表:gov_count_order_total
字段:id, quantity, transaction_time, status, del_flag 字段:id, yak_number, update_time
条件:del_flag='0',status='COMPLETED' 口径:SUM(yak_number) 为成交头数,COUNT(*) 为订单数;按 update_time 分桶
返回:labels, yakNumber, orderCount
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -53,15 +54,12 @@ GET /dashboard/yak-trading-data
GET /dashboard/comprehensive-sales-stats GET /dashboard/comprehensive-sales-stats
文件:综合销售统计.ms | 前端:ComprehensiveSalesStats.vue 文件:综合销售统计.ms | 前端:ComprehensiveSalesStats.vue
业务:三个饼图——本月销售、总体销售(按产地乡镇 Top5)、采购地区(Top5)。 业务:三个饼图——本月销售、总体销售(按 origin_place Top5)、采购地区(按 buyer_place Top5)。
参数:type(可选)monthlySales / overallSalesDistribution / purchaseRegionDistribution 参数:type(可选)monthlySales / overallSalesDistribution / purchaseRegionDistribution
表:yak_sn_order;采购地区 LEFT JOIN yak_sn_customer 表:gov_count_order_total
字段: 字段:origin_place, buyer_place, yak_number, year, month
yak_sn_order — quantity, origin_place, destination_place, buyer_id, transaction_time, status, del_flag
yak_sn_customer — id, region_name, del_flag
条件:del_flag='0',status='COMPLETED'
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -70,10 +68,14 @@ GET /dashboard/comprehensive-sales-stats
GET /dashboard/price-trend GET /dashboard/price-trend
文件:活牛鲜肉价格趋势.ms | 前端:YakPriceTrend.vue 文件:活牛鲜肉价格趋势.ms | 前端:YakPriceTrend.vue
业务:近 8 个月活牛、鲜肉市场采集均价(不做重量折算)。 业务:近 8 个月活牛、鲜肉采集均价(不做重量折算)。
表:yak_trade_market_price 表:gov_count_price
字段:price_date, price_type(LIVE_YAK / FRESH_MEAT), price, unit 字段:year, month, price_key, value, unit
口径:
活牛 — price_key = hx(活畜),各区域 AVG(value)
鲜肉 — price_key IN (mnr, mnt, mnp)(牦牛肉/腿/排),各区域 AVG(value)
返回:labels, liveCattlePrice, beefPrice, liveUnit, beefUnit
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -82,14 +84,19 @@ GET /dashboard/price-trend
GET /dashboard/exchange-service-info GET /dashboard/exchange-service-info
文件:交易中心实时服务信息.ms | 前端:ExchangeMonitor.vue 文件:交易中心实时服务信息.ms | 前端:ExchangeMonitor.vue
业务:交易中心实时概览——供应/待售/已售头数、剩余车位、今日进场车辆、供应商数。 业务:交易中心实时概览——供应/待售/已售头数、剩余车位、进场车辆、供应商数。
表及字段: 表:gov_count_real_info
yak_trade_ear_tag_inventory — status(计总供应/待售/已售) 条件:group_item = jygy,source_system = yzt
yak_car_parking_zone — total_capacity, current_count, status 字段:key, name, value, tag, unit
yak_car_record — plate_no, entry_time, del_flag key 映射:
yak_trade_entry_record — vehicle_no, entered_at mngyzl → totalSupply(牦牛供应总量)
yak_sn_customer — customer_type='SELLER', del_flag dsmn → forSaleYaks(待售牦牛)
ysmn → soldYaks(已售牦牛)
sycw → remainingParking(剩余车位)
jccl → enteringVehicles(进场车辆)
gyssl → supplierCount(供应商数量)
返回:上述 value + Tag + Unit
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -98,11 +105,12 @@ GET /dashboard/exchange-service-info
GET /dashboard/yak-sales-type-stats GET /dashboard/yak-sales-type-stats
文件:牦牛销售类型统计.ms | 前端:YakSalesTypeStats.vue 文件:牦牛销售类型统计.ms | 前端:YakSalesTypeStats.vue
业务:按订单用途归类为屠宰/养殖/其他,统计头数与占比 业务:按 purpose 统计成交头数与占比(一条记录 = 一单)
表:yak_sn_order 表:gov_count_order_total
字段:quantity, purpose, status, del_flag 字段:purpose, yak_number
条件:del_flag='0',status='COMPLETED' 口径:GROUP BY purpose,SUM(yak_number);空 purpose 归为「其他」
返回:name(屠宰/养殖/其他等), value, percent
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -111,11 +119,12 @@ GET /dashboard/yak-sales-type-stats
GET /dashboard/buyer-source-analysis GET /dashboard/buyer-source-analysis
文件:采购商户来源分析.ms | 前端:PurchaserAnalysis.vue 文件:采购商户来源分析.ms | 前端:PurchaserAnalysis.vue
业务:按地区统计采购商户(BUYER)数量 Top9 业务:按 buyer_place 统计采购商户数量 Top9(一条记录 = 一单)
表:yak_sn_customer 表:gov_count_order_total
字段:region_name, customer_type, del_flag 字段:buyer_place, buyer_name, id
条件:customer_type='BUYER',del_flag='0',region_name 非空 口径:GROUP BY buyer_place,COUNT(DISTINCT buyer_name);buyer_name 为空时按 id 计 1 户
返回:name, value, fullName, description
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -164,11 +173,21 @@ GET /dashboard/yak-supply-detail
GET /dashboard/market-environment GET /dashboard/market-environment
文件:市场环境监控.ms | 前端:MarketEnvironmentMonitor.vue 文件:市场环境监控.ms | 前端:MarketEnvironmentMonitor.vue
业务:环境温湿度 + 气象数据各取最新一条 业务:环境温湿度 + 气象八项实时指标
表及字段: 表:gov_count_real_info
iot_device_env_data — temperature, humidity, collect_time 条件:group_item = env-monitor,source_system = yzt
iot_device_weather_data — air_pressure, pm25, pm10, uv_index, rainfall, wind_speed, wind_direction, collect_time 字段:key, name, value, tag, unit
key 映射:
temperature 温度
humidity 湿度
aqi 空气质量
pressure 大气压强
uv 紫外线
rainfall 降雨量
wind_direction 风向(value=角度,tag=风向名)
wind_power 风力(value=级数)
返回:上述 value + Tag + Unit
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -177,21 +196,23 @@ GET /dashboard/market-environment
GET /dashboard/market-realtime-monitor GET /dashboard/market-realtime-monitor
文件:市场实时监控.ms | 前端:MarketRealtimeMonitor.vue 文件:市场实时监控.ms | 前端:MarketRealtimeMonitor.vue
业务:视频监控设备列表及播放/预览地址。 业务:萤石云视频监控设备列表,EZUIKit 播放 ezopen 地址。
表及字段: 表及字段:
iot_device_video — id, name, number, location, address, status, play_url, hd_play_url, preview_img_url, channel_number, index, del_flag, is_show iot_device_video — id, name, serial_number, channel_number, status, play_url, hd_play_url, preview_img_url, video_account_id, index, del_flag, is_show
iot_device_video_data — device_id, stream_url, snapshot_url, online_status, stream_status, fault_code, collect_time iot_video_account — id, app_key, token
关联:v.video_account_id = a.id
条件:del_flag='0',is_show=true 条件:del_flag='0',is_show=true
返回:ezopenUrl, accessToken, preview 等
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
13. 市场实时监控播放配置 13. 市场实时监控播放配置
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
GET /dashboard/market-realtime-player-config GET /dashboard/market-realtime-player-config
文件:市场实时监控播放配置.ms | 前端:MarketRealtimeMonitor.vue(liveStreamPlayer.js) 文件:市场实时监控播放配置.ms | 前端:MarketRealtimeMonitor.vue(ezvizPlayer.js)
业务:直播流协议识别规则与 hls.js / flv.js 播放器参数(静态配置)。 业务:萤石云 EZUIKit 播放器参数(staticPath、template、scaleMode 等)。
数据表:无 数据表:无
@ -202,22 +223,25 @@ GET /dashboard/market-realtime-player-config
GET /dashboard/map-trading-network GET /dashboard/map-trading-network
文件:地图迁徙数据.ms | 前端:ChinaMap.vue 文件:地图迁徙数据.ms | 前端:ChinaMap.vue
业务:中央地图三种模式流向——销售网络(红原→全国)、源地供应(全国→红原)、红原出栏(乡镇→红原) 业务:中央地图三种模式流向,头数 SUM(yak_number),一条记录一单
表:yak_sn_order;outflow/inflow LEFT JOIN yak_sn_customer 表:gov_count_order_total
字段: 字段:origin_place, destination_place, yak_number
yak_sn_order — quantity, origin_place, destination_place, buyer_id, seller_id, status, del_flag 口径:
yak_sn_customer — id, region_name, del_flag outflow 销售网络 — origin_place 含「红原」,按 destination_place 聚合,红原→全国
条件:del_flag='0',status='COMPLETED';local 模式另要求 origin_place 含「红原」 inflow 源地供应 — destination_place 含「红原」,按 origin_place 聚合,全国→红原
local 红原出栏 — destination_place 含「红原」且 origin_place 含「红原」,按 origin_place 乡镇聚合,县内乡镇→红原
关联:系统配置 mapHub.name 与脚本 hubName 需一致(默认「红原县」) 关联:系统配置 mapHub.name 与脚本 hubName 需一致(默认「红原县」)
================================================================================ ================================================================================
表 → 接口索引 表 → 接口索引
================================================================================ ================================================================================
yak_sn_order 2, 3, 4, 7, 14 gov_count_real_info 6, 11
yak_sn_customer 4, 6, 8, 9, 10, 14 gov_count_price 5
yak_trade_market_price 5 gov_count_order_total 3, 4, 7, 8, 14
gov_count_item 2
yak_sn_customer 4, 6, 9, 10
yak_trade_ear_tag_inventory 6, 9, 10 yak_trade_ear_tag_inventory 6, 9, 10
yak_trade_entry_record 6, 9, 10 yak_trade_entry_record 6, 9, 10
yak_trade_entry_record_ear_tag 9, 10 yak_trade_entry_record_ear_tag 9, 10
@ -227,8 +251,6 @@ yak_trade_quarantine_certificate 10
yak_car_parking_zone 6 yak_car_parking_zone 6
yak_car_record 6, 10 yak_car_record 6, 10
sys_oss 10 sys_oss 10
iot_device_env_data 11 iot_video_account 12
iot_device_weather_data 11
iot_device_video 12 iot_device_video 12
iot_device_video_data 12
(无表) 1, 13 (无表) 1, 13

Loading…
Cancel
Save