From a7eb054c0338ef0fa1327e47e332d8ffe6d44bda Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Tue, 11 Nov 2025 09:55:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E6=88=90=E4=B8=BAi?= =?UTF-8?q?=E4=B8=BB=E6=92=AD=E8=AE=BE=E5=A4=87=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/api/RegisterDeviceClientApi.java | 9 ++++ .../inner/asserts/team/TeamErrorCode.java | 5 ++ .../user/ability/RegisterDeviceGateway.java | 16 ++++++ .../common/team/decay/TeamMemberAddDecay.java | 7 +++ .../device/LatestMobileDeviceService.java | 9 ++++ .../impl/LatestMobileDeviceServiceImpl.java | 30 +++++++++++ .../user/RegisterDeviceGatewayImpl.java | 51 +++++++++++++++++++ .../device/RegisterDeviceClientEndpoint.java | 5 ++ .../device/RegisterDeviceClientService.java | 8 +++ .../impl/RegisterDeviceClientServiceImpl.java | 42 +++++++++++++++ 10 files changed, 182 insertions(+) diff --git a/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/user/device/api/RegisterDeviceClientApi.java b/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/user/device/api/RegisterDeviceClientApi.java index e666f1d8..34e67480 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/user/device/api/RegisterDeviceClientApi.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/user/device/api/RegisterDeviceClientApi.java @@ -148,4 +148,13 @@ public interface RegisterDeviceClientApi { */ @GetMapping("/user/device/fingerprint") ResultResponse getDeviceFingerprintByUserId(@RequestParam("userId") Long userId); + + /** + * 检查当前用户的设备指纹是否与其他主播用户共享 + * + * @param userId 用户ID + * @return true-存在共享设备的主播用户, false-不存在 + */ + @GetMapping("/user/device/fingerprint/check-anchor") + ResultResponse checkDeviceFingerprintHasAnchor(@RequestParam("userId") Long userId); } diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/team/TeamErrorCode.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/team/TeamErrorCode.java index 58fd5d27..7391d3fc 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/team/TeamErrorCode.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/team/TeamErrorCode.java @@ -227,6 +227,11 @@ public enum TeamErrorCode implements IResponseErrorCode { * 用户不是BD Leader */ USER_NOT_BDLEADER(6052, "User is not BD Leader"), + + /** + * 设备指纹已被其他主播使用 + */ + DEVICE_FINGERPRINT_USED_BY_ANCHOR(6053, "This device is already being used by another anchor"), ; private final Integer code; diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/user/ability/RegisterDeviceGateway.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/user/ability/RegisterDeviceGateway.java index f550f73b..37672d6c 100644 --- a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/user/ability/RegisterDeviceGateway.java +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/user/ability/RegisterDeviceGateway.java @@ -21,4 +21,20 @@ public interface RegisterDeviceGateway { * @return true 允许注册、false 不允许 */ boolean checkAllowDeviceRegister(AllowDeviceRegisterCmd cmd); + + /** + * 获取用户设备指纹 + * + * @param userId 用户ID + * @return 设备指纹字符串 + */ + String getDeviceFingerprintByUserId(Long userId); + + /** + * 检查当前用户的设备指纹是否与其他主播用户共享 + * + * @param userId 用户ID + * @return true-存在共享设备的主播用户, false-不存在 + */ + Boolean checkDeviceFingerprintHasAnchor(Long userId); } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/team/decay/TeamMemberAddDecay.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/team/decay/TeamMemberAddDecay.java index b30e2904..a2d79cbc 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/team/decay/TeamMemberAddDecay.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/team/decay/TeamMemberAddDecay.java @@ -3,6 +3,7 @@ package com.red.circle.other.infra.common.team.decay; import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.response.CommonErrorCode; import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway; import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamApplicationProcessApproval; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember; @@ -38,6 +39,7 @@ public class TeamMemberAddDecay { private final TeamProfileService teamProfileService; private final UserHistoryIdentityService historyIdentityService; private final TeamApplicationProcessApprovalService applicationProcessApprovalService; + private final RegisterDeviceGateway registerDeviceGateway; /** * 添加成员. @@ -53,6 +55,11 @@ public class TeamMemberAddDecay { ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT, Objects.equals(memberUser.getOriginSys(), teamProfile.getSysOrigin())); + // 主播设备指纹校验 + Boolean hasAnchorWithSameDevice = registerDeviceGateway.checkDeviceFingerprintHasAnchor( + param.getMemberUserId()); + ResponseAssert.isFalse(TeamErrorCode.DEVICE_FINGERPRINT_USED_BY_ANCHOR, hasAnchorWithSameDevice); + teamMemberService.addMember(new TeamMember() .setSortId(IdWorkerUtils.getId()) .setSysOrigin(teamProfile.getSysOrigin()) diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/LatestMobileDeviceService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/LatestMobileDeviceService.java index 363b4ac4..649ad0ab 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/LatestMobileDeviceService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/LatestMobileDeviceService.java @@ -116,4 +116,13 @@ public interface LatestMobileDeviceService extends BaseService listUserIdsByDeviceFingerprint(String fingerprint, String sysOrigin); + } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/impl/LatestMobileDeviceServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/impl/LatestMobileDeviceServiceImpl.java index affccabd..f29a6f0b 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/impl/LatestMobileDeviceServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/device/impl/LatestMobileDeviceServiceImpl.java @@ -311,4 +311,34 @@ public class LatestMobileDeviceServiceImpl extends return requestClient + "|" + phoneModel + "|" + ip; } + @Override + public List listUserIdsByDeviceFingerprint(String fingerprint, String sysOrigin) { + if (StringUtils.isBlank(fingerprint) || StringUtils.isBlank(sysOrigin)) { + return CollectionUtils.newArrayList(); + } + + String[] parts = fingerprint.split("\\|"); + if (parts.length != 3) { + return CollectionUtils.newArrayList(); + } + + String requestClient = parts[0]; + String phoneModel = parts[1]; + String ip = parts[2]; + + return Optional.ofNullable(query() + .select(LatestMobileDevice::getUserId) + .eq(LatestMobileDevice::getRequestClient, requestClient) + .eq(LatestMobileDevice::getPhoneModel, phoneModel) + .eq(LatestMobileDevice::getIp, ip) + .eq(LatestMobileDevice::getSysOrigin, sysOrigin) + .last(PageConstant.formatLimit(500)) + .list()) + .map(devices -> devices.stream() + .map(LatestMobileDevice::getUserId) + .distinct() + .collect(Collectors.toList())) + .orElseGet(CollectionUtils::newArrayList); + } + } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/RegisterDeviceGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/RegisterDeviceGatewayImpl.java index 162ca9cb..36cae06e 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/RegisterDeviceGatewayImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/RegisterDeviceGatewayImpl.java @@ -1,5 +1,9 @@ package com.red.circle.other.infra.gateway.user; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; +import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService; import com.red.circle.tool.core.text.StringUtils; import com.red.circle.tool.core.thread.ThreadPoolManager; import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway; @@ -21,6 +25,9 @@ public class RegisterDeviceGatewayImpl implements RegisterDeviceGateway { private final IpRegisterQuantityService ipRegisterQuantityService; private final DeviceRegisterQuantityService deviceRegisterQuantityService; + private final LatestMobileDeviceService latestMobileDeviceService; + private final UserProfileGateway userProfileGateway; + private final TeamMemberService teamMemberService; /** * 累计注册数量. @@ -66,4 +73,48 @@ public class RegisterDeviceGatewayImpl implements RegisterDeviceGateway { >= cmd.getDeviceLimit(); } + @Override + public String getDeviceFingerprintByUserId(Long userId) { + return latestMobileDeviceService.getDeviceFingerprintByUserId(userId); + } + + @Override + public Boolean checkDeviceFingerprintHasAnchor(Long userId) { + if (userId == null) { + return false; + } + + com.red.circle.other.domain.model.user.UserProfile userProfile = userProfileGateway.getByUserId(userId); + if (userProfile == null) { + return false; + } + + String fingerprint = latestMobileDeviceService.getDeviceFingerprintByUserId(userId); + if (StringUtils.isBlank(fingerprint)) { + return false; + } + + java.util.List userIds = latestMobileDeviceService.listUserIdsByDeviceFingerprint( + fingerprint, userProfile.getOriginSys()); + + if (userIds == null || userIds.isEmpty()) { + return false; + } + + for (Long otherUserId : userIds) { + if (java.util.Objects.equals(otherUserId, userId)) { + continue; + } + + // 判断是否为主播:检查用户是否为团队成员 + TeamMember teamMember = + teamMemberService.getByMemberId(otherUserId); + if (teamMember != null) { + return true; + } + } + + return false; + } + } diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/user/device/RegisterDeviceClientEndpoint.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/user/device/RegisterDeviceClientEndpoint.java index 3797121a..b8d2f4f9 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/user/device/RegisterDeviceClientEndpoint.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/user/device/RegisterDeviceClientEndpoint.java @@ -123,4 +123,9 @@ public class RegisterDeviceClientEndpoint implements RegisterDeviceClientApi { public ResultResponse getDeviceFingerprintByUserId(Long userId) { return ResultResponse.success(registerDeviceClientService.getDeviceFingerprintByUserId(userId)); } + + @Override + public ResultResponse checkDeviceFingerprintHasAnchor(Long userId) { + return ResultResponse.success(registerDeviceClientService.checkDeviceFingerprintHasAnchor(userId)); + } } diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/RegisterDeviceClientService.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/RegisterDeviceClientService.java index ab2c7463..e7c94cf9 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/RegisterDeviceClientService.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/RegisterDeviceClientService.java @@ -116,4 +116,12 @@ public interface RegisterDeviceClientService { * @return 设备指纹字符串 */ String getDeviceFingerprintByUserId(Long userId); + + /** + * 检查当前用户的设备指纹是否与其他主播用户共享 + * + * @param userId 用户ID + * @return true-存在共享设备的主播用户, false-不存在 + */ + Boolean checkDeviceFingerprintHasAnchor(Long userId); } diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/impl/RegisterDeviceClientServiceImpl.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/impl/RegisterDeviceClientServiceImpl.java index c8ff7082..ee5445eb 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/impl/RegisterDeviceClientServiceImpl.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/user/device/impl/RegisterDeviceClientServiceImpl.java @@ -8,6 +8,8 @@ import com.red.circle.other.app.inner.convertor.user.UserProfileInnerConvertor; import com.red.circle.other.app.inner.service.user.device.RegisterDeviceClientService; import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; import com.red.circle.other.infra.database.rds.entity.user.user.LatestMobileDevice; import com.red.circle.other.infra.database.rds.service.user.device.ArchiveDeviceService; import com.red.circle.other.infra.database.rds.service.user.device.DeviceRegisterQuantityService; @@ -49,6 +51,7 @@ public class RegisterDeviceClientServiceImpl implements RegisterDeviceClientServ private final LatestMobileDeviceService latestMobileDeviceService; private final RegisterDeviceInnerConvertor registerDeviceInnerConvertor; private final DeviceRegisterQuantityService deviceRegisterQuantityService; + private final TeamMemberService teamMemberService; @Override public Boolean existsDevice(String deviceNo, String sysOrigin) { @@ -234,4 +237,43 @@ public class RegisterDeviceClientServiceImpl implements RegisterDeviceClientServ public String getDeviceFingerprintByUserId(Long userId) { return latestMobileDeviceService.getDeviceFingerprintByUserId(userId); } + + @Override + public Boolean checkDeviceFingerprintHasAnchor(Long userId) { + if (userId == null) { + return false; + } + + UserProfile userProfile = userProfileGateway.getByUserId(userId); + if (userProfile == null) { + return false; + } + + String fingerprint = latestMobileDeviceService.getDeviceFingerprintByUserId(userId); + if (StringUtils.isBlank(fingerprint)) { + return false; + } + + List userIds = latestMobileDeviceService.listUserIdsByDeviceFingerprint( + fingerprint, userProfile.getOriginSys()); + + if (CollectionUtils.isEmpty(userIds)) { + return false; + } + + for (Long otherUserId : userIds) { + if (Objects.equals(otherUserId, userId)) { + continue; + } + + // 判断是否为主播:检查用户是否为团队成员 + TeamMember teamMember = + teamMemberService.getByMemberId(otherUserId); + if (teamMember != null) { + return true; + } + } + + return false; + } }