通讯录没起名时不再取对方设备的名字 (#3473)
* 别把对方给设备起的名字抄进自己的通讯录 通讯录里没起名的时候,以前会跑去设备表查目标设备的名字。那是设备主人在智控台上自己起的, 直接抄进调用方的通讯录,等于把别人的昵称漏了出去。现在改成一律用 MAC 尾号称呼, 只有目标是自己那台设备时,才用自己设备表里的名字。补了单测,覆盖对方已起名、目标是自己、 显式起名、目标没登记这几种情况。 * 取自己的设备名改用不会撞行的查法 设备表里同一个 MAC 可能有两行只差大小写,一次只准取一条的查法撞上就抛异常,保存通讯录整个接口带崩。改成按归属查的那一种,按 id 取第一条。 补齐自己那台设备在设备表里没起名时退回尾号称呼的用例,另有几条用例的名字跟实际测的东西对不上,一并改掉。
This commit is contained in:
2 files changed
+197
-7
No files matched your search
+25
-4
@@ -95,6 +95,10 @@ public class DeviceAddressBookServiceImpl implements DeviceAddressBookService {
|
||||
callerNickname = targetBook.get(bookKey(callerMac));
|
||||
}
|
||||
if (StringUtils.isBlank(callerNickname)) {
|
||||
// 主叫显示名取的是主叫方自己那台设备在设备表里的名字,不是对方的。
|
||||
// 对方那边给主叫方起的称呼在上面的 targetBook 里已经查过。
|
||||
// 产品上按「来电显示」的口径处理,所以这里不套通讯录那套「不抄对方私产」的限制
|
||||
// 这个接口没有登录用户上下文,归属校验那个查法在这里会返回空,只能用不校验归属的查法
|
||||
DeviceEntity callerDevice = deviceService.getDeviceByMacAddress(callerMac);
|
||||
if (callerDevice != null) {
|
||||
callerNickname = callerDevice.getAlias();
|
||||
@@ -212,11 +216,18 @@ public class DeviceAddressBookServiceImpl implements DeviceAddressBookService {
|
||||
DeviceAddressBookEntity entity = new DeviceAddressBookEntity();
|
||||
entity.setMacAddress(macAddress);
|
||||
entity.setTargetMac(targetMac);
|
||||
// 如果 alias 为空,就默认使用设备名称;对方设备尚未登记时退回尾号称呼
|
||||
// 调用方没起名就退回尾号称呼。对方设备在智控台上起的名字是对方的私产,
|
||||
// 抄进调用方的通讯录等于把别人的昵称漏出去,所以别人的设备一律不去设备表取名
|
||||
if (StringUtils.isBlank(alias)) {
|
||||
DeviceEntity targetDevice = deviceService.getDeviceByMacAddress(targetMac);
|
||||
if (targetDevice != null) {
|
||||
alias = targetDevice.getAlias();
|
||||
// 只有给自己那台设备起名时才用设备表里的名字,那本来就是调用方自己的
|
||||
if (isOwnDevice(macAddress, targetMac)) {
|
||||
// 用带归属校验的那个查法。设备表里同一个 MAC 可能有两行只差大小写,
|
||||
// 一次只准取一条的那种查法撞上这种数据会抛 TooManyResultsException,
|
||||
// 整个保存接口带崩;按 id 取第一条,撞行也不报错
|
||||
DeviceEntity ownDevice = deviceService.getOwnedDeviceByMacAddress(macAddress);
|
||||
if (ownDevice != null && StringUtils.isNotBlank(ownDevice.getAlias())) {
|
||||
alias = ownDevice.getAlias();
|
||||
}
|
||||
}
|
||||
// 设备登记了但用户没起过名,alias 建表就是 null,这是默认状态
|
||||
// 不是异常。这时候落个 null 进去,缓存刷新会把整条跳过,
|
||||
@@ -304,6 +315,16 @@ public class DeviceAddressBookServiceImpl implements DeviceAddressBookService {
|
||||
return macAddress == null ? null : macAddress.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 目标设备是不是调用方自己那台
|
||||
*
|
||||
* <p>
|
||||
* MAC 不区分大小写,比较前先按通讯录的键归一化,跟别处的比较口径保持一致
|
||||
*/
|
||||
private static boolean isOwnDevice(String macAddress, String targetMac) {
|
||||
return bookKey(macAddress) != null && bookKey(macAddress).equals(bookKey(targetMac));
|
||||
}
|
||||
|
||||
private String formatMacAsDeviceName(String mac) {
|
||||
if (StringUtils.isBlank(mac) || mac.length() < 2) {
|
||||
return mac;
|
||||
|
||||
+172
-3
@@ -1,5 +1,6 @@
|
||||
package xiaozhi.modules.device.service.impl;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -21,6 +22,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.ibatis.exceptions.TooManyResultsException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.MockedStatic;
|
||||
@@ -75,7 +77,7 @@ class DeviceAddressBookServiceImplTest {
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
when(deviceService.getDeviceByMacAddress(OTHER_MAC)).thenReturn(null);
|
||||
when(deviceService.getOwnedDeviceByMacAddress(anyString())).thenReturn(null);
|
||||
|
||||
service.saveOrUpdate(MAC, OTHER_MAC, null, true);
|
||||
|
||||
@@ -85,10 +87,11 @@ class DeviceAddressBookServiceImplTest {
|
||||
assertNotNull(entity.getAlias());
|
||||
assertEquals("尾号为66的设备", entity.getAlias());
|
||||
assertTrue(entity.getHasPermission());
|
||||
verify(deviceService, never()).getOwnedDeviceByMacAddress(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void blankAliasOnRegisteredTargetWithoutNicknameFallsBackToMacSuffix() {
|
||||
void blankAliasOnAnotherDeviceFallsBackToMacSuffixWithoutAskingTheDeviceTable() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
@@ -101,6 +104,7 @@ class DeviceAddressBookServiceImplTest {
|
||||
registered.setMacAddress(OTHER_MAC);
|
||||
registered.setAlias(null);
|
||||
when(deviceService.getDeviceByMacAddress(OTHER_MAC)).thenReturn(registered);
|
||||
when(deviceService.getOwnedDeviceByMacAddress(OTHER_MAC)).thenReturn(registered);
|
||||
|
||||
service.saveOrUpdate(MAC, OTHER_MAC, null, true);
|
||||
|
||||
@@ -108,6 +112,171 @@ class DeviceAddressBookServiceImplTest {
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("尾号为66的设备", entityCaptor.getValue().getAlias(),
|
||||
"登记了但没起名的设备落个空名,缓存刷新会跳过这条,对方永远呼不过来");
|
||||
verify(deviceService, never()).getDeviceByMacAddress(anyString());
|
||||
verify(deviceService, never()).getOwnedDeviceByMacAddress(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void blankAliasOnRegisteredTargetDoesNotTakeOverTheirNickname() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
DeviceEntity theirs = new DeviceEntity();
|
||||
theirs.setMacAddress(OTHER_MAC);
|
||||
theirs.setAlias("老王的音响");
|
||||
when(deviceService.getDeviceByMacAddress(OTHER_MAC)).thenReturn(theirs);
|
||||
when(deviceService.getOwnedDeviceByMacAddress(OTHER_MAC)).thenReturn(theirs);
|
||||
|
||||
service.saveOrUpdate(MAC, OTHER_MAC, null, true);
|
||||
|
||||
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("尾号为66的设备", entityCaptor.getValue().getAlias(),
|
||||
"对方在智控台上给这台设备起的名字被抄进了调用方的通讯录");
|
||||
verify(deviceService, never()).getDeviceByMacAddress(anyString());
|
||||
verify(deviceService, never()).getOwnedDeviceByMacAddress(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void blankAliasOnOwnDeviceUsesTheNameFromItsOwnDeviceRow() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
DeviceEntity mine = new DeviceEntity();
|
||||
mine.setMacAddress(MAC);
|
||||
mine.setAlias("我的书房");
|
||||
when(deviceService.getOwnedDeviceByMacAddress(MAC)).thenReturn(mine);
|
||||
|
||||
service.saveOrUpdate(MAC, MAC, null, true);
|
||||
|
||||
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("我的书房", entityCaptor.getValue().getAlias(),
|
||||
"给自己那台设备起名,用的就是自己设备表里的名字");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ownDeviceWithoutAliasInDeviceTableFallsBackToMacSuffix() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
DeviceEntity mine = new DeviceEntity();
|
||||
mine.setMacAddress(MAC);
|
||||
mine.setAlias(null);
|
||||
when(deviceService.getOwnedDeviceByMacAddress(MAC)).thenReturn(mine);
|
||||
|
||||
service.saveOrUpdate(MAC, MAC, null, true);
|
||||
|
||||
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("尾号为55的设备", entityCaptor.getValue().getAlias(),
|
||||
"自己的设备在设备表里也没起名,落个空名的话缓存刷新会跳过这条");
|
||||
}
|
||||
|
||||
@Test
|
||||
void duplicateDeviceRowsDifferingOnlyInCaseDoNotBreakNaming() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
// 设备表里同一个 MAC 有两行只差大小写,一次只准取一条的那种查法会直接抛异常
|
||||
when(deviceService.getDeviceByMacAddress(anyString()))
|
||||
.thenThrow(new TooManyResultsException("selectOne 撞上两行只差大小写的数据"));
|
||||
DeviceEntity first = new DeviceEntity();
|
||||
first.setMacAddress(MAC);
|
||||
first.setAlias("我的书房");
|
||||
when(deviceService.getOwnedDeviceByMacAddress(MAC)).thenReturn(first);
|
||||
|
||||
assertDoesNotThrow(() -> service.saveOrUpdate(MAC, MAC, null, true),
|
||||
"同 MAC 有两行只差大小写时,取名不能把整个接口带崩");
|
||||
|
||||
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("我的书房", entityCaptor.getValue().getAlias(),
|
||||
"撞行时按 id 取第一条那条的名字");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ownDeviceIsRecognizedWhateverCaseTheAddressIsWrittenIn() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
DeviceEntity mine = new DeviceEntity();
|
||||
mine.setMacAddress(HEX_MAC);
|
||||
mine.setAlias("我的书房");
|
||||
when(deviceService.getOwnedDeviceByMacAddress(HEX_MAC)).thenReturn(mine);
|
||||
|
||||
service.saveOrUpdate(HEX_MAC, HEX_MAC.toUpperCase(), null, true);
|
||||
|
||||
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("我的书房", entityCaptor.getValue().getAlias(),
|
||||
"同一个地址写成大小写两种样子,就被当成别人的设备了");
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitAliasIsKeptAsIs() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
|
||||
service.saveOrUpdate(MAC, OTHER_MAC, "客厅", true);
|
||||
|
||||
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("客厅", entityCaptor.getValue().getAlias(),
|
||||
"自己给别人的设备起名是通讯录的本意,不能被兜底逻辑改掉");
|
||||
verify(deviceService, never()).getDeviceByMacAddress(anyString());
|
||||
verify(deviceService, never()).getOwnedDeviceByMacAddress(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void blankAliasOnUnregisteredTargetDoesNotBlowUp() {
|
||||
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
when(deviceService.getOwnedDeviceByMacAddress(anyString())).thenReturn(null);
|
||||
|
||||
assertDoesNotThrow(() -> service.saveOrUpdate(MAC, OTHER_MAC, null, true),
|
||||
"目标设备没登记时接口得正常返回,不能抛异常");
|
||||
|
||||
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||
assertEquals("尾号为66的设备", entityCaptor.getValue().getAlias());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -325,7 +494,7 @@ class DeviceAddressBookServiceImplTest {
|
||||
DeviceService deviceService = mock(DeviceService.class);
|
||||
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||
when(deviceService.getDeviceByMacAddress(anyString())).thenReturn(null);
|
||||
when(deviceService.getOwnedDeviceByMacAddress(anyString())).thenReturn(null);
|
||||
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||
addressBookDao, redisUtils, deviceService, mock(SysParamsService.class));
|
||||
|
||||
|
||||
Reference in new issue
Block a user