UserService.java 10.9 KB
package com.aigeo.company.service;

import com.aigeo.common.enums.UserRole;
import com.aigeo.common.exception.BusinessException;
import com.aigeo.common.result.ResultCode;
import com.aigeo.company.controller.UserController;
import com.aigeo.company.entity.User;
import com.aigeo.company.repository.CompanyRepository;
import com.aigeo.company.repository.UserRepository;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.Clock;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * 用户服务层
 *
 * @author AIGEO Team
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {

    private final UserRepository userRepository;
    private final CompanyRepository companyRepository;
    private final PasswordEncoder passwordEncoder;
    private final Clock clock = Clock.systemDefaultZone();

    /**
     * 分页查询用户列表
     */
    public Page<User> getUserPage(Integer companyId, Pageable pageable) {
        if (companyId != null) {
            return userRepository.findByCompanyIdOrderByCreatedAtDesc(companyId, pageable);
        }
        return userRepository.findAll(pageable);
    }

    /**
     * 根据公司ID获取用户列表
     */
    public List<User> getUsersByCompanyId(Integer companyId) {
        return userRepository.findByCompanyIdAndIsActiveTrue(companyId);
    }

    /**
     * 根据ID获取用户
     */
    public User getUserById(Integer id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_FOUND));
    }

    /**
     * 根据用户名获取用户
     */
    public User getUserByUsername(String username) {
        if (username == null || username.isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "用户名不能为空");
        }
        return userRepository.findByUsername(username)
                .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_FOUND));
    }

    /**
     * 根据邮箱获取用户
     */
    public User getUserByEmail(String email) {
        if (email == null || email.isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "邮箱不能为空");
        }
        return userRepository.findByEmail(email)
                .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_FOUND));
    }

    /**
     * 创建用户
     */
    @Transactional
    public User createUser(User user) {
        // 验证公司存在
        var companyOpt = companyRepository.findById(user.getCompanyId());
        if (companyOpt.isEmpty()) {
            throw new BusinessException(ResultCode.COMPANY_NOT_FOUND);
        }
        var company = companyOpt.get();

        // 验证用户名唯一性
        if (userRepository.existsByUsername(user.getUsername())) {
            throw new BusinessException(ResultCode.USERNAME_EXISTS);
        }

        // 验证邮箱唯一性
        if (userRepository.existsByEmail(user.getEmail())) {
            throw new BusinessException(ResultCode.EMAIL_EXISTS);
        }

        // 检查公司用户数量限制
        long userCount = userRepository.countActiveUsersByCompanyId(user.getCompanyId());
        if (userCount >= company.getMaxUsers()) {
            throw new BusinessException(ResultCode.QUOTA_EXCEEDED, "用户数量已达上限");
        }

        // 加密密码
        if (StringUtils.hasText(user.getPasswordHash())) {
            user.setPasswordHash(passwordEncoder.encode(user.getPasswordHash()));
        }

        User savedUser = userRepository.save(user);
        log.info("创建用户成功: {}", savedUser.getUsername());
        return savedUser;
    }

    /**
     * 更新用户信息
     */
    @Transactional
    public User updateUser(Integer id, User userUpdate) {
        User existingUser = getUserById(id);

        // 验证用户名唯一性(排除自己)
        if (!existingUser.getUsername().equals(userUpdate.getUsername()) &&
                userRepository.existsByUsername(userUpdate.getUsername())) {
            throw new BusinessException(ResultCode.USERNAME_EXISTS);
        }

        // 验证邮箱唯一性(排除自己)
        if (!existingUser.getEmail().equals(userUpdate.getEmail()) &&
                userRepository.existsByEmail(userUpdate.getEmail())) {
            throw new BusinessException(ResultCode.EMAIL_EXISTS);
        }

        // 更新字段
        existingUser.setEmail(userUpdate.getEmail());
        existingUser.setFullName(userUpdate.getFullName());
        existingUser.setAvatarUrl(userUpdate.getAvatarUrl());
        existingUser.setRole(userUpdate.getRole());

        User savedUser = userRepository.save(existingUser);
        log.info("更新用户信息成功: {}", savedUser.getUsername());
        return savedUser;
    }

    /**
     * 更新用户密码
     */
    @Transactional
    public void updatePassword(Integer id, String newPassword) {
        User user = getUserById(id);
        user.setPasswordHash(passwordEncoder.encode(newPassword));
        userRepository.save(user);
        log.info("更新用户密码成功: {}", user.getUsername());
    }

    /**
     * 更新用户状态
     */
    @Transactional
    public User updateUserStatus(Integer id, Boolean isActive) {
        User user = getUserById(id);

        // 检查是否尝试禁用最后一个管理员
        if (!isActive && user.isAdmin() && !userRepository.hasAdminUserExcluding(user.getCompanyId(), user.getId())) {
            throw new BusinessException(ResultCode.BUSINESS_ERROR, "不能禁用最后一个管理员用户");
        }

        user.setIsActive(isActive);
        User savedUser = userRepository.save(user);
        log.info("更新用户状态成功: {} -> {}", savedUser.getUsername(), isActive ? "启用" : "禁用");
        return savedUser;
    }

    /**
     * 更新用户角色
     */
    @Transactional
    public User updateUserRole(Integer id, UserRole role) {
        User user = getUserById(id);

        // 检查是否尝试降级最后一个管理员
        if (user.isAdmin() && !UserRole.ADMIN.equals(role) &&
                !userRepository.hasAdminUserExcluding(user.getCompanyId(), user.getId())) {
            throw new BusinessException(ResultCode.BUSINESS_ERROR, "不能降级最后一个管理员用户");
        }

        user.setRole(role);
        User savedUser = userRepository.save(user);
        log.info("更新用户角色成功: {} -> {}", savedUser.getUsername(), role);
        return savedUser;
    }

    /**
     * 更新最后登录时间
     */
    @Transactional
    public void updateLastLoginTime(Integer id) {
        User user = getUserById(id);
        user.updateLastLoginTime();
        userRepository.save(user);
    }

    /**
     * 删除用户(软删除)
     */
    @Transactional
    public void deleteUser(Integer id) {
        User user = getUserById(id);

        // 检查是否尝试删除最后一个管理员
        if (user.isAdmin() && !userRepository.hasAdminUserExcluding(user.getCompanyId(), user.getId())) {
            throw new BusinessException(ResultCode.BUSINESS_ERROR, "不能删除最后一个管理员用户");
        }

        user.setIsActive(false);
        userRepository.save(user);
        log.info("删除用户成功: {}", user.getUsername());
    }

    /**
     * 验证密码
     */
    public boolean validatePassword(Integer id, String password) {
        User user = getUserById(id);
        if (user.getPasswordHash() == null) {
            return false;
        }
        return passwordEncoder.matches(password, user.getPasswordHash());
    }

    /**
     * 检查用户名是否可用
     */
    public boolean isUsernameAvailable(String username) {
        if (username == null || username.isEmpty()) {
            return false;
        }
        return !userRepository.existsByUsername(username);
    }

    /**
     * 检查邮箱是否可用
     */
    public boolean isEmailAvailable(String email) {
        if (email == null || email.isEmpty()) {
            return false;
        }
        return !userRepository.existsByEmail(email);
    }

    /**
     * 获取最近登录的用户
     */
    public List<User> getRecentlyActiveUsers(Integer companyId, int days) {
        LocalDateTime since = LocalDateTime.now(clock).minusDays(days);
        return userRepository.findByCompanyIdAndLastLoginAfter(companyId, since);
    }

    // 以下为未实现方法,建议补充实现或抛出异常
    public Page<User> searchUsers(Integer companyId, String username, String email, String role, Boolean isActive, Pageable pageable) {
        throw new UnsupportedOperationException("未实现");
    }

    public List<User> getAllUsers() {
        throw new UnsupportedOperationException("未实现");
    }

    public List<User> getActiveUsers(Integer companyId) {
        throw new UnsupportedOperationException("未实现");
    }

    public boolean existsByUsername(@NotBlank(message = "用户名不能为空") String username) {
        if (username == null || username.isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "用户名不能为空");
        }
        return userRepository.existsByUsername(username);
    }

    public boolean existsByEmail(@NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") String email) {
        if (email == null || email.isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "邮箱不能为空");
        }
        return userRepository.existsByEmail(email);
    }

    public User saveUser(@Valid User user) {
        throw new UnsupportedOperationException("未实现");
    }

    public int batchUpdateStatus(@NotNull List<Integer> ids, @NotNull Boolean isActive) {
        throw new UnsupportedOperationException("未实现");
    }

    public String resetPassword(@NotNull Integer id) {
        throw new UnsupportedOperationException("未实现");
    }

    public boolean existsById(@NotNull Integer id) {
        return userRepository.existsById(id);
    }

    public boolean hasAssociatedData(@NotNull Integer id) {
        throw new UnsupportedOperationException("未实现");
    }

    public List<User> searchByKeyword(@NotNull String keyword, Integer companyId, int limit) {
        throw new UnsupportedOperationException("未实现");
    }

    public UserController.UserStatistics getUserStatistics(Integer companyId) {
        throw new UnsupportedOperationException("未实现");
    }
}