From 7233f13598af4994ec549ed7b727dbdd1ca1f986 Mon Sep 17 00:00:00 2001 From: chenhao Date: Wed, 17 Jun 2026 15:16:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=E4=BC=98=E5=8C=96=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=A0=B7=E5=BC=8F=E5=92=8C=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20Redis=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 `devices/index.less` 中注释掉不必要的 CSS 规则 - 更新 `AndroidAuthServiceImpl` 和 `AndroidDeviceRegistrationServiceImpl` 中的异常信息和方法简化 - 在 `MeetingCreateDrawer.tsx` 中启用文本精炼功能 - 在 `devices/index.tsx` 中使用通用成功消息 - 在 `DeviceOnlineManagementServiceImpl` 中添加对终端类型的映射 - 更新 `ClientManagement.tsx` 中的平台类型选项 - 在 `MeetingPointsManagement.tsx` 中注释掉当前可用额度显示 - 在 `scan-confirm/index.tsx` 中更新登录确认消息 - 更新 `RedisSupport` 以使用 Lettuce 库并调整相关方法 --- .../android/impl/AndroidAuthServiceImpl.java | 2 +- .../AndroidDeviceRegistrationServiceImpl.java | 3 +- .../DeviceOnlineManagementServiceImpl.java | 23 ++++++++- .../com/imeeting/support/RedisSupport.java | 49 ++++++++++++------- .../business/MeetingCreateDrawer.tsx | 4 +- .../src/pages/business/ClientManagement.tsx | 48 +++++++++++++++--- .../business/MeetingPointsManagement.tsx | 40 +++++++-------- frontend/src/pages/devices/index.less | 8 +-- frontend/src/pages/devices/index.tsx | 4 +- imeeting-h5/src/pages/scan-confirm/index.tsx | 2 +- 10 files changed, 125 insertions(+), 58 deletions(-) diff --git a/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java b/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java index a0ea0a1..48e3843 100644 --- a/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/android/impl/AndroidAuthServiceImpl.java @@ -272,7 +272,7 @@ public class AndroidAuthServiceImpl implements AndroidAuthService { private DeviceInfoEntity requireRegisteredDevice(String deviceId) { DeviceInfoEntity device = findDevice(deviceId); if (device == null) { - throw new RuntimeException("设备未注册,请先调用设备注册接口"); + throw new RuntimeException("设备未注册"); } return device; } diff --git a/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceRegistrationServiceImpl.java b/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceRegistrationServiceImpl.java index 1c391dd..0121604 100644 --- a/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceRegistrationServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/android/impl/AndroidDeviceRegistrationServiceImpl.java @@ -94,7 +94,6 @@ public class AndroidDeviceRegistrationServiceImpl implements AndroidDeviceRegist } private String normalizeTerminalType(String value) { - String normalized = normalize(value); - return normalized == null ? null : normalized.toLowerCase(); + return normalize(value); } } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/DeviceOnlineManagementServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/DeviceOnlineManagementServiceImpl.java index 41a131f..195331c 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/DeviceOnlineManagementServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/DeviceOnlineManagementServiceImpl.java @@ -1,5 +1,7 @@ package com.imeeting.service.biz.impl; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.ListUtil; import com.imeeting.dto.android.AndroidAuthContext; import com.imeeting.dto.android.AndroidDeviceSessionState; import com.imeeting.dto.biz.DeviceAdminUpdateCommand; @@ -11,7 +13,9 @@ import com.imeeting.service.android.AndroidDeviceSessionService; import com.imeeting.service.android.AndroidGatewayPushService; import com.imeeting.service.biz.DeviceOnlineManagementService; import com.imeeting.service.biz.LicenseService; +import com.unisbase.dto.SysDictItemDTO; import com.unisbase.security.LoginUser; +import com.unisbase.service.SysDictItemService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,7 +24,10 @@ import org.springframework.util.StringUtils; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -31,8 +38,9 @@ public class DeviceOnlineManagementServiceImpl implements DeviceOnlineManagement private final AndroidGatewayPushService androidGatewayPushService; private final AndroidDeviceBindingService androidDeviceBindingService; private final LicenseService licenseService; + private final SysDictItemService sysDictItemService; - @Override + @Override public void recordConnected(AndroidAuthContext authContext) { if (authContext == null || !StringUtils.hasText(authContext.getDeviceId())) { return; @@ -82,8 +90,19 @@ public class DeviceOnlineManagementServiceImpl implements DeviceOnlineManagement @Override public List listForAdmin(LoginUser loginUser) { List devices = deviceInfoMapper.selectAdminList(loginUser == null ? null : loginUser.getTenantId(), isPlatformAdmin(loginUser)); - for (DeviceOnlineAdminVO device : devices) { + List clientPlatform = sysDictItemService.getItemsByTypeCode("client_platform"); + Map typeMap = new HashMap<>(); + for (SysDictItemDTO sysDictItemDTO : clientPlatform) { + List itemsByTypeCode = sysDictItemService.getItemsByTypeCode(sysDictItemDTO.getItemValue()); + if (CollectionUtil.isNotEmpty(itemsByTypeCode)) { + typeMap.putAll(itemsByTypeCode.stream().collect(Collectors.toMap(SysDictItemDTO::getItemValue, SysDictItemDTO::getItemLabel))); + } + } + + + for (DeviceOnlineAdminVO device : devices) { AndroidDeviceSessionState state = androidDeviceSessionService.getByDeviceId(device.getDeviceCode()); + device.setTerminalType(typeMap.getOrDefault(device.getTerminalType(), device.getTerminalType())); if (state != null) { device.setOnline(true); device.setLastOnlineAt(toLocalDateTime(state.getLastSeenAt())); diff --git a/backend/src/main/java/com/imeeting/support/RedisSupport.java b/backend/src/main/java/com/imeeting/support/RedisSupport.java index aff047b..7393bea 100644 --- a/backend/src/main/java/com/imeeting/support/RedisSupport.java +++ b/backend/src/main/java/com/imeeting/support/RedisSupport.java @@ -1,9 +1,11 @@ package com.imeeting.support; import com.fasterxml.jackson.databind.ObjectMapper; +import io.lettuce.core.SetArgs; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.sync.RedisCommands; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.time.Duration; @@ -14,12 +16,12 @@ import java.util.Collection; @RequiredArgsConstructor public class RedisSupport { - private final StringRedisTemplate redisTemplate; + private final StatefulRedisConnection redisConnection; private final ObjectMapper objectMapper; public String getStringQuietly(String key) { try { - return redisTemplate.opsForValue().get(key); + return commands().get(key); } catch (Exception ex) { log.warn("读取 Redis 字符串失败, key={}", key, ex); return null; @@ -41,7 +43,7 @@ public class RedisSupport { public void setString(String key, String value) { try { - redisTemplate.opsForValue().set(key, value); + commands().set(key, value); } catch (Exception ex) { throw new RuntimeException("写入 Redis 字符串失败, key=" + key, ex); } @@ -49,7 +51,7 @@ public class RedisSupport { public void setString(String key, String value, Duration ttl) { try { - redisTemplate.opsForValue().set(key, value, ttl); + commands().psetex(key, ttl.toMillis(), value); } catch (Exception ex) { throw new RuntimeException("写入 Redis 字符串失败, key=" + key, ex); } @@ -65,8 +67,8 @@ public class RedisSupport { public boolean setIfAbsentQuietly(String key, String value, Duration ttl) { try { - Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, ttl); - return Boolean.TRUE.equals(success); + String result = commands().set(key, value, buildNxPxArgs(ttl)); + return isOk(result); } catch (Exception ex) { log.warn("写入 Redis 锁失败, key={}", key, ex); return false; @@ -75,8 +77,8 @@ public class RedisSupport { public boolean setIfAbsentOrThrow(String key, String value, Duration ttl) { try { - Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, ttl); - return Boolean.TRUE.equals(success); + String result = commands().set(key, value, buildNxPxArgs(ttl)); + return isOk(result); } catch (Exception ex) { throw new RuntimeException("写入 Redis 锁失败, key=" + key, ex); } @@ -84,15 +86,18 @@ public class RedisSupport { public void deleteQuietly(String key) { try { - redisTemplate.delete(key); + commands().del(key); } catch (Exception ex) { log.warn("删除 Redis Key 失败, key={}", key, ex); } } public void deleteQuietly(Collection keys) { + if (keys == null || keys.isEmpty()) { + return; + } try { - redisTemplate.delete(keys); + commands().del(keys.toArray(String[]::new)); } catch (Exception ex) { log.warn("批量删除 Redis Key 失败, keys={}", keys, ex); } @@ -103,7 +108,7 @@ public class RedisSupport { return; } try { - redisTemplate.opsForSet().remove(key, (Object[]) members); + commands().srem(key, members); } catch (Exception ex) { log.warn("从 Redis Set 删除成员失败, key={}", key, ex); } @@ -114,8 +119,7 @@ public class RedisSupport { return false; } try { - Long added = redisTemplate.opsForSet().add(key, member); - return added != null && added > 0; + return commands().sadd(key, member) > 0; } catch (Exception ex) { log.warn("add Redis set member failed, key={}", key, ex); return false; @@ -127,8 +131,7 @@ public class RedisSupport { return false; } try { - Boolean memberPresent = redisTemplate.opsForSet().isMember(key, member); - return Boolean.TRUE.equals(memberPresent); + return Boolean.TRUE.equals(commands().sismember(key, member)); } catch (Exception ex) { log.warn("check Redis set member failed, key={}", key, ex); return false; @@ -137,7 +140,7 @@ public class RedisSupport { public long getSetSizeQuietly(String key) { try { - Long size = redisTemplate.opsForSet().size(key); + Long size = commands().scard(key); return size == null ? 0L : size; } catch (Exception ex) { log.warn("read Redis set size failed, key={}", key, ex); @@ -145,6 +148,18 @@ public class RedisSupport { } } + private RedisCommands commands() { + return redisConnection.sync(); + } + + private SetArgs buildNxPxArgs(Duration ttl) { + return SetArgs.Builder.nx().px(ttl.toMillis()); + } + + private boolean isOk(String result) { + return "OK".equalsIgnoreCase(result); + } + private String writeJson(Object value) { try { return objectMapper.writeValueAsString(value); diff --git a/frontend/src/components/business/MeetingCreateDrawer.tsx b/frontend/src/components/business/MeetingCreateDrawer.tsx index 9d452ce..2f4121c 100644 --- a/frontend/src/components/business/MeetingCreateDrawer.tsx +++ b/frontend/src/components/business/MeetingCreateDrawer.tsx @@ -217,7 +217,7 @@ export const MeetingCreateDrawer: React.FC = ({ hotWordGroupId: defaultPrompt?.hotWordGroupId ?? 0, summaryDetailLevel: "STANDARD", useSpkId: 1, - enableTextRefine: false, + enableTextRefine: true, mode: "2pass", language: "auto", enablePunctuation: true, @@ -651,4 +651,4 @@ export const MeetingCreateDrawer: React.FC = ({ )} ); -}; \ No newline at end of file +}; diff --git a/frontend/src/pages/business/ClientManagement.tsx b/frontend/src/pages/business/ClientManagement.tsx index 03752f0..8ea374f 100644 --- a/frontend/src/pages/business/ClientManagement.tsx +++ b/frontend/src/pages/business/ClientManagement.tsx @@ -1,4 +1,23 @@ -import { App, Button, Card, Col, Drawer, Empty, Form, Input, InputNumber, Popconfirm, Row, Select, Space, Switch, Table, Tabs, Tag, Typography, Upload } from "antd"; +import { + App, + Button, + Card, + Col, + Drawer, + Empty, + Form, + Input, + InputNumber, + Popconfirm, + Row, + Select, + Space, + Switch, + Table, + Tag, + Typography, + Upload +} from "antd"; import type { ColumnsType } from "antd/es/table"; import { CheckCircleOutlined, CloudUploadOutlined, DeleteOutlined, DownloadOutlined, EditOutlined, LaptopOutlined, MobileOutlined, PlusOutlined, ReloadOutlined, RocketOutlined, SearchOutlined, UploadOutlined, WindowsOutlined } from "@ant-design/icons"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -155,6 +174,13 @@ export default function ClientManagement() { () => Object.fromEntries(platformGroups.flatMap((group) => group.options.map((option) => [option.value, option]))) as Record, [platformGroups] ); + const platformTypeOptions = useMemo( + () => [{label: "全部类型", value: "all"}, ...platformGroups.map((group) => ({ + label: group.label, + value: group.key + }))], + [platformGroups] + ); const loadData = useCallback(async () => { setLoading(true); @@ -447,12 +473,20 @@ export default function ClientManagement() { - - - } allowClear style={{ width: 320 }} value={searchValue} onChange={(event) => setSearchValue(event.target.value)} /> - } allowClear + style={{width: 320}} value={searchValue} onChange={(event) => setSearchValue(event.target.value)}/> + diff --git a/frontend/src/pages/business/MeetingPointsManagement.tsx b/frontend/src/pages/business/MeetingPointsManagement.tsx index 0484405..7a4fa3d 100644 --- a/frontend/src/pages/business/MeetingPointsManagement.tsx +++ b/frontend/src/pages/business/MeetingPointsManagement.tsx @@ -101,12 +101,12 @@ function buildSummaryCards(overview: MeetingPointsOverviewVO | null) { value: number | string; note: string; }> = [ - { - key: "available-balance", - title: "当前可用额度", - value: isUnlimitedBalanceMode ? "无限" : (overview.totalAvailableBalance ?? 0), - note: isUnlimitedBalanceMode ? "关闭余额校验后只记录消耗和流水" : "按当前账户模式计算的可用额度", - }, + // { + // key: "available-balance", + // title: "当前可用额度", + // value: isUnlimitedBalanceMode ? "无限" : (overview.totalAvailableBalance ?? 0), + // note: isUnlimitedBalanceMode ? "关闭余额校验后只记录消耗和流水" : "按当前账户模式计算的可用额度", + // }, { key: "charge-count", title: "累计消耗次数", @@ -493,20 +493,20 @@ export default function MeetingPointsManagement() { 账户概览 - - - 模式:{getAccountModeLabel(overview?.accountMode)} - - - 优先级:{getChargePriorityLabel(overview?.chargePriority)} - - - {isUnlimitedBalanceMode ? "无限余额模式" : "校验余额模式"} - - - {isAdmin ? "管理员视角" : "当前用户视角"} - - + {/**/} + {/* */} + {/* 模式:{getAccountModeLabel(overview?.accountMode)}*/} + {/* */} + {/* */} + {/* 优先级:{getChargePriorityLabel(overview?.chargePriority)}*/} + {/* */} + {/* */} + {/* {isUnlimitedBalanceMode ? "无限余额模式" : "校验余额模式"}*/} + {/* */} + {/* */} + {/* {isAdmin ? "管理员视角" : "当前用户视角"}*/} + {/* */} + {/**/} diff --git a/frontend/src/pages/devices/index.less b/frontend/src/pages/devices/index.less index d659561..040c9c7 100644 --- a/frontend/src/pages/devices/index.less +++ b/frontend/src/pages/devices/index.less @@ -175,10 +175,10 @@ font-variant-numeric: tabular-nums; } -.app-page__table-wrap .ant-table-wrapper .ant-table-cell-fix-right-first, -.app-page__table-wrap .ant-table-wrapper .ant-table-cell-fix-right-last { - right: 0 !important; -} +//.app-page__table-wrap .ant-table-wrapper .ant-table-cell-fix-right-first, +//.app-page__table-wrap .ant-table-wrapper .ant-table-cell-fix-right-last { +// right: 0 !important; +//} @media (max-width: 768px) { .devices-page { diff --git a/frontend/src/pages/devices/index.tsx b/frontend/src/pages/devices/index.tsx index 174f887..bfc2bc6 100644 --- a/frontend/src/pages/devices/index.tsx +++ b/frontend/src/pages/devices/index.tsx @@ -117,13 +117,13 @@ export default function Devices() { const remove = async (record: DeviceInfo) => { await deleteManagedDevice(record.deviceId); - message.success(t("devicesExt.deleteSucceeded")); + message.success(t("common.success")); await loadData(); }; const resetStats = async (record: DeviceInfo) => { await resetManagedDeviceStats(record.deviceId); - message.success(t("devicesExt.resetStatsSucceeded")); + message.success(t("common.success")); await loadData(); }; diff --git a/imeeting-h5/src/pages/scan-confirm/index.tsx b/imeeting-h5/src/pages/scan-confirm/index.tsx index cb977ed..9077ee9 100644 --- a/imeeting-h5/src/pages/scan-confirm/index.tsx +++ b/imeeting-h5/src/pages/scan-confirm/index.tsx @@ -52,7 +52,7 @@ export default function ScanConfirmPage() { navigate("/meetings")}> 返回我的会议