13 KiB
主要展示(其余字段在前端暂时隐藏或注释掉)
-
位置信息:标识、位置编号、位置别名、父级编号、是否标识、备注、创建人、创建时间、更新人、更新时间
-
设备信息:标识、设备编号、设备名称、所在位置、设备地址、设备端口、读取频率、是否标识、备注、创建人、创建时间、更新人、更新时间
-
读取记录:标识、设备编号、读取状态(1-成功;0-失败)、条码信息、记录时间
-
代码注意事项
- ** 连表查询规范**
使用mybatis-plus做连表查询时:MybatisPlus自带方法不支持连表查询,需要在mapper.xml中编写sql语句.- 在实体类和
VO中添加连表字段,实体类字段需加@TableField(exist = false)注解。
- types 同步维护
连表查询得到的字段,需要同步补充到前端types.ts中,保证类型定义完整。 - 接口前缀规范
参考同模块下已有接口前缀,统一后台@RequestMapping前缀。 - 前端列表 / 表单字段规范
- 只展示业务字段,技术字段仅用于内部关联(连表查询得到名称字段),若无关联则隐藏技术字段(前端在
columns中设为false) - 业务字段不显示多余的说明文字,例如字典字段的括号以及括号的字典数据
- 列表不直接展示主键,使用“序号”列(
index)替代。 - 注释掉不合理的搜索条件,对话框中注释掉不合理的输入字段。
- 数字字段使用数字输入框;
- 新增对话框时,前端激活标识
isMarked默认为1。 - 需要绑定其他表字段时,通过下拉框选择,表单字段使用
get${ClassName}List接口获取不分页的全部数据。
- 只展示业务字段,技术字段仅用于内部关联(连表查询得到名称字段),若无关联则隐藏技术字段(前端在
- 主子表 List 字段
主子表结构时,需要在主表实体中将子表作为List类型字段,便于提交新增/修改、返回数据和删除时统一处理,保证事务一致性。 - 字典使用规范
- 后端只返回字典键值。
- 前端列表通过
dict-tag组件展示字典文本。 - 前端需要显式声明字典,例如:
const { project_category, change_type, project_phases } = toRefs<any>(proxy?.useDict('project_category', 'change_type', 'project_phases'));
- 实体字段类型规范
- 数据库
int→ JavaInteger decimal→BigDecimalbigint→Longdouble→Doublefloat→Float
- 数据库
- ** 连表查询规范**
- 主子表删除规范
- 主子表删除时,必须在同一事务中同时删除主表与子表记录,保证数据一致性。
- 接口合并与聚合
- 前端调用接口应尽量合并:同一模块或同一功能点优先通过单个接口组装返回所需数据,减少多次请求。
- 前端 columns 配置规则
columns的key必须从0开始,不间断完整自增。- 对比实际表格列与
columns配置,第0列使用“序号”(index列)而不是主键。 - 将“项目ID”等技术列从
columns中完全移除(而不是隐藏),去掉租户编号等无业务意义的字段,其他字段根据实际情况调整,确保columns.key连续从0自增且与表格实际列一一对应。
- 名称获取规范
- 所有创建人名称、部门名称必须通过后端连表查询得到,而不是前端拼接或本地缓存获取。
- 业务编号规范
- 业务编号应该唯一。
- 树形结构规范
- 树形结构的parent_id和ancestors字段的递归逻辑需要完善,若parent_id和ancestors为0则为顶级节点,0是必须有的。
- 更新子节点的ancestors
- 更新子节点的ancestors时,需要更新所有子节点的ancestors,而不是只更新当前节点的ancestors。
- 前端树形结构的新增或修改
- 前端对话框第一行必须是先选择父节点!
- 特别注意:rfid_read_record分表
- rfid_read_record按照日期分表,例如rfid_read_record_20251126,可参考rfid_read_record_20251126.sql文件,数据库类型是MySQL
- 前后端代码可以参考ShardingQuery.md文件(其他项目TiDb数据库的总结文档)
业务逻辑
位置树与设备
- 位置树分为车间/机台/工位,共三级
- 设备绑定在工位上
- IP地址 deviceAddress 唯一校验
- 端口号devicePort应该都是20108,创建的时候先默认这个,如果有需要再修改
设备记录
分页查询
- 分页查询逻辑参考rfid-middleware\ruoyi-modules\hw-rfid\ShardingQuery.md文档
条码信息显示空格符号的空格处理
- 202020202020205357303034 设备返回的这个 20 转成 ascii 是个空格
- 条码信息原始值可能包含空格(0x20) 和 NULL 字符(0x00) 等不可见字符,例如设备返回
202020202020205357303034,其中20转成 ASCII 是空格。 - Mapper 层在
RfidReadRecordMapper.xml中对barcode使用TRIM(t.barcode) as barcode去掉前后空格,但对 NULL/控制字符无效(SQL 的TRIM()只能去除标准空格 ASCII 32)。 - Service 层在
RfidReadRecordServiceImpl中通过cleanBarcodeList / cleanBarcode,使用正则Pattern.compile("[\\s\\x00-\\x1F\\x7F]+")清理所有不可见字符,只保留业务字符,最终前端看到的条码为无空格的纯业务值(例如QNC006)。
设备记录成功率逻辑
- 成功率统计接口:
GET /rfid/dashboard/successRate,按小时(0–23 点)返回折线图数据,用于前端折线图展示. - 统计维度:后端自动以当前系统日期作为“今日”,同时统计昨日同一小时的成功率,前端无需传任何查询参数.
- 分表与时间范围:分别通过
RfidReadRecordTableHelper.getTableName(今日)与RfidReadRecordTableHelper.getTableName(昨日)生成当日与昨日分表(如rfid_read_record_20251126),各自只统计00:00:00至23:59:59之间的读取记录. - 成功率聚合 SQL:在分表上按
DATE_FORMAT(record_time, '%H:00')分组,统计每小时的总记录数totalCount与成功记录数successCount(read_status = '1')。 - 成功率计算公式:
successRate = ROUND(successCount * 100.0 / totalCount, 2),即“该小时成功条数 ÷ 总条数 × 100”,保留两位小数. - Mapper 至少返回
timePoint、successRate字段,Service 层分别将“今日”与“昨日”的统计结果转为Map<timePoint, successRate>,再按 0–23 小时顺序构造 24 条数据:successRate:今日该小时成功率;yesterdaySuccessRate:昨日同一小时成功率;- 对于没有统计结果的小时,对应字段返回
null,保证时间轴完整.
- 异常与容错:当某日分表不存在或查询出错时,Service 捕获异常记录告警日志并返回空列表或空 Map,不影响整个看板接口的可用性.
看板模块
接口1:实时统计(顶部概览 + 告警列表)
请求方式:
GET /rfid/dashboard/realtime
前端方法:getRealtimeStats(alarmLimit?: number)
后端链路:DashboardController#getRealtimeStats→IDashboardService#getRealtimeStats
请求参数(Query)
alarmLimit?: number
告警列表限制数量,可选。- 不传:返回全部告警记录,供前端滚动显示。
返回实体与字段
- 后端返回类型:
DashboardVO.RealtimeStats - 前端 TS 类型:
RealtimeStats
字段说明:
-
overview: StatisticsOverview—— 顶部统计概览deviceTotal: number:设备总数(仅统计is_marked = 1的设备)。onlineCount: number:在线设备数量(online_status = 1)。offlineCount: number:离线设备数量(online_status = 0)。alarmCount: number:告警设备数量(alarm_status = 1)。
-
alarmStats: AlarmStatVO[]—— 告警统计列表alarmTime: string:告警时间(MM-dd HH:mm格式)。deviceName: string:设备名称。location: string:所在位置名称。alarmLevel: string:告警级别。alarmAction: string:告警行为/建议动作。
接口2:位置树(含设备信息)
请求方式:
GET /rfid/dashboard/deviceStatus
前端方法:getLocationTree()
后端链路:DashboardController#getLocationTree→IDashboardService#getLocationTree
请求参数(Query)
- 无参数,直接调用即返回完整位置树。
返回实体与字段
- 后端返回类型:
List<DashboardVO.LocationTreeNode> - 前端 TS 类型:
LocationTreeNode[]
字段说明:
-
位置基础信息
id: number:位置 ID。locationCode: string:位置编号。locationAlias: string:位置别名。locationType: string:位置类型(1-车间;2-工序;3-工位/设备)。parentId: number:父级位置 ID(0或null表示顶级)。
-
设备信息(仅当
locationType = 3时有值)deviceId: number:设备 ID,前端用于匹配 WebSocket 数据。deviceCode: string:设备编号。deviceName: string:设备名称。onlineStatus: string:在线状态(1-在线;0-离线)。alarmStatus: string:告警状态(0-正常;1-告警)。
-
子节点
children: LocationTreeNode[]:子位置/设备列表。
WebSocket 数据匹配说明
C# 服务会通过 WebSocket 实时推送设备读取记录,格式示例:
{
"objid": 1993656942031147008,
"deviceId": 1,
"readStatus": "1",
"epcStr": "SW004",
"alarmFlag": "0",
"alarmLevel": "",
"alarmType": "",
"alarmAction": "",
"recordTime": "2025-11-26T20:23:49.695356+08:00"
}
前端通过 deviceId 字段匹配位置树中的设备节点,实现实时数据展示。
接口3:设备记录的成功率(按小时统计)
请求方式:
GET /rfid/dashboard/successRate
前端方法:getSuccessRateTrends(type?: string)
后端链路:DashboardController#getSuccessRateTrends→DashboardServiceImpl#getSuccessRateTrends
请求参数(Query)
type?: string- 前端不传参,后端直接默认当天,返回
返回实体与字段
- 后端返回类型:
List<DashboardVO.SuccessRateTrend> - 前端 TS 类型:
SuccessRateTrend[]
字段说明:
-
timePoint: string- 时间点,格式为
"HH:00",例如"09:00",从"00:00"到"23:00"共 24 个。
- 时间点,格式为
-
successRate: number | null- 该小时的读取成功率(百分比,如
98.5)。 - 如果某个小时没有任何读取记录,则为
null。
- 该小时的读取成功率(百分比,如
后端处理要点(概要版)
-
查询维度
- 入参
type:today(默认)、yesterday。 - 根据
type计算目标日期targetDate:today或其它值 →LocalDate.now()(今日);yesterday→LocalDate.now().minusDays(1)(昨日)。
- 入参
-
分表与时间范围
- 使用
RfidReadRecordTableHelper.getTableName(targetDate)生成当日分表名,例如rfid_read_record_20251126。 - 计算当日时间范围字符串:
- 起始时间
startTime = targetDate.atStartOfDay()(yyyy-MM-dd 00:00:00); - 结束时间
endTime = targetDate.atTime(23, 59, 59)(yyyy-MM-dd 23:59:59)。
- 起始时间
- 使用
-
按小时统计成功率(Mapper 层)
- 调用
readRecordMapper.selectSuccessRateByHour(tableName, startTime, endTime)进行聚合统计。 - SQL 约定返回字段:
timePoint:小时点,格式为"HH:00",如"08:00";successRate:该小时的读取成功率(类型可能为Double/BigDecimal等)。
- 成功率的具体计算逻辑由 Mapper SQL 维护(如成功条数 / 总条数),Service 层只消费结果,不参与公式计算。
- 调用
-
补全 0–23 小时完整时间轴
- 将 Mapper 返回结果转换为
Map<String, Double>; - 循环
hour = 0..23构建 24 个时间点; - 对没有统计结果的小时,
successRate返回为null。
- 将 Mapper 返回结果转换为
-
前端展示约定
- 折线图 X 轴:直接使用
timePoint("HH:00")。 - 折线图 Y 轴:使用
successRate数值。 - 当前端拿到
successRate = null的点时,可根据实际需求选择:- 展示为空值(不连线 / 断点);或
- 按 0 处理(展示为 0%)。
- 由于后端已保证 24 个小时点完整返回,前端无需再做补全或排序逻辑。
- 折线图 X 轴:直接使用
原有接口(聚合 / 辅助)
-
GET /rfid/dashboard/data- 前端方法:
getDashboardData(locationId?: number)/getDashboardStats(locationId?: number)(兼容老名称)。 - 参数:
locationId?: number,位置 ID,可选。 - 返回:
DashboardVO(统计概览 + 设备状态列表 + 成功率趋势 + 告警统计)。
- 前端方法:
-
GET /rfid/dashboard/overview- 前端方法:
getOverview()。 - 参数:无。
- 返回:
StatisticsOverview,字段含义同上。
- 前端方法:
-
GET /rfid/dashboard/alarmStats- 前端方法:
getAlarmStats(limit?: number)。 - 参数:
limit?: number,限制返回条数,默认 10。 - 返回:
AlarmStatVO[],字段含义同实时统计中的alarmStats.
- 前端方法: