|
@@ -0,0 +1,962 @@
|
|
|
|
|
+package com.aijiuyi.admin.service.impl;
|
|
|
|
|
+
|
|
|
|
|
+import com.aijiuyi.admin.common.constant.ResultCode;
|
|
|
|
|
+import com.aijiuyi.admin.common.exception.BusinessException;
|
|
|
|
|
+import com.aijiuyi.admin.controller.dto.AppDeviceBindDTO;
|
|
|
|
|
+import com.aijiuyi.admin.controller.dto.AppDeviceSelectDTO;
|
|
|
|
|
+import com.aijiuyi.admin.controller.dto.AppGroupMemberDTO;
|
|
|
|
|
+import com.aijiuyi.admin.controller.dto.AppProfileDTO;
|
|
|
|
|
+import com.aijiuyi.admin.entity.AppUser;
|
|
|
|
|
+import com.aijiuyi.admin.entity.Device;
|
|
|
|
|
+import com.aijiuyi.admin.entity.DeviceGroup;
|
|
|
|
|
+import com.aijiuyi.admin.entity.DeviceGroupMember;
|
|
|
|
|
+import com.aijiuyi.admin.entity.UserCategory;
|
|
|
|
|
+import com.aijiuyi.admin.entity.UserDevice;
|
|
|
|
|
+import com.aijiuyi.admin.entity.UserProfile;
|
|
|
|
|
+import com.aijiuyi.admin.mapper.AppUserMapper;
|
|
|
|
|
+import com.aijiuyi.admin.mapper.DeviceGroupMapper;
|
|
|
|
|
+import com.aijiuyi.admin.mapper.DeviceGroupMemberMapper;
|
|
|
|
|
+import com.aijiuyi.admin.mapper.DeviceMapper;
|
|
|
|
|
+import com.aijiuyi.admin.mapper.UserCategoryMapper;
|
|
|
|
|
+import com.aijiuyi.admin.mapper.UserDeviceMapper;
|
|
|
|
|
+import com.aijiuyi.admin.mapper.UserProfileMapper;
|
|
|
|
|
+import com.aijiuyi.admin.service.AppDeviceGroupService;
|
|
|
|
|
+import com.aijiuyi.admin.service.UserAcupointService;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * App设备、家庭群组与用户档案服务实现。
|
|
|
|
|
+ */
|
|
|
|
|
+@Service
|
|
|
|
|
+public class AppDeviceGroupServiceImpl implements AppDeviceGroupService {
|
|
|
|
|
+
|
|
|
|
|
+ private static final int DEVICE_TYPE_FAMILY = 1;
|
|
|
|
|
+ private static final int DEVICE_TYPE_PUBLIC = 2;
|
|
|
|
|
+ private static final int NORMAL_USER_TYPE = 1;
|
|
|
|
|
+ private static final int STATUS_ENABLED = 1;
|
|
|
|
|
+ private static final String PHONE_PATTERN = "^1[3-9]\\d{9}$";
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private AppUserMapper appUserMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private DeviceMapper deviceMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private DeviceGroupMapper deviceGroupMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private DeviceGroupMemberMapper deviceGroupMemberMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private UserProfileMapper userProfileMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private UserDeviceMapper userDeviceMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private UserCategoryMapper userCategoryMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private UserAcupointService userAcupointService;
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public Map<String, Object> bindDevice(Long appUserId, AppDeviceBindDTO dto) {
|
|
|
|
|
+ AppUser appUser = requireAppUser(appUserId);
|
|
|
|
|
+ Device device = requireDevice(dto.getDeviceCode());
|
|
|
|
|
+ UserProfile accountProfile = ensureAccountProfile(appUser);
|
|
|
|
|
+
|
|
|
|
|
+ if (isPublicDevice(device)) {
|
|
|
|
|
+ ensureUserDevice(accountProfile, device);
|
|
|
|
|
+ return buildDeviceContext(device, null, null, accountProfile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ DeviceGroup group = ensureDeviceGroup(device);
|
|
|
|
|
+ DeviceGroupMember member = findActiveMemberByAppUserId(group.getId(), appUserId);
|
|
|
|
|
+ UserProfile profile;
|
|
|
|
|
+ if (member != null) {
|
|
|
|
|
+ profile = requireProfile(member.getProfileId());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ member = findActiveMemberByProfileId(group.getId(), accountProfile.getId());
|
|
|
|
|
+ if (member == null) {
|
|
|
|
|
+ member = createGroupMember(group.getId(), accountProfile.getId(), appUserId);
|
|
|
|
|
+ } else if (!Objects.equals(member.getAppUserId(), appUserId)) {
|
|
|
|
|
+ DeviceGroupMember update = new DeviceGroupMember();
|
|
|
|
|
+ update.setId(member.getId());
|
|
|
|
|
+ update.setAppUserId(appUserId);
|
|
|
|
|
+ deviceGroupMemberMapper.updateById(update);
|
|
|
|
|
+ member.setAppUserId(appUserId);
|
|
|
|
|
+ }
|
|
|
|
|
+ profile = accountProfile;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ensureUserDevice(profile, device);
|
|
|
|
|
+ return buildDeviceContext(device, group, member, profile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<Map<String, Object>> listDevices(Long appUserId) {
|
|
|
|
|
+ requireAppUser(appUserId);
|
|
|
|
|
+ Map<String, Map<String, Object>> result = new LinkedHashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ List<DeviceGroupMember> familyMembers = deviceGroupMemberMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DeviceGroupMember>()
|
|
|
|
|
+ .eq(DeviceGroupMember::getAppUserId, appUserId)
|
|
|
|
|
+ .eq(DeviceGroupMember::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ .orderByDesc(DeviceGroupMember::getJoinTime)
|
|
|
|
|
+ );
|
|
|
|
|
+ for (DeviceGroupMember member : familyMembers) {
|
|
|
|
|
+ DeviceGroup group = deviceGroupMapper.selectById(member.getGroupId());
|
|
|
|
|
+ if (group == null || !Integer.valueOf(STATUS_ENABLED).equals(group.getStatus())) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ Device device = findDeviceByCode(group.getDeviceCode());
|
|
|
|
|
+ if (device == null || !isFamilyDevice(device)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ UserProfile profile = userProfileMapper.selectById(member.getProfileId());
|
|
|
|
|
+ result.put("F:" + device.getDeviceCode(), buildDeviceListItem(device, group, member, profile));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<UserProfile> profiles = userProfileMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<UserProfile>().eq(UserProfile::getUserId, appUserId)
|
|
|
|
|
+ );
|
|
|
|
|
+ if (!CollectionUtils.isEmpty(profiles)) {
|
|
|
|
|
+ List<Long> profileIds = profiles.stream().map(UserProfile::getId).collect(Collectors.toList());
|
|
|
|
|
+ List<UserDevice> bindings = userDeviceMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<UserDevice>()
|
|
|
|
|
+ .in(UserDevice::getUserId, profileIds)
|
|
|
|
|
+ .eq(UserDevice::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ .orderByDesc(UserDevice::getBindTime)
|
|
|
|
|
+ );
|
|
|
|
|
+ Map<Long, UserProfile> profileMap = profiles.stream()
|
|
|
|
|
+ .collect(Collectors.toMap(UserProfile::getId, p -> p, (a, b) -> a));
|
|
|
|
|
+ for (UserDevice binding : bindings) {
|
|
|
|
|
+ Device device = findDeviceByCode(binding.getDeviceCode());
|
|
|
|
|
+ if (device == null || !isPublicDevice(device)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ UserProfile profile = profileMap.get(binding.getUserId());
|
|
|
|
|
+ result.put("P:" + device.getDeviceCode(), buildDeviceListItem(device, null, null, profile));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new ArrayList<>(result.values());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Map<String, Object> selectDevice(Long appUserId, AppDeviceSelectDTO dto) {
|
|
|
|
|
+ requireAppUser(appUserId);
|
|
|
|
|
+ Device device = requireDevice(dto.getDeviceCode());
|
|
|
|
|
+ if (isFamilyDevice(device)) {
|
|
|
|
|
+ DeviceGroup group = dto.getGroupId() != null
|
|
|
|
|
+ ? requireGroup(dto.getGroupId())
|
|
|
|
|
+ : requireDeviceGroup(device.getDeviceCode());
|
|
|
|
|
+ if (!device.getDeviceCode().equals(group.getDeviceCode())) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.FORBIDDEN.getCode(), "无权使用该家庭设备");
|
|
|
|
|
+ }
|
|
|
|
|
+ DeviceGroupMember currentMember = requireCurrentAccountGroupMember(group.getId(), appUserId);
|
|
|
|
|
+ DeviceGroupMember selectedMember = currentMember;
|
|
|
|
|
+ if (dto.getProfileId() != null) {
|
|
|
|
|
+ selectedMember = findActiveMemberByProfileId(group.getId(), dto.getProfileId());
|
|
|
|
|
+ if (selectedMember == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.FORBIDDEN.getCode(), "所选成员不属于该群组");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ UserProfile selectedProfile = requireProfile(selectedMember.getProfileId());
|
|
|
|
|
+ return buildDeviceContext(device, group, selectedMember, selectedProfile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ UserProfile selectedProfile = dto.getProfileId() != null
|
|
|
|
|
+ ? requireProfile(dto.getProfileId())
|
|
|
|
|
+ : ensureAccountProfile(requireAppUser(appUserId));
|
|
|
|
|
+ if (!Objects.equals(selectedProfile.getUserId(), appUserId)) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.FORBIDDEN.getCode(), "公共设备只能选择当前账号档案");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!hasActiveUserDevice(selectedProfile.getId(), device.getDeviceCode())) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.FORBIDDEN.getCode(), "请先绑定设备");
|
|
|
|
|
+ }
|
|
|
|
|
+ return buildDeviceContext(device, null, null, selectedProfile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<Map<String, Object>> listGroupMembers(Long appUserId, Long groupId) {
|
|
|
|
|
+ DeviceGroup group = assertGroupAccess(groupId, appUserId);
|
|
|
|
|
+ List<DeviceGroupMember> members = deviceGroupMemberMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DeviceGroupMember>()
|
|
|
|
|
+ .eq(DeviceGroupMember::getGroupId, group.getId())
|
|
|
|
|
+ .eq(DeviceGroupMember::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ .orderByAsc(DeviceGroupMember::getJoinTime)
|
|
|
|
|
+ );
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ for (DeviceGroupMember member : members) {
|
|
|
|
|
+ UserProfile profile = userProfileMapper.selectById(member.getProfileId());
|
|
|
|
|
+ if (profile != null) {
|
|
|
|
|
+ result.add(buildMemberMap(member, profile));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public Map<String, Object> addGroupMember(Long appUserId, Long groupId, AppGroupMemberDTO dto) {
|
|
|
|
|
+ DeviceGroup group = assertGroupAccess(groupId, appUserId);
|
|
|
|
|
+ validateFingerWidths(dto.getGender(), dto.getFingerWidth1(), dto.getFingerWidth15(), dto.getFingerWidth3());
|
|
|
|
|
+ String phone = normalizePhone(dto.getPhone());
|
|
|
|
|
+ checkGroupPhoneAvailable(group.getId(), phone, null);
|
|
|
|
|
+
|
|
|
|
|
+ AppUser linkedAccount = ensureAccountForPhone(phone, dto.getName(), dto.getUserCategoryId());
|
|
|
|
|
+ checkGroupAppUserAvailable(group.getId(), linkedAccount == null ? null : linkedAccount.getId(), null);
|
|
|
|
|
+
|
|
|
|
|
+ UserProfile profile = resolveProfileForNewMember(dto, phone, linkedAccount);
|
|
|
|
|
+ DeviceGroupMember member = createGroupMember(group.getId(), profile.getId(), linkedAccount == null ? null : linkedAccount.getId());
|
|
|
|
|
+
|
|
|
|
|
+ Device device = requireDevice(group.getDeviceCode());
|
|
|
|
|
+ ensureUserDevice(profile, device);
|
|
|
|
|
+ regenerateIfComplete(profile);
|
|
|
|
|
+ return buildMemberMap(member, profile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public Map<String, Object> updateGroupMember(Long appUserId, Long groupId, Long profileId, AppGroupMemberDTO dto) {
|
|
|
|
|
+ DeviceGroup group = assertGroupAccess(groupId, appUserId);
|
|
|
|
|
+ DeviceGroupMember member = findActiveMemberByProfileId(group.getId(), profileId);
|
|
|
|
|
+ if (member == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.PROFILE_NOT_FOUND);
|
|
|
|
|
+ }
|
|
|
|
|
+ requireProfile(profileId);
|
|
|
|
|
+ validateFingerWidths(dto.getGender(), dto.getFingerWidth1(), dto.getFingerWidth15(), dto.getFingerWidth3());
|
|
|
|
|
+
|
|
|
|
|
+ String phone = normalizePhone(dto.getPhone());
|
|
|
|
|
+ checkGroupPhoneAvailable(group.getId(), phone, profileId);
|
|
|
|
|
+ AppUser linkedAccount = ensureAccountForPhone(phone, dto.getName(), dto.getUserCategoryId());
|
|
|
|
|
+ Long linkedAppUserId = linkedAccount == null ? null : linkedAccount.getId();
|
|
|
|
|
+ checkGroupAppUserAvailable(group.getId(), linkedAppUserId, profileId);
|
|
|
|
|
+
|
|
|
|
|
+ updateProfileFromGroupDto(profileId, dto, phone, linkedAppUserId);
|
|
|
|
|
+ updateMemberAppUser(member, linkedAppUserId);
|
|
|
|
|
+
|
|
|
|
|
+ UserProfile updatedProfile = requireProfile(profileId);
|
|
|
|
|
+ Device device = requireDevice(group.getDeviceCode());
|
|
|
|
|
+ ensureUserDevice(updatedProfile, device);
|
|
|
|
|
+ regenerateIfComplete(updatedProfile);
|
|
|
|
|
+ return buildMemberMap(member, updatedProfile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Map<String, Object> getCurrentProfile(Long appUserId) {
|
|
|
|
|
+ AppUser appUser = requireAppUser(appUserId);
|
|
|
|
|
+ UserProfile profile = ensureAccountProfile(appUser);
|
|
|
|
|
+ return buildCurrentProfileMap(appUser, profile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public Map<String, Object> updateCurrentProfile(Long appUserId, AppProfileDTO dto) {
|
|
|
|
|
+ AppUser appUser = requireAppUser(appUserId);
|
|
|
|
|
+ if (StringUtils.hasText(dto.getNickname()) || StringUtils.hasText(dto.getAvatar())) {
|
|
|
|
|
+ AppUser updateUser = new AppUser();
|
|
|
|
|
+ updateUser.setId(appUser.getId());
|
|
|
|
|
+ if (StringUtils.hasText(dto.getNickname())) {
|
|
|
|
|
+ updateUser.setNickname(dto.getNickname().trim());
|
|
|
|
|
+ appUser.setNickname(updateUser.getNickname());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.hasText(dto.getAvatar())) {
|
|
|
|
|
+ updateUser.setAvatar(dto.getAvatar().trim());
|
|
|
|
|
+ appUser.setAvatar(updateUser.getAvatar());
|
|
|
|
|
+ }
|
|
|
|
|
+ appUserMapper.updateById(updateUser);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ UserProfile profile = ensureAccountProfile(appUser);
|
|
|
|
|
+ validateFingerWidths(dto.getGender(), dto.getFingerWidth1(), dto.getFingerWidth15(), dto.getFingerWidth3());
|
|
|
|
|
+ UserProfile updateProfile = new UserProfile();
|
|
|
|
|
+ updateProfile.setId(profile.getId());
|
|
|
|
|
+ boolean changed = fillNonNullProfileFields(updateProfile, dto);
|
|
|
|
|
+ if (changed) {
|
|
|
|
|
+ userProfileMapper.updateById(updateProfile);
|
|
|
|
|
+ profile = requireProfile(profile.getId());
|
|
|
|
|
+ regenerateIfComplete(profile);
|
|
|
|
|
+ }
|
|
|
|
|
+ return buildCurrentProfileMap(appUser, profile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private AppUser requireAppUser(Long appUserId) {
|
|
|
|
|
+ AppUser appUser = appUserMapper.selectById(appUserId);
|
|
|
|
|
+ if (appUser == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.APP_USER_NOT_FOUND);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Integer.valueOf(0).equals(appUser.getStatus())) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.APP_USER_DISABLED);
|
|
|
|
|
+ }
|
|
|
|
|
+ return appUser;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Device requireDevice(String deviceCode) {
|
|
|
|
|
+ Device device = findDeviceByCode(deviceCode);
|
|
|
|
|
+ if (device == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.DEVICE_NOT_FOUND);
|
|
|
|
|
+ }
|
|
|
|
|
+ return device;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Device findDeviceByCode(String deviceCode) {
|
|
|
|
|
+ String code = trimToNull(deviceCode);
|
|
|
|
|
+ if (code == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "设备编号不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ return deviceMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<Device>()
|
|
|
|
|
+ .eq(Device::getDeviceCode, code)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private UserProfile requireProfile(Long profileId) {
|
|
|
|
|
+ UserProfile profile = userProfileMapper.selectById(profileId);
|
|
|
|
|
+ if (profile == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.PROFILE_NOT_FOUND);
|
|
|
|
|
+ }
|
|
|
|
|
+ return profile;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroup requireGroup(Long groupId) {
|
|
|
|
|
+ DeviceGroup group = deviceGroupMapper.selectById(groupId);
|
|
|
|
|
+ if (group == null || !Integer.valueOf(STATUS_ENABLED).equals(group.getStatus())) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.NOT_FOUND.getCode(), "家庭群组不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ return group;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroup requireDeviceGroup(String deviceCode) {
|
|
|
|
|
+ DeviceGroup group = findDeviceGroupByCode(deviceCode);
|
|
|
|
|
+ if (group == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.NOT_FOUND.getCode(), "设备尚未建立家庭群组,请先绑定设备");
|
|
|
|
|
+ }
|
|
|
|
|
+ return group;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroup assertGroupAccess(Long groupId, Long appUserId) {
|
|
|
|
|
+ DeviceGroup group = requireGroup(groupId);
|
|
|
|
|
+ requireCurrentAccountGroupMember(group.getId(), appUserId);
|
|
|
|
|
+ return group;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroupMember requireCurrentAccountGroupMember(Long groupId, Long appUserId) {
|
|
|
|
|
+ DeviceGroupMember member = findActiveMemberByAppUserId(groupId, appUserId);
|
|
|
|
|
+ if (member == null) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.FORBIDDEN.getCode(), "无权访问该家庭群组");
|
|
|
|
|
+ }
|
|
|
|
|
+ return member;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroup ensureDeviceGroup(Device device) {
|
|
|
|
|
+ DeviceGroup group = findDeviceGroupByCode(device.getDeviceCode());
|
|
|
|
|
+ if (group != null) {
|
|
|
|
|
+ return group;
|
|
|
|
|
+ }
|
|
|
|
|
+ group = new DeviceGroup();
|
|
|
|
|
+ group.setDeviceCode(device.getDeviceCode());
|
|
|
|
|
+ group.setStatus(STATUS_ENABLED);
|
|
|
|
|
+ deviceGroupMapper.insert(group);
|
|
|
|
|
+ return group;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroup findDeviceGroupByCode(String deviceCode) {
|
|
|
|
|
+ return deviceGroupMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DeviceGroup>()
|
|
|
|
|
+ .eq(DeviceGroup::getDeviceCode, deviceCode)
|
|
|
|
|
+ .eq(DeviceGroup::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroupMember createGroupMember(Long groupId, Long profileId, Long appUserId) {
|
|
|
|
|
+ DeviceGroupMember member = new DeviceGroupMember();
|
|
|
|
|
+ member.setGroupId(groupId);
|
|
|
|
|
+ member.setProfileId(profileId);
|
|
|
|
|
+ member.setAppUserId(appUserId);
|
|
|
|
|
+ member.setStatus(STATUS_ENABLED);
|
|
|
|
|
+ member.setJoinTime(LocalDateTime.now());
|
|
|
|
|
+ deviceGroupMemberMapper.insert(member);
|
|
|
|
|
+ return member;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroupMember findActiveMemberByProfileId(Long groupId, Long profileId) {
|
|
|
|
|
+ return deviceGroupMemberMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DeviceGroupMember>()
|
|
|
|
|
+ .eq(DeviceGroupMember::getGroupId, groupId)
|
|
|
|
|
+ .eq(DeviceGroupMember::getProfileId, profileId)
|
|
|
|
|
+ .eq(DeviceGroupMember::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DeviceGroupMember findActiveMemberByAppUserId(Long groupId, Long appUserId) {
|
|
|
|
|
+ return deviceGroupMemberMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DeviceGroupMember>()
|
|
|
|
|
+ .eq(DeviceGroupMember::getGroupId, groupId)
|
|
|
|
|
+ .eq(DeviceGroupMember::getAppUserId, appUserId)
|
|
|
|
|
+ .eq(DeviceGroupMember::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private UserProfile ensureAccountProfile(AppUser appUser) {
|
|
|
|
|
+ UserProfile profile = userProfileMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<UserProfile>()
|
|
|
|
|
+ .eq(UserProfile::getUserId, appUser.getId())
|
|
|
|
|
+ .orderByAsc(UserProfile::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ if (profile != null) {
|
|
|
|
|
+ return profile;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String phone = trimToNull(appUser.getPhone());
|
|
|
|
|
+ if (phone != null) {
|
|
|
|
|
+ profile = userProfileMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<UserProfile>()
|
|
|
|
|
+ .eq(UserProfile::getPhone, phone)
|
|
|
|
|
+ .and(w -> w.isNull(UserProfile::getUserId).or().eq(UserProfile::getUserId, appUser.getId()))
|
|
|
|
|
+ .orderByAsc(UserProfile::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ if (profile != null) {
|
|
|
|
|
+ UserProfile update = new UserProfile();
|
|
|
|
|
+ update.setId(profile.getId());
|
|
|
|
|
+ update.setUserId(appUser.getId());
|
|
|
|
|
+ userProfileMapper.updateById(update);
|
|
|
|
|
+ profile.setUserId(appUser.getId());
|
|
|
|
|
+ return profile;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ profile = new UserProfile();
|
|
|
|
|
+ profile.setUserId(appUser.getId());
|
|
|
|
|
+ profile.setPhone(phone);
|
|
|
|
|
+ profile.setName(resolveProfileName(appUser.getNickname(), phone, appUser.getId()));
|
|
|
|
|
+ profile.setUserType(NORMAL_USER_TYPE);
|
|
|
|
|
+ profile.setAddress(appUser.getAddress());
|
|
|
|
|
+ userProfileMapper.insert(profile);
|
|
|
|
|
+ return profile;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private AppUser ensureAccountForPhone(String phone, String name, Long userCategoryId) {
|
|
|
|
|
+ if (phone == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ validatePhone(phone);
|
|
|
|
|
+ AppUser user = appUserMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<AppUser>()
|
|
|
|
|
+ .eq(AppUser::getPhone, phone)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ if (user != null) {
|
|
|
|
|
+ if (Integer.valueOf(0).equals(user.getStatus())) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.APP_USER_DISABLED);
|
|
|
|
|
+ }
|
|
|
|
|
+ syncUserCategory(user, userCategoryId);
|
|
|
|
|
+ return user;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ user = new AppUser();
|
|
|
|
|
+ user.setPhone(phone);
|
|
|
|
|
+ user.setNickname(resolveProfileName(name, phone, null));
|
|
|
|
|
+ user.setUserType(NORMAL_USER_TYPE);
|
|
|
|
|
+ user.setUserCategoryId(userCategoryId != null ? validateUserCategory(userCategoryId) : getDefaultUserCategoryId());
|
|
|
|
|
+ user.setStatus(STATUS_ENABLED);
|
|
|
|
|
+ appUserMapper.insert(user);
|
|
|
|
|
+ AppUser update = new AppUser();
|
|
|
|
|
+ update.setId(user.getId());
|
|
|
|
|
+ update.setAppId("AJY" + String.format("%08d", user.getId()));
|
|
|
|
|
+ appUserMapper.updateById(update);
|
|
|
|
|
+ user.setAppId(update.getAppId());
|
|
|
|
|
+ return user;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void syncUserCategory(AppUser user, Long userCategoryId) {
|
|
|
|
|
+ if (userCategoryId == null || !Integer.valueOf(NORMAL_USER_TYPE).equals(user.getUserType())) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long validCategoryId = validateUserCategory(userCategoryId);
|
|
|
|
|
+ if (validCategoryId.equals(user.getUserCategoryId())) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ AppUser update = new AppUser();
|
|
|
|
|
+ update.setId(user.getId());
|
|
|
|
|
+ update.setUserCategoryId(validCategoryId);
|
|
|
|
|
+ appUserMapper.updateById(update);
|
|
|
|
|
+ user.setUserCategoryId(validCategoryId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Long validateUserCategory(Long userCategoryId) {
|
|
|
|
|
+ if (userCategoryId == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ long count = userCategoryMapper.selectCount(new LambdaQueryWrapper<UserCategory>()
|
|
|
|
|
+ .eq(UserCategory::getId, userCategoryId)
|
|
|
|
|
+ .eq(UserCategory::getUserType, NORMAL_USER_TYPE));
|
|
|
|
|
+ if (count <= 0) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.USER_CATEGORY_NOT_FOUND);
|
|
|
|
|
+ }
|
|
|
|
|
+ return userCategoryId;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Long getDefaultUserCategoryId() {
|
|
|
|
|
+ UserCategory category = userCategoryMapper.selectOne(new LambdaQueryWrapper<UserCategory>()
|
|
|
|
|
+ .eq(UserCategory::getUserType, NORMAL_USER_TYPE)
|
|
|
|
|
+ .eq(UserCategory::getName, "普通用户")
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (category == null) {
|
|
|
|
|
+ category = userCategoryMapper.selectOne(new LambdaQueryWrapper<UserCategory>()
|
|
|
|
|
+ .eq(UserCategory::getUserType, NORMAL_USER_TYPE)
|
|
|
|
|
+ .orderByAsc(UserCategory::getId)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ }
|
|
|
|
|
+ return category != null ? category.getId() : null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private UserProfile buildProfileFromGroupDto(AppGroupMemberDTO dto, String phone, Long appUserId) {
|
|
|
|
|
+ UserProfile profile = new UserProfile();
|
|
|
|
|
+ profile.setUserId(appUserId);
|
|
|
|
|
+ profile.setName(dto.getName().trim());
|
|
|
|
|
+ profile.setGender(dto.getGender());
|
|
|
|
|
+ profile.setAge(dto.getAge());
|
|
|
|
|
+ profile.setPhone(phone);
|
|
|
|
|
+ profile.setUserType(NORMAL_USER_TYPE);
|
|
|
|
|
+ profile.setProvinceCode(trimToNull(dto.getProvinceCode()));
|
|
|
|
|
+ profile.setCityCode(trimToNull(dto.getCityCode()));
|
|
|
|
|
+ profile.setDistrictCode(trimToNull(dto.getDistrictCode()));
|
|
|
|
|
+ profile.setAddress(trimToNull(dto.getAddress()));
|
|
|
|
|
+ profile.setShoulderWidth(dto.getShoulderWidth());
|
|
|
|
|
+ profile.setBodyHeight(dto.getBodyHeight());
|
|
|
|
|
+ profile.setSpineLength(dto.getSpineLength());
|
|
|
|
|
+ profile.setHeight(dto.getHeight());
|
|
|
|
|
+ profile.setWeight(dto.getWeight());
|
|
|
|
|
+ profile.setFingerWidth1(dto.getFingerWidth1());
|
|
|
|
|
+ profile.setFingerWidth15(dto.getFingerWidth15());
|
|
|
|
|
+ profile.setFingerWidth3(dto.getFingerWidth3());
|
|
|
|
|
+ profile.setAcupointTableId(dto.getAcupointTableId());
|
|
|
|
|
+ return profile;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private UserProfile resolveProfileForNewMember(AppGroupMemberDTO dto, String phone, AppUser linkedAccount) {
|
|
|
|
|
+ if (linkedAccount == null) {
|
|
|
|
|
+ UserProfile profile = buildProfileFromGroupDto(dto, phone, null);
|
|
|
|
|
+ userProfileMapper.insert(profile);
|
|
|
|
|
+ return profile;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ UserProfile profile = userProfileMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<UserProfile>()
|
|
|
|
|
+ .eq(UserProfile::getUserId, linkedAccount.getId())
|
|
|
|
|
+ .orderByAsc(UserProfile::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ if (profile == null) {
|
|
|
|
|
+ profile = userProfileMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<UserProfile>()
|
|
|
|
|
+ .eq(UserProfile::getPhone, phone)
|
|
|
|
|
+ .isNull(UserProfile::getUserId)
|
|
|
|
|
+ .orderByAsc(UserProfile::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ if (profile == null) {
|
|
|
|
|
+ profile = buildProfileFromGroupDto(dto, phone, linkedAccount.getId());
|
|
|
|
|
+ userProfileMapper.insert(profile);
|
|
|
|
|
+ return profile;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ updateProfileFromGroupDto(profile.getId(), dto, phone, linkedAccount.getId());
|
|
|
|
|
+ return requireProfile(profile.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void updateProfileFromGroupDto(Long profileId, AppGroupMemberDTO dto, String phone, Long appUserId) {
|
|
|
|
|
+ LambdaUpdateWrapper<UserProfile> wrapper = new LambdaUpdateWrapper<UserProfile>()
|
|
|
|
|
+ .eq(UserProfile::getId, profileId)
|
|
|
|
|
+ .set(UserProfile::getUserId, appUserId)
|
|
|
|
|
+ .set(UserProfile::getName, dto.getName().trim())
|
|
|
|
|
+ .set(UserProfile::getGender, dto.getGender())
|
|
|
|
|
+ .set(UserProfile::getAge, dto.getAge())
|
|
|
|
|
+ .set(UserProfile::getPhone, phone)
|
|
|
|
|
+ .set(UserProfile::getProvinceCode, trimToNull(dto.getProvinceCode()))
|
|
|
|
|
+ .set(UserProfile::getCityCode, trimToNull(dto.getCityCode()))
|
|
|
|
|
+ .set(UserProfile::getDistrictCode, trimToNull(dto.getDistrictCode()))
|
|
|
|
|
+ .set(UserProfile::getAddress, trimToNull(dto.getAddress()))
|
|
|
|
|
+ .set(UserProfile::getShoulderWidth, dto.getShoulderWidth())
|
|
|
|
|
+ .set(UserProfile::getBodyHeight, dto.getBodyHeight())
|
|
|
|
|
+ .set(UserProfile::getSpineLength, dto.getSpineLength())
|
|
|
|
|
+ .set(UserProfile::getHeight, dto.getHeight())
|
|
|
|
|
+ .set(UserProfile::getWeight, dto.getWeight())
|
|
|
|
|
+ .set(UserProfile::getFingerWidth1, dto.getFingerWidth1())
|
|
|
|
|
+ .set(UserProfile::getFingerWidth15, dto.getFingerWidth15())
|
|
|
|
|
+ .set(UserProfile::getFingerWidth3, dto.getFingerWidth3())
|
|
|
|
|
+ .set(UserProfile::getAcupointTableId, dto.getAcupointTableId());
|
|
|
|
|
+ userProfileMapper.update(null, wrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean fillNonNullProfileFields(UserProfile profile, AppProfileDTO dto) {
|
|
|
|
|
+ boolean changed = false;
|
|
|
|
|
+ if (StringUtils.hasText(dto.getName())) {
|
|
|
|
|
+ profile.setName(dto.getName().trim());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getGender() != null) {
|
|
|
|
|
+ profile.setGender(dto.getGender());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getAge() != null) {
|
|
|
|
|
+ profile.setAge(dto.getAge());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ String phone = normalizePhone(dto.getPhone());
|
|
|
|
|
+ if (phone != null) {
|
|
|
|
|
+ profile.setPhone(phone);
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getProvinceCode() != null) {
|
|
|
|
|
+ profile.setProvinceCode(trimToNull(dto.getProvinceCode()));
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getCityCode() != null) {
|
|
|
|
|
+ profile.setCityCode(trimToNull(dto.getCityCode()));
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getDistrictCode() != null) {
|
|
|
|
|
+ profile.setDistrictCode(trimToNull(dto.getDistrictCode()));
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getAddress() != null) {
|
|
|
|
|
+ profile.setAddress(trimToNull(dto.getAddress()));
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getShoulderWidth() != null) {
|
|
|
|
|
+ profile.setShoulderWidth(dto.getShoulderWidth());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getBodyHeight() != null) {
|
|
|
|
|
+ profile.setBodyHeight(dto.getBodyHeight());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getSpineLength() != null) {
|
|
|
|
|
+ profile.setSpineLength(dto.getSpineLength());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getHeight() != null) {
|
|
|
|
|
+ profile.setHeight(dto.getHeight());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getWeight() != null) {
|
|
|
|
|
+ profile.setWeight(dto.getWeight());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getFingerWidth1() != null) {
|
|
|
|
|
+ profile.setFingerWidth1(dto.getFingerWidth1());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getFingerWidth15() != null) {
|
|
|
|
|
+ profile.setFingerWidth15(dto.getFingerWidth15());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getFingerWidth3() != null) {
|
|
|
|
|
+ profile.setFingerWidth3(dto.getFingerWidth3());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getAcupointTableId() != null) {
|
|
|
|
|
+ profile.setAcupointTableId(dto.getAcupointTableId());
|
|
|
|
|
+ changed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return changed;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void updateMemberAppUser(DeviceGroupMember member, Long appUserId) {
|
|
|
|
|
+ LambdaUpdateWrapper<DeviceGroupMember> wrapper = new LambdaUpdateWrapper<DeviceGroupMember>()
|
|
|
|
|
+ .eq(DeviceGroupMember::getId, member.getId())
|
|
|
|
|
+ .set(DeviceGroupMember::getAppUserId, appUserId);
|
|
|
|
|
+ deviceGroupMemberMapper.update(null, wrapper);
|
|
|
|
|
+ member.setAppUserId(appUserId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void ensureUserDevice(UserProfile profile, Device device) {
|
|
|
|
|
+ UserDevice binding = userDeviceMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<UserDevice>()
|
|
|
|
|
+ .eq(UserDevice::getUserId, profile.getId())
|
|
|
|
|
+ .eq(UserDevice::getDeviceCode, device.getDeviceCode())
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ if (binding == null) {
|
|
|
|
|
+ binding = new UserDevice();
|
|
|
|
|
+ binding.setUserId(profile.getId());
|
|
|
|
|
+ binding.setDeviceCode(device.getDeviceCode());
|
|
|
|
|
+ binding.setDeviceName(device.getDeviceName());
|
|
|
|
|
+ binding.setDeviceModel(device.getDeviceModel());
|
|
|
|
|
+ binding.setIsPrimary(0);
|
|
|
|
|
+ binding.setOnlineStatus(device.getOnlineStatus());
|
|
|
|
|
+ binding.setStatus(STATUS_ENABLED);
|
|
|
|
|
+ binding.setBindTime(LocalDateTime.now());
|
|
|
|
|
+ userDeviceMapper.insert(binding);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ LambdaUpdateWrapper<UserDevice> wrapper = new LambdaUpdateWrapper<UserDevice>()
|
|
|
|
|
+ .eq(UserDevice::getId, binding.getId())
|
|
|
|
|
+ .set(UserDevice::getDeviceName, device.getDeviceName())
|
|
|
|
|
+ .set(UserDevice::getDeviceModel, device.getDeviceModel())
|
|
|
|
|
+ .set(UserDevice::getOnlineStatus, device.getOnlineStatus())
|
|
|
|
|
+ .set(UserDevice::getStatus, STATUS_ENABLED);
|
|
|
|
|
+ userDeviceMapper.update(null, wrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean hasActiveUserDevice(Long profileId, String deviceCode) {
|
|
|
|
|
+ long count = userDeviceMapper.selectCount(
|
|
|
|
|
+ new LambdaQueryWrapper<UserDevice>()
|
|
|
|
|
+ .eq(UserDevice::getUserId, profileId)
|
|
|
|
|
+ .eq(UserDevice::getDeviceCode, deviceCode)
|
|
|
|
|
+ .eq(UserDevice::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ );
|
|
|
|
|
+ return count > 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void checkGroupPhoneAvailable(Long groupId, String phone, Long excludeProfileId) {
|
|
|
|
|
+ if (phone == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<DeviceGroupMember> members = deviceGroupMemberMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DeviceGroupMember>()
|
|
|
|
|
+ .eq(DeviceGroupMember::getGroupId, groupId)
|
|
|
|
|
+ .eq(DeviceGroupMember::getStatus, STATUS_ENABLED)
|
|
|
|
|
+ );
|
|
|
|
|
+ for (DeviceGroupMember member : members) {
|
|
|
|
|
+ if (Objects.equals(member.getProfileId(), excludeProfileId)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ UserProfile profile = userProfileMapper.selectById(member.getProfileId());
|
|
|
|
|
+ if (profile != null && phone.equals(profile.getPhone())) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.PHONE_PROFILE_EXISTS.getCode(), "同一群组内手机号不能重复");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void checkGroupAppUserAvailable(Long groupId, Long appUserId, Long excludeProfileId) {
|
|
|
|
|
+ if (appUserId == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ DeviceGroupMember member = findActiveMemberByAppUserId(groupId, appUserId);
|
|
|
|
|
+ if (member != null && !Objects.equals(member.getProfileId(), excludeProfileId)) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.PHONE_PROFILE_EXISTS.getCode(), "该手机号对应账号已在群组中");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void regenerateIfComplete(UserProfile profile) {
|
|
|
|
|
+ if (isProfileComplete(profile)) {
|
|
|
|
|
+ userAcupointService.generateForUser(profile.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean isProfileComplete(UserProfile profile) {
|
|
|
|
|
+ return profile != null
|
|
|
|
|
+ && StringUtils.hasText(profile.getName())
|
|
|
|
|
+ && profile.getGender() != null
|
|
|
|
|
+ && profile.getAge() != null
|
|
|
|
|
+ && profile.getShoulderWidth() != null
|
|
|
|
|
+ && profile.getBodyHeight() != null
|
|
|
|
|
+ && profile.getSpineLength() != null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateFingerWidths(Integer gender, BigDecimal fingerWidth1,
|
|
|
|
|
+ BigDecimal fingerWidth15, BigDecimal fingerWidth3) {
|
|
|
|
|
+ boolean male = Integer.valueOf(1).equals(gender);
|
|
|
|
|
+ boolean female = Integer.valueOf(2).equals(gender);
|
|
|
|
|
+ if (!male && !female) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ checkFingerWidth("1寸", fingerWidth1,
|
|
|
|
|
+ male ? new BigDecimal("1.5") : new BigDecimal("1.3"),
|
|
|
|
|
+ male ? new BigDecimal("2.8") : new BigDecimal("2.4"));
|
|
|
|
|
+ checkFingerWidth("1.5寸", fingerWidth15,
|
|
|
|
|
+ male ? new BigDecimal("2.2") : new BigDecimal("2.0"),
|
|
|
|
|
+ male ? new BigDecimal("4.0") : new BigDecimal("3.5"));
|
|
|
|
|
+ checkFingerWidth("3寸", fingerWidth3,
|
|
|
|
|
+ male ? new BigDecimal("4.5") : new BigDecimal("4.0"),
|
|
|
|
|
+ male ? new BigDecimal("8.0") : new BigDecimal("7.0"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void checkFingerWidth(String label, BigDecimal value, BigDecimal min, BigDecimal max) {
|
|
|
|
|
+ if (value == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value.compareTo(min) < 0 || value.compareTo(max) > 0) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.FINGER_WIDTH_RANGE_ERROR.getCode(),
|
|
|
|
|
+ label + "指宽应在 " + min.toPlainString() + "~" + max.toPlainString() + " cm 之间");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> buildDeviceContext(Device device, DeviceGroup group,
|
|
|
|
|
+ DeviceGroupMember member, UserProfile profile) {
|
|
|
|
|
+ Map<String, Object> data = buildDeviceListItem(device, group, member, profile);
|
|
|
|
|
+ data.put("device", buildDeviceMap(device));
|
|
|
|
|
+ data.put("group", group == null ? null : buildGroupMap(group));
|
|
|
|
|
+ data.put("member", member == null ? null : buildMemberMap(member, profile));
|
|
|
|
|
+ data.put("profile", buildProfileMap(profile));
|
|
|
|
|
+ data.put("profileComplete", isProfileComplete(profile));
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> buildDeviceListItem(Device device, DeviceGroup group,
|
|
|
|
|
+ DeviceGroupMember member, UserProfile profile) {
|
|
|
|
|
+ Map<String, Object> data = buildDeviceMap(device);
|
|
|
|
|
+ data.put("groupId", group == null ? null : group.getId());
|
|
|
|
|
+ data.put("memberId", member == null ? null : member.getId());
|
|
|
|
|
+ data.put("profileId", profile == null ? null : profile.getId());
|
|
|
|
|
+ data.put("profileName", profile == null ? null : profile.getName());
|
|
|
|
|
+ data.put("profileComplete", isProfileComplete(profile));
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> buildDeviceMap(Device device) {
|
|
|
|
|
+ Map<String, Object> data = new HashMap<>(16);
|
|
|
|
|
+ data.put("id", device.getId());
|
|
|
|
|
+ data.put("deviceId", device.getId());
|
|
|
|
|
+ data.put("deviceCode", device.getDeviceCode());
|
|
|
|
|
+ data.put("deviceName", device.getDeviceName());
|
|
|
|
|
+ data.put("deviceModel", device.getDeviceModel());
|
|
|
|
|
+ data.put("deviceType", resolveDeviceType(device));
|
|
|
|
|
+ data.put("deviceTypeName", isPublicDevice(device) ? "公共设备" : "家庭设备");
|
|
|
|
|
+ data.put("serialNo", device.getSerialNo());
|
|
|
|
|
+ data.put("firmwareVersion", device.getFirmwareVersion());
|
|
|
|
|
+ data.put("onlineStatus", device.getOnlineStatus());
|
|
|
|
|
+ data.put("lastOnlineTime", device.getLastOnlineTime());
|
|
|
|
|
+ data.put("address", device.getAddress());
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> buildGroupMap(DeviceGroup group) {
|
|
|
|
|
+ Map<String, Object> data = new HashMap<>(8);
|
|
|
|
|
+ data.put("id", group.getId());
|
|
|
|
|
+ data.put("groupId", group.getId());
|
|
|
|
|
+ data.put("deviceCode", group.getDeviceCode());
|
|
|
|
|
+ data.put("status", group.getStatus());
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> buildMemberMap(DeviceGroupMember member, UserProfile profile) {
|
|
|
|
|
+ Map<String, Object> data = new HashMap<>(32);
|
|
|
|
|
+ data.put("memberId", member.getId());
|
|
|
|
|
+ data.put("groupId", member.getGroupId());
|
|
|
|
|
+ data.put("profileId", profile.getId());
|
|
|
|
|
+ data.put("appUserId", member.getAppUserId());
|
|
|
|
|
+ data.put("status", member.getStatus());
|
|
|
|
|
+ data.put("joinTime", member.getJoinTime());
|
|
|
|
|
+ data.put("profileComplete", isProfileComplete(profile));
|
|
|
|
|
+ data.putAll(buildProfileMap(profile));
|
|
|
|
|
+ data.put("profile", buildProfileMap(profile));
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> buildProfileMap(UserProfile profile) {
|
|
|
|
|
+ Map<String, Object> data = new HashMap<>(32);
|
|
|
|
|
+ if (profile == null) {
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+ data.put("profileId", profile.getId());
|
|
|
|
|
+ data.put("id", profile.getId());
|
|
|
|
|
+ data.put("appUserId", profile.getUserId());
|
|
|
|
|
+ data.put("name", profile.getName());
|
|
|
|
|
+ data.put("gender", profile.getGender());
|
|
|
|
|
+ data.put("age", profile.getAge());
|
|
|
|
|
+ data.put("phone", profile.getPhone());
|
|
|
|
|
+ data.put("userType", profile.getUserType());
|
|
|
|
|
+ data.put("provinceCode", profile.getProvinceCode());
|
|
|
|
|
+ data.put("cityCode", profile.getCityCode());
|
|
|
|
|
+ data.put("districtCode", profile.getDistrictCode());
|
|
|
|
|
+ data.put("address", profile.getAddress());
|
|
|
|
|
+ data.put("shoulderWidth", profile.getShoulderWidth());
|
|
|
|
|
+ data.put("bodyHeight", profile.getBodyHeight());
|
|
|
|
|
+ data.put("spineLength", profile.getSpineLength());
|
|
|
|
|
+ data.put("height", profile.getHeight());
|
|
|
|
|
+ data.put("weight", profile.getWeight());
|
|
|
|
|
+ data.put("fingerWidth1", profile.getFingerWidth1());
|
|
|
|
|
+ data.put("fingerWidth15", profile.getFingerWidth15());
|
|
|
|
|
+ data.put("fingerWidth3", profile.getFingerWidth3());
|
|
|
|
|
+ data.put("acupointTableId", profile.getAcupointTableId());
|
|
|
|
|
+ data.put("profileComplete", isProfileComplete(profile));
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> buildCurrentProfileMap(AppUser appUser, UserProfile profile) {
|
|
|
|
|
+ Map<String, Object> data = buildProfileMap(profile);
|
|
|
|
|
+ data.put("userId", appUser.getId());
|
|
|
|
|
+ data.put("appUserId", appUser.getId());
|
|
|
|
|
+ data.put("nickname", appUser.getNickname());
|
|
|
|
|
+ data.put("avatar", appUser.getAvatar());
|
|
|
|
|
+ data.put("accountPhone", appUser.getPhone());
|
|
|
|
|
+ data.put("phone", profile.getPhone());
|
|
|
|
|
+ data.put("appId", appUser.getAppId());
|
|
|
|
|
+ data.put("userCategoryId", appUser.getUserCategoryId());
|
|
|
|
|
+ return data;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean isFamilyDevice(Device device) {
|
|
|
|
|
+ return resolveDeviceType(device) == DEVICE_TYPE_FAMILY;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean isPublicDevice(Device device) {
|
|
|
|
|
+ return resolveDeviceType(device) == DEVICE_TYPE_PUBLIC;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int resolveDeviceType(Device device) {
|
|
|
|
|
+ return device.getDeviceType() == null ? DEVICE_TYPE_FAMILY : device.getDeviceType();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String normalizePhone(String phone) {
|
|
|
|
|
+ String value = trimToNull(phone);
|
|
|
|
|
+ if (value != null) {
|
|
|
|
|
+ validatePhone(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validatePhone(String phone) {
|
|
|
|
|
+ if (phone != null && !phone.matches(PHONE_PATTERN)) {
|
|
|
|
|
+ throw new BusinessException(ResultCode.PHONE_FORMAT_INVALID);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String trimToNull(String value) {
|
|
|
|
|
+ if (!StringUtils.hasText(value)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return value.trim();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String resolveProfileName(String preferredName, String phone, Long fallbackId) {
|
|
|
|
|
+ String name = trimToNull(preferredName);
|
|
|
|
|
+ if (name != null) {
|
|
|
|
|
+ return name;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (phone != null && phone.length() >= 4) {
|
|
|
|
|
+ return "用户" + phone.substring(phone.length() - 4);
|
|
|
|
|
+ }
|
|
|
|
|
+ return "用户" + (fallbackId == null ? "" : fallbackId);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|