UserService.java
10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
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("未实现");
}
}