package com.tcm.training.admin.user.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.tcm.training.admin.common.PageResponse; import com.tcm.training.admin.user.dto.AccountCreateRequest; import com.tcm.training.admin.user.dto.AccountResetPasswordRequest; import com.tcm.training.admin.user.dto.AccountStatusRequest; import com.tcm.training.admin.user.dto.AccountUpdateRequest; import com.tcm.training.admin.user.entity.SysRole; import com.tcm.training.admin.user.entity.SysUser; import com.tcm.training.admin.user.entity.SysUserRole; import com.tcm.training.admin.user.mapper.SysRoleMapper; import com.tcm.training.admin.user.mapper.SysUserMapper; import com.tcm.training.admin.user.mapper.SysUserRoleMapper; import com.tcm.training.admin.user.row.AccountDetailRow; import com.tcm.training.admin.user.row.AccountListRow; import com.tcm.training.admin.user.vo.AccountDetailVO; import com.tcm.training.admin.user.vo.AccountListItemVO; import com.tcm.training.admin.user.vo.AccountStatusVO; import com.tcm.training.admin.user.vo.RoleOptionVO; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; @Service public class AccountAppService { private static final Pattern PASSWORD_PATTERN = Pattern.compile( "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z\\d]).{12,64}$" ); private final SysUserMapper sysUserMapper; private final SysRoleMapper sysRoleMapper; private final SysUserRoleMapper sysUserRoleMapper; private final BCryptPasswordEncoder passwordEncoder; public AccountAppService( SysUserMapper sysUserMapper, SysRoleMapper sysRoleMapper, SysUserRoleMapper sysUserRoleMapper, BCryptPasswordEncoder passwordEncoder ) { this.sysUserMapper = sysUserMapper; this.sysRoleMapper = sysRoleMapper; this.sysUserRoleMapper = sysUserRoleMapper; this.passwordEncoder = passwordEncoder; } public PageResponse page( int page, int pageSize, String realName, String phone, Long roleId, Integer status ) { IPage records = sysUserMapper.selectAccountPage( new Page(page, pageSize), realName, phone, roleId, status ); List list = new ArrayList(); for (AccountListRow row : records.getRecords()) { AccountListItemVO vo = new AccountListItemVO(); vo.setId(row.getId()); vo.setRealName(row.getRealName()); vo.setUsername(row.getUsername()); vo.setPhone(row.getPhone()); vo.setRoleId(row.getRoleId()); vo.setRoleName(row.getRoleName()); vo.setStatus(row.getStatus()); vo.setCreatedAt(row.getCreatedAt()); list.add(vo); } return PageResponse.of(list, records.getTotal(), records.getCurrent(), records.getSize()); } public AccountDetailVO detail(Long id) { AccountDetailRow row = sysUserMapper.selectAccountDetail(id); if (row == null) { throw new IllegalArgumentException("帐号不存在"); } return toDetailVO(row); } @Transactional public AccountDetailVO create(AccountCreateRequest req) { validatePassword(req.getInitialPassword()); String username = req.getUsername().trim(); String phone = req.getPhone().trim(); ensureUsernameUnique(username, null); ensurePhoneUnique(phone, null); SysRole role = requireRole(req.getRoleId()); SysUser user = new SysUser(); user.setUsername(username); user.setNickname(req.getRealName().trim()); user.setRealName(req.getRealName().trim()); user.setPhone(phone); user.setStatus(req.getStatus()); user.setDeleted(0); user.setPassword(passwordEncoder.encode(req.getInitialPassword())); user.setCreatedAt(LocalDateTime.now()); user.setUpdatedAt(LocalDateTime.now()); sysUserMapper.insert(user); bindSingleRole(user.getId(), role.getId()); return detail(user.getId()); } @Transactional public AccountDetailVO update(Long id, AccountUpdateRequest req) { SysUser user = requireUser(id); String phone = req.getPhone().trim(); ensurePhoneUnique(phone, id); SysRole role = requireRole(req.getRoleId()); user.setNickname(req.getRealName().trim()); user.setRealName(req.getRealName().trim()); user.setPhone(phone); user.setStatus(req.getStatus()); sysUserMapper.updateById(user); bindSingleRole(id, role.getId()); return detail(id); } @Transactional public AccountStatusVO patchStatus(Long id, AccountStatusRequest req) { SysUser user = requireUser(id); user.setStatus(req.getStatus()); sysUserMapper.updateById(user); return new AccountStatusVO(id, req.getStatus()); } @Transactional public void resetPassword(Long id, AccountResetPasswordRequest req) { SysUser user = requireUser(id); if (!req.getNewPassword().equals(req.getConfirmPassword())) { throw new IllegalArgumentException("两次密码输入不一致"); } validatePassword(req.getNewPassword()); user.setPassword(passwordEncoder.encode(req.getNewPassword())); sysUserMapper.updateById(user); } @Transactional public void delete(Long id) { SysUser user = requireUser(id); user.setStatus(0); sysUserMapper.updateById(user); sysUserMapper.deleteById(id); sysUserRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getUserId, id)); } public List listRoleOptions() { List roles = sysRoleMapper.selectList(new LambdaQueryWrapper() .eq(SysRole::getDeleted, 0) .eq(SysRole::getStatus, 1) .orderByAsc(SysRole::getId)); List list = new ArrayList(); for (SysRole role : roles) { list.add(new RoleOptionVO(role.getId(), role.getRoleName(), role.getRoleCode())); } return list; } private SysUser requireUser(Long id) { SysUser user = sysUserMapper.selectOne(new LambdaQueryWrapper() .eq(SysUser::getId, id) .eq(SysUser::getDeleted, 0) .last("limit 1")); if (user == null) { throw new IllegalArgumentException("帐号不存在"); } return user; } private SysRole requireRole(Long roleId) { SysRole role = sysRoleMapper.selectOne(new LambdaQueryWrapper() .eq(SysRole::getId, roleId) .eq(SysRole::getDeleted, 0) .eq(SysRole::getStatus, 1) .last("limit 1")); if (role == null) { throw new IllegalArgumentException("所属角色不存在或已禁用"); } return role; } private void ensureUsernameUnique(String username, Long excludeId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(SysUser::getUsername, username); if (excludeId != null) { wrapper.ne(SysUser::getId, excludeId); } SysUser exists = sysUserMapper.selectOne(wrapper.last("limit 1")); if (exists != null) { throw new IllegalArgumentException("登录帐号已存在"); } } private void ensurePhoneUnique(String phone, Long excludeId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(SysUser::getPhone, phone); if (excludeId != null) { wrapper.ne(SysUser::getId, excludeId); } SysUser exists = sysUserMapper.selectOne(wrapper.last("limit 1")); if (exists != null) { throw new IllegalArgumentException("联系电话已被占用"); } } private void bindSingleRole(Long userId, Long roleId) { sysUserRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getUserId, userId)); SysUserRole relation = new SysUserRole(); relation.setUserId(userId); relation.setRoleId(roleId); relation.setCreatedAt(LocalDateTime.now()); sysUserRoleMapper.insert(relation); } private void validatePassword(String password) { if (!PASSWORD_PATTERN.matcher(password).matches()) { throw new IllegalArgumentException("密码需至少12位,且包含大小写字母、数字和特殊字符"); } } private AccountDetailVO toDetailVO(AccountDetailRow row) { AccountDetailVO vo = new AccountDetailVO(); vo.setId(row.getId()); vo.setRealName(row.getRealName()); vo.setUsername(row.getUsername()); vo.setPhone(row.getPhone()); vo.setRoleId(row.getRoleId()); vo.setRoleName(row.getRoleName()); vo.setStatus(row.getStatus()); vo.setCreatedAt(row.getCreatedAt()); vo.setUpdatedAt(row.getUpdatedAt()); return vo; } }