| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.tcm.training.admin.common.DateTimes; |
| | | import com.tcm.training.admin.common.PageResponse; |
| | | import com.tcm.training.admin.common.PhoneMasker; |
| | | import com.tcm.training.admin.live.entity.Clinic; |
| | | import com.tcm.training.admin.member.entity.AppUser; |
| | | import com.tcm.training.admin.live.mapper.ClinicMapper; |
| | | import com.tcm.training.admin.member.excel.MemberUserExcelRow; |
| | | import com.tcm.training.admin.member.dto.UserBatchClinicUpdateRequest; |
| | | import com.tcm.training.admin.member.dto.UserResetPasswordRequest; |
| | | import com.tcm.training.admin.member.dto.UserStatusRequest; |
| | | import com.tcm.training.admin.member.dto.UserUpdateRequest; |
| | | import com.tcm.training.admin.member.entity.AppUser; |
| | | import com.tcm.training.admin.member.excel.MemberUserExcelRow; |
| | | import com.tcm.training.admin.member.excel.UserExcelRow; |
| | | import com.tcm.training.admin.member.mapper.UserMapper; |
| | | import com.tcm.training.admin.member.row.UserDetailRow; |
| | | import com.tcm.training.admin.member.row.UserListRow; |
| | | import com.tcm.training.admin.member.vo.ClinicInUserVO; |
| | | import com.tcm.training.admin.member.vo.ClinicOptionVO; |
| | | import com.tcm.training.admin.member.vo.UserDetailVO; |
| | | import com.tcm.training.admin.member.vo.UserListItemVO; |
| | | import com.tcm.training.admin.member.vo.UserStatusVO; |
| | | import com.tcm.training.admin.member.vo.WechatBindingVO; |
| | | import com.tcm.training.admin.member.vo.*; |
| | | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | this.passwordEncoder = passwordEncoder; |
| | | } |
| | | |
| | | /** 分页查询用户。 */ |
| | | public PageResponse<UserListItemVO> page(int page, |
| | | int pageSize, |
| | | String nickname, |
| | |
| | | UserListItemVO item = new UserListItemVO(); |
| | | item.setId(row.getId()); |
| | | item.setNickname(row.getNickname()); |
| | | item.setPhone(PhoneMasker.mask(row.getPhone())); |
| | | item.setPhone(row.getPhone()); |
| | | item.setAvatar(row.getAvatar()); |
| | | item.setType(row.getType()); |
| | | item.setClinicName(row.getClinicName()); |
| | |
| | | return PageResponse.of(list, result.getTotal(), result.getCurrent(), result.getSize()); |
| | | } |
| | | |
| | | /** 查询用户详情。 */ |
| | | public UserDetailVO detail(Long id) { |
| | | UserDetailRow row = userMapper.selectUserDetail(id); |
| | | if (row == null) { |
| | |
| | | return vo; |
| | | } |
| | | |
| | | @Transactional |
| | | @Transactional(rollbackFor = Exception.class) |
| | | /** 更新用户信息。 */ |
| | | public UserDetailVO update(Long id, UserUpdateRequest req) { |
| | | AppUser user = requireUser(id); |
| | | if (req.getClinicId() != null) { |
| | | Clinic clinic = clinicMapper.selectById(req.getClinicId()); |
| | | if (clinic == null || (clinic.getDeleted() != null && clinic.getDeleted() == 1)) { |
| | | throw new IllegalArgumentException("绑定诊所不存在"); |
| | | } |
| | | } |
| | | boolean personalUser = Integer.valueOf(0).equals(user.getType()); |
| | | Long clinicUserId = personalUser ? resolveClinicUserId(req.getClinicId()) : null; |
| | | AppUser phoneConflict = userMapper.selectOne(new LambdaQueryWrapper<AppUser>() |
| | | .eq(AppUser::getPhone, req.getPhone().trim()) |
| | | .ne(AppUser::getId, id) |
| | |
| | | user.setPhone(req.getPhone().trim()); |
| | | user.setAvatar(trimToNull(req.getAvatar())); |
| | | user.setStatus(req.getStatus()); |
| | | user.setClinicId(req.getClinicId()); |
| | | user.setMemberExpireAt(req.getMemberExpireAt()); |
| | | userMapper.updateById(user); |
| | | if (personalUser) { |
| | | updateClinicBinding(java.util.Collections.singletonList(id), req.getClinicId(), clinicUserId); |
| | | } |
| | | return detail(id); |
| | | } |
| | | |
| | | @Transactional |
| | | @Transactional(rollbackFor = Exception.class) |
| | | /** 修改用户状态。 */ |
| | | public UserStatusVO patchStatus(Long id, UserStatusRequest req) { |
| | | AppUser user = requireUser(id); |
| | | user.setStatus(req.getStatus()); |
| | |
| | | return new UserStatusVO(id, req.getStatus()); |
| | | } |
| | | |
| | | @Transactional |
| | | @Transactional(rollbackFor = Exception.class) |
| | | /** 重置用户密码。 */ |
| | | public void resetPassword(Long id, UserResetPasswordRequest req) { |
| | | AppUser user = requireUser(id); |
| | | if (!req.getNewPassword().equals(req.getConfirmPassword())) { |
| | |
| | | userMapper.updateById(user); |
| | | } |
| | | |
| | | @Transactional |
| | | @Transactional(rollbackFor = Exception.class) |
| | | /** 删除用户。 */ |
| | | public void delete(Long id) { |
| | | requireUser(id); |
| | | userMapper.deleteById(id); |
| | | } |
| | | |
| | | @Transactional |
| | | @Transactional(rollbackFor = Exception.class) |
| | | /** 解除用户微信绑定。 */ |
| | | public UserDetailVO unbindWechat(Long id) { |
| | | requireUser(id); |
| | | userMapper.update(null, new LambdaUpdateWrapper<AppUser>() |
| | |
| | | return detail(id); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | /** 批量更新用户诊所归属。 */ |
| | | public void batchUpdateClinic(UserBatchClinicUpdateRequest req) { |
| | | Long clinicUserId = resolveClinicUserId(req.getClinicId()); |
| | | Long userCount = userMapper.selectCount(new LambdaQueryWrapper<AppUser>() |
| | | .in(AppUser::getId, req.getUserIds()) |
| | | .eq(AppUser::getType, 0) |
| | | .eq(AppUser::getDeleted, 0)); |
| | | if (userCount != req.getUserIds().size()) { |
| | | throw new IllegalArgumentException("部分用户不存在或不是普通用户"); |
| | | } |
| | | updateClinicBinding(req.getUserIds(), req.getClinicId(), clinicUserId); |
| | | } |
| | | |
| | | /** 诊所 ID 与诊所账号用户 ID 不同,患者查询依赖后者。 */ |
| | | private Long resolveClinicUserId(Long clinicId) { |
| | | if (clinicId == null) { |
| | | return null; |
| | | } |
| | | Clinic clinic = clinicMapper.selectById(clinicId); |
| | | if (clinic == null || Integer.valueOf(1).equals(clinic.getDeleted())) { |
| | | throw new IllegalArgumentException("绑定诊所不存在"); |
| | | } |
| | | List<AppUser> clinicUsers = userMapper.selectList(new LambdaQueryWrapper<AppUser>() |
| | | .eq(AppUser::getClinicId, clinicId) |
| | | .eq(AppUser::getType, 1) |
| | | .eq(AppUser::getDeleted, 0) |
| | | .last("limit 2")); |
| | | if (clinicUsers.isEmpty()) { |
| | | throw new IllegalArgumentException("目标诊所尚未关联诊所账号,无法绑定患者"); |
| | | } |
| | | if (clinicUsers.size() > 1) { |
| | | throw new IllegalArgumentException("目标诊所关联多个账号,请先确认患者归属"); |
| | | } |
| | | return clinicUsers.get(0).getId(); |
| | | } |
| | | |
| | | /** 同步两个归属字段;显式 set 确保解除绑定时 null 也能写入。 */ |
| | | private void updateClinicBinding(List<Long> userIds, Long clinicId, Long clinicUserId) { |
| | | userMapper.update(null, new LambdaUpdateWrapper<AppUser>() |
| | | .in(AppUser::getId, userIds) |
| | | .eq(AppUser::getType, 0) |
| | | .eq(AppUser::getDeleted, 0) |
| | | .set(AppUser::getClinicId, clinicId) |
| | | .set(AppUser::getSuperiorClinicUserId, clinicUserId)); |
| | | } |
| | | |
| | | /** 查询诊所选项。 */ |
| | | public List<ClinicOptionVO> listClinicOptions() { |
| | | List<Clinic> clinics = clinicMapper.selectList(new LambdaQueryWrapper<Clinic>() |
| | | .eq(Clinic::getDeleted, 0) |
| | |
| | | MemberUserExcelRow excel = new MemberUserExcelRow(); |
| | | excel.setId(row.getId()); |
| | | excel.setNickname(row.getNickname()); |
| | | excel.setPhone(PhoneMasker.mask(row.getPhone())); |
| | | excel.setPhone(row.getPhone()); |
| | | excel.setMemberStatus(row.getIsMember() != null && row.getIsMember() == 1 ? "生效中" : "已失效"); |
| | | excel.setMemberExpireAt(DateTimes.format(row.getMemberExpireAt())); |
| | | memberExcelRows.add(excel); |
| | |
| | | excel.setId(row.getId()); |
| | | excel.setNickname(row.getNickname()); |
| | | excel.setType(typeText(row.getType())); |
| | | excel.setPhone(PhoneMasker.mask(row.getPhone())); |
| | | excel.setPhone(row.getPhone()); |
| | | excel.setClinicName(row.getClinicName() == null ? "-" : row.getClinicName()); |
| | | excel.setAge(row.getAge()); |
| | | excel.setGender(genderText(row.getGender())); |