diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/controller/RfidDashboardController.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/controller/RfidDashboardController.java index 4e80bd4..6af08cd 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/controller/RfidDashboardController.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/controller/RfidDashboardController.java @@ -1,12 +1,9 @@ package org.dromara.rfid.controller; -import cn.dev33.satoken.annotation.SaCheckPermission; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; -import org.dromara.rfid.domain.RfidDevice; import org.dromara.rfid.domain.vo.RfidDashboardStatsVo; -import org.dromara.rfid.service.IRfidDeviceService; +import org.dromara.rfid.mapper.RfidDeviceMapper; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -24,42 +21,14 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/rfid/dashboard") public class RfidDashboardController { - private final IRfidDeviceService rfidDeviceService; - - /** 在线状态:在线 */ - private static final String ONLINE_STATUS_ONLINE = "1"; - - /** 在线状态:离线 */ - private static final String ONLINE_STATUS_OFFLINE = "0"; - - /** 告警状态:告警 */ - private static final String ALARM_STATUS_ALARM = "1"; + private final RfidDeviceMapper rfidDeviceMapper; /** * 获取首页统计数据 */ @GetMapping("/stats") public R getStats() { - RfidDashboardStatsVo stats = new RfidDashboardStatsVo(); - - // 设备总数 - stats.setTotalDevices(rfidDeviceService.count()); - - // 在线数量 (online_status = 1) - stats.setOnlineDevices(rfidDeviceService.count( - Wrappers.lambdaQuery().eq(RfidDevice::getOnlineStatus, ONLINE_STATUS_ONLINE)//在线状态(0-离线;1-在线) - )); - - // 离线数量 (online_status = 0) - stats.setOfflineDevices(rfidDeviceService.count( - Wrappers.lambdaQuery().eq(RfidDevice::getOnlineStatus, ONLINE_STATUS_OFFLINE)//在线状态(0-离线;1-在线) - )); - - // 告警数量 (alarm_status = 1) - stats.setAlarmDevices(rfidDeviceService.count( - Wrappers.lambdaQuery().eq(RfidDevice::getAlarmStatus, ALARM_STATUS_ALARM)//告警状态(0-正常;1-告警) - )); - - return R.ok(stats); + // 旧接口仍保持原有统计口径,但用一次聚合查询降低首页刷新时的数据库压力。 + return R.ok(rfidDeviceMapper.selectDashboardStats()); } } diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/RfidReadRecord.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/RfidReadRecord.java index ce8581a..f507a73 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/RfidReadRecord.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/RfidReadRecord.java @@ -1,13 +1,13 @@ package org.dromara.rfid.domain; -import org.dromara.common.mybatis.core.domain.BaseEntity; -import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; -import java.util.Date; -import com.fasterxml.jackson.annotation.JsonFormat; +import org.dromara.common.mybatis.core.domain.BaseEntity; import java.io.Serial; +import java.util.Date; /** * 读取记录对象 @@ -43,6 +43,11 @@ public class RfidReadRecord extends BaseEntity { */ private Long deviceId; + /** + * 设备编号(冗余存储,避免高频连表查询) + */ + private String deviceCode; + /** * 读取状态(1-成功;0-失败) */ diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/bo/RfidReadRecordBo.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/bo/RfidReadRecordBo.java index f2b8bbf..b1c9dda 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/bo/RfidReadRecordBo.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/bo/RfidReadRecordBo.java @@ -37,6 +37,11 @@ public class RfidReadRecordBo extends BaseEntity { @NotNull(message = "设备id不能为空", groups = { AddGroup.class, EditGroup.class }) private Long deviceId; + /** + * 设备编号(查询过滤用,冗余存储字段) + */ + private String deviceCode; + /** * 读取状态(1-成功;0-失败) */ diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/vo/RfidReadRecordVo.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/vo/RfidReadRecordVo.java index 1cce1fa..d0d34b5 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/vo/RfidReadRecordVo.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/domain/vo/RfidReadRecordVo.java @@ -12,9 +12,6 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.util.Date; - - /** * 读取记录视图对象 rfid_read_record diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/helper/RfidReadRecordTableHelper.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/helper/RfidReadRecordTableHelper.java index 594bfd3..aba7a8b 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/helper/RfidReadRecordTableHelper.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/helper/RfidReadRecordTableHelper.java @@ -1,7 +1,6 @@ package org.dromara.rfid.helper; import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.utils.SpringUtils; @@ -48,6 +47,14 @@ public class RfidReadRecordTableHelper { */ private static final Map TABLE_EXISTS_CACHE = new ConcurrentHashMap<>(64); + /** + * 表存在性检查锁 + *

+ * 缓存失效的瞬间可能有多个看板请求并发进来,按表名加锁可以避免同时打 information_schema。 + *

+ */ + private static final Map TABLE_CHECK_LOCKS = new ConcurrentHashMap<>(64); + /** * 缓存刷新时间戳(用于定期刷新缓存) */ @@ -72,6 +79,7 @@ public class RfidReadRecordTableHelper { */ public static void clearTableExistsCache() { TABLE_EXISTS_CACHE.clear(); + TABLE_CHECK_LOCKS.clear(); lastCacheRefreshTime = System.currentTimeMillis(); log.debug("分表缓存已清除"); } @@ -156,19 +164,6 @@ public class RfidReadRecordTableHelper { return tableNames; } - /** - * 从表名解析日期后缀 - * - * @param tableName 表名 - * @return 日期字符串,如 20251126 - */ - public static String parseDateSuffix(String tableName) { - if (StrUtil.isBlank(tableName) || !tableName.startsWith(BASE_TABLE_NAME + "_")) { - return null; - } - return tableName.substring((BASE_TABLE_NAME + "_").length()); - } - /** * 检查表是否存在(带缓存) *

@@ -192,15 +187,21 @@ public class RfidReadRecordTableHelper { return cached; } - // 查询数据库 - boolean exists = doCheckTableExists(tableName); + Object tableLock = TABLE_CHECK_LOCKS.computeIfAbsent(tableName, key -> new Object()); + synchronized (tableLock) { + // 获取锁后再次读取缓存,避免并发请求重复查询数据库元数据。 + cached = TABLE_EXISTS_CACHE.get(tableName); + if (cached != null) { + return cached; + } - // 只缓存存在的表(不存在的表可能后续会创建) - if (exists) { - TABLE_EXISTS_CACHE.put(tableName, Boolean.TRUE); + boolean exists = doCheckTableExists(tableName); + // 只缓存存在的表(不存在的表可能后续会创建) + if (exists) { + TABLE_EXISTS_CACHE.put(tableName, Boolean.TRUE); + } + return exists; } - - return exists; } /** diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/mapper/RfidDeviceMapper.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/mapper/RfidDeviceMapper.java index c61ce52..023c222 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/mapper/RfidDeviceMapper.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/mapper/RfidDeviceMapper.java @@ -1,7 +1,9 @@ package org.dromara.rfid.mapper; import org.dromara.rfid.domain.RfidDevice; +import org.dromara.rfid.domain.vo.DashboardVO; import org.dromara.rfid.domain.vo.RfidDeviceVo; +import org.dromara.rfid.domain.vo.RfidDashboardStatsVo; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -50,6 +52,20 @@ public interface RfidDeviceMapper extends BaseMapperPlus queryWrapper); + /** + * 统计已标识设备的看板概览数据 + * + * @return 已标识设备统计概览 + */ + DashboardVO.StatisticsOverview selectMarkedDeviceStatistics(); + + /** + * 统计首页设备状态数据 + * + * @return 首页统计数据 + */ + RfidDashboardStatsVo selectDashboardStats(); + /** * 分页查询设备信息(自定义条件) * diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/DashboardServiceImpl.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/DashboardServiceImpl.java index f6b1794..878d467 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/DashboardServiceImpl.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/DashboardServiceImpl.java @@ -71,32 +71,8 @@ public class DashboardServiceImpl implements IDashboardService { */ @Override public DashboardVO.StatisticsOverview getOverview() { - DashboardVO.StatisticsOverview overview = new DashboardVO.StatisticsOverview(); - - // 统计设备总数 - Long deviceTotal = deviceMapper.selectCount(Wrappers.lambdaQuery(RfidDevice.class) - .eq(RfidDevice::getIsMarked, "1")); - overview.setDeviceTotal(deviceTotal); - - // 统计在线数量 - Long onlineCount = deviceMapper.selectCount(Wrappers.lambdaQuery(RfidDevice.class) - .eq(RfidDevice::getIsMarked, "1") - .eq(RfidDevice::getOnlineStatus, "1")); - overview.setOnlineCount(onlineCount); - - // 统计离线数量 - Long offlineCount = deviceMapper.selectCount(Wrappers.lambdaQuery(RfidDevice.class) - .eq(RfidDevice::getIsMarked, "1") - .eq(RfidDevice::getOnlineStatus, "0")); - overview.setOfflineCount(offlineCount); - - // 统计告警数量(当天告警设备数) - Long alarmCount = deviceMapper.selectCount(Wrappers.lambdaQuery(RfidDevice.class) - .eq(RfidDevice::getIsMarked, "1") - .eq(RfidDevice::getAlarmStatus, "1")); - overview.setAlarmCount(alarmCount); - - return overview; + // 看板按秒级刷新,统计口径固定为“已标识设备”,一次聚合可减少数据库重复扫描。 + return deviceMapper.selectMarkedDeviceStatistics(); } /** diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidDeviceServiceImpl.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidDeviceServiceImpl.java index 5cccf3a..4f70547 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidDeviceServiceImpl.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidDeviceServiceImpl.java @@ -6,7 +6,9 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -23,7 +25,6 @@ import org.dromara.rfid.helper.RfidReadRecordTableHelper; import java.util.Date; import java.util.List; -import java.util.Map; import java.util.Collection; /** @@ -61,7 +62,7 @@ public class RfidDeviceServiceImpl implements IRfidDeviceService { */ @Override public TableDataInfo queryPageList(RfidDeviceBo bo, PageQuery pageQuery) { - LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Wrapper lqw = buildQueryWrapper(bo); // 使用自定义 Mapper XML + MyBatis-Plus Wrapper 进行分页查询 Page result = baseMapper.selectCustomRfidDeviceVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); @@ -75,7 +76,7 @@ public class RfidDeviceServiceImpl implements IRfidDeviceService { */ @Override public List queryList(RfidDeviceBo bo) { - LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Wrapper lqw = buildQueryWrapper(bo); // 使用自定义 Mapper XML + MyBatis-Plus Wrapper 查询列表 return baseMapper.selectCustomRfidDeviceVoList(lqw); } @@ -94,28 +95,50 @@ public class RfidDeviceServiceImpl implements IRfidDeviceService { return baseMapper.countCustomRfidDevice(wrapper); } - private LambdaQueryWrapper buildQueryWrapper(RfidDeviceBo bo) { - Map params = bo.getParams(); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.orderByAsc(RfidDevice::getId); - lqw.eq(StringUtils.isNotBlank(bo.getDeviceCode()), RfidDevice::getDeviceCode, bo.getDeviceCode()); - lqw.like(StringUtils.isNotBlank(bo.getDeviceName()), RfidDevice::getDeviceName, bo.getDeviceName()); - lqw.eq(bo.getLocationId() != null, RfidDevice::getLocationId, bo.getLocationId()); - lqw.eq(StringUtils.isNotBlank(bo.getDeviceAddress()), RfidDevice::getDeviceAddress, bo.getDeviceAddress()); - lqw.eq(bo.getDevicePort() != null, RfidDevice::getDevicePort, bo.getDevicePort()); - lqw.eq(bo.getReadFrequency() != null, RfidDevice::getReadFrequency, bo.getReadFrequency()); - lqw.eq(StringUtils.isNotBlank(bo.getOnlineStatus()), RfidDevice::getOnlineStatus, bo.getOnlineStatus()); - lqw.eq(StringUtils.isNotBlank(bo.getAlarmStatus()), RfidDevice::getAlarmStatus, bo.getAlarmStatus()); - if (StringUtils.isNotBlank(bo.getIsMarked())) { - lqw.apply("t.is_marked = {0}", bo.getIsMarked()); - } - lqw.eq(StringUtils.isNotBlank(bo.getCreatedBy()), RfidDevice::getCreatedBy, bo.getCreatedBy()); - lqw.eq(bo.getCreatedAt() != null, RfidDevice::getCreatedAt, bo.getCreatedAt()); - lqw.eq(StringUtils.isNotBlank(bo.getUpdatedBy()), RfidDevice::getUpdatedBy, bo.getUpdatedBy()); - lqw.eq(bo.getUpdatedAt() != null, RfidDevice::getUpdatedAt, bo.getUpdatedAt()); + private Wrapper buildQueryWrapper(RfidDeviceBo bo) { + QueryWrapper lqw = Wrappers.query(); + lqw.orderByAsc("t.id"); + lqw.eq(StringUtils.isNotBlank(bo.getDeviceCode()), "t.device_code", bo.getDeviceCode()); + lqw.like(StringUtils.isNotBlank(bo.getDeviceName()), "t.device_name", bo.getDeviceName()); + appendLocationScopeCondition(lqw, bo.getLocationId()); + lqw.eq(StringUtils.isNotBlank(bo.getDeviceAddress()), "t.device_address", bo.getDeviceAddress()); + lqw.eq(bo.getDevicePort() != null, "t.device_port", bo.getDevicePort()); + lqw.eq(bo.getReadFrequency() != null, "t.read_frequency", bo.getReadFrequency()); + lqw.eq(StringUtils.isNotBlank(bo.getOnlineStatus()), "t.online_status", bo.getOnlineStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getAlarmStatus()), "t.alarm_status", bo.getAlarmStatus()); + // 设备表与位置表都有 is_marked 字段,显式指定 t 别名,避免连表查询出现歧义。 + lqw.eq(StringUtils.isNotBlank(bo.getIsMarked()), "t.is_marked", bo.getIsMarked()); + lqw.eq(StringUtils.isNotBlank(bo.getCreatedBy()), "t.created_by", bo.getCreatedBy()); + lqw.eq(bo.getCreatedAt() != null, "t.created_at", bo.getCreatedAt()); + lqw.eq(StringUtils.isNotBlank(bo.getUpdatedBy()), "t.updated_by", bo.getUpdatedBy()); + lqw.eq(bo.getUpdatedAt() != null, "t.updated_at", bo.getUpdatedAt()); return lqw; } + /** + * 追加位置范围筛选。 + *

+ * 设备只绑定到 3-工位;左侧树点到 1-车间或 2-工序时,需要展开到其所有叶子工位, + * 避免按父节点 id 精确匹配导致列表为空。 + */ + private void appendLocationScopeCondition(QueryWrapper lqw, Long locationId) { + if (locationId == null) { + return; + } + lqw.and(wrapper -> wrapper + .eq("t.location_id", locationId) + .or() + .apply(""" + exists ( + select 1 + from rfid_location leaf + where leaf.id = t.location_id + and leaf.location_type = '3' + and find_in_set({0}, leaf.ancestors) > 0 + ) + """, locationId)); + } + /** * 新增设备信息 * @@ -153,14 +176,14 @@ public class RfidDeviceServiceImpl implements IRfidDeviceService { */ private void validEntityBeforeSave(RfidDevice entity){ // 业务编号 deviceCode 唯一校验 - // if (StringUtils.isNotBlank(entity.getDeviceCode())) { - // boolean exists = baseMapper.existsRfidDevice(Wrappers.lambdaQuery() - // .eq(RfidDevice::getDeviceCode, entity.getDeviceCode()) - // .ne(entity.getId() != null, RfidDevice::getId, entity.getId())); - // if (exists) { - // throw new ServiceException("设备编号已存在"); - // } - // } + if (StringUtils.isNotBlank(entity.getDeviceCode())) { + boolean exists = baseMapper.existsRfidDevice(Wrappers.lambdaQuery() + .eq(RfidDevice::getDeviceCode, entity.getDeviceCode()) + .ne(entity.getId() != null, RfidDevice::getId, entity.getId())); + if (exists) { + throw new ServiceException("设备编号已存在"); + } + } // IP地址 deviceAddress 唯一校验 if (StringUtils.isNotBlank(entity.getDeviceAddress())) { diff --git a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidReadRecordServiceImpl.java b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidReadRecordServiceImpl.java index 8d6f46e..9ed8187 100644 --- a/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidReadRecordServiceImpl.java +++ b/ruoyi-modules/hw-rfid/src/main/java/org/dromara/rfid/service/impl/RfidReadRecordServiceImpl.java @@ -21,6 +21,8 @@ import org.dromara.rfid.domain.RfidReadRecord; import org.dromara.rfid.mapper.RfidReadRecordMapper; import org.dromara.rfid.service.IRfidReadRecordService; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.List; import java.util.Collection; @@ -48,6 +50,11 @@ public class RfidReadRecordServiceImpl implements IRfidReadRecordService { */ private static final Pattern BARCODE_CLEAN_PATTERN = Pattern.compile("[\\s\\x00-\\x1F\\x7F]+"); + /** + * 多表 UNION 查询最大日期跨度 + */ + private static final long MAX_QUERY_DAYS = 30L; + /** * 查询读取记录 * @@ -77,6 +84,8 @@ public class RfidReadRecordServiceImpl implements IRfidReadRecordService { */ @Override public TableDataInfo queryPageList(RfidReadRecordBo bo, PageQuery pageQuery) { + assertQueryDateRange(bo); + // 获取实际存在的分表列表 List tableNames = RfidReadRecordTableHelper.getExistingTableNames( bo.getBeginRecordTime(), bo.getEndRecordTime()); @@ -109,6 +118,8 @@ public class RfidReadRecordServiceImpl implements IRfidReadRecordService { */ @Override public List queryList(RfidReadRecordBo bo) { + assertQueryDateRange(bo); + // 获取实际存在的分表列表 List tableNames = RfidReadRecordTableHelper.getExistingTableNames( bo.getBeginRecordTime(), bo.getEndRecordTime()); @@ -171,6 +182,36 @@ public class RfidReadRecordServiceImpl implements IRfidReadRecordService { return lqw; } + /** + * 校验分表查询日期范围 + *

+ * 读取记录是高频数据,全量列表/导出接口必须限制跨表范围,避免一次请求扫描过多日分表。 + *

+ */ + private void assertQueryDateRange(RfidReadRecordBo bo) { + Date beginRecordTime = bo.getBeginRecordTime(); + Date endRecordTime = bo.getEndRecordTime(); + if (beginRecordTime == null && endRecordTime == null) { + return; + } + + LocalDate start = beginRecordTime != null + ? cn.hutool.core.date.DateUtil.toLocalDateTime(beginRecordTime).toLocalDate() + : LocalDate.now(); + LocalDate end = endRecordTime != null + ? cn.hutool.core.date.DateUtil.toLocalDateTime(endRecordTime).toLocalDate() + : LocalDate.now(); + if (start.isAfter(end)) { + LocalDate temp = start; + start = end; + end = temp; + } + long queryDays = ChronoUnit.DAYS.between(start, end) + 1; + if (queryDays > MAX_QUERY_DAYS) { + throw new ServiceException("读取记录查询范围不能超过" + MAX_QUERY_DAYS + "天"); + } + } + /** * 批量清理条码中的不可见字符 * @@ -204,20 +245,6 @@ public class RfidReadRecordServiceImpl implements IRfidReadRecordService { record.setBarcode(cleanedBarcode); } - /** - * 将字符串转换为十六进制表示,用于调试 - */ - private String toHexString(String str) { - if (str == null) { - return "null"; - } - StringBuilder sb = new StringBuilder(); - for (char c : str.toCharArray()) { - sb.append(String.format("%02X ", (int) c)); - } - return sb.toString().trim(); - } - /** * 新增读取记录 *