blob: 99675914f9ddc81c7e724f100c74b959c1432736 [file] [log] [blame]
22301115cf6dba22025-03-25 19:06:21 +08001package com.example.myproject.service;
Jin572111b2025-06-09 23:04:12 +08002import cn.dev33.satoken.stp.StpUtil;
JinGefe5140c2025-06-06 20:07:42 +08003import com.example.myproject.entity.FriendRelation;
22301138b2533412025-06-05 00:18:58 +08004import com.example.myproject.entity.User;
223011385e9c35a2025-06-04 15:52:45 +08005import com.example.myproject.entity.Users;
6import com.example.myproject.entity.UserInviteCode;
JinGefe5140c2025-06-06 20:07:42 +08007import com.example.myproject.repository.FriendRelationRepository;
Jinf50fba62025-06-09 22:47:24 +08008import com.example.myproject.repository.LoginLogRepository;
223011385e9c35a2025-06-04 15:52:45 +08009import com.example.myproject.repository.UserRepository;
10import com.example.myproject.repository.UserInviteCodeRepository;
Krishyab0cc1882025-06-09 10:54:09 +080011import jakarta.transaction.Transactional;
223011385e9c35a2025-06-04 15:52:45 +080012import org.springframework.beans.factory.annotation.Autowired;
13import org.springframework.stereotype.Service;
22301138b2533412025-06-05 00:18:58 +080014import org.springframework.web.multipart.MultipartFile;
Jin572111b2025-06-09 23:04:12 +080015import lombok.RequiredArgsConstructor;
22301138b2533412025-06-05 00:18:58 +080016import javax.servlet.http.HttpServletRequest;
17import java.io.File;
18import java.io.IOException;
Jinf50fba62025-06-09 22:47:24 +080019import java.math.BigDecimal;
22301138b2533412025-06-05 00:18:58 +080020import java.nio.file.Files;
21import java.nio.file.Path;
22import java.nio.file.Paths;
Krishyab0cc1882025-06-09 10:54:09 +080023import java.time.LocalDate;
22301138b2533412025-06-05 00:18:58 +080024import java.time.LocalDateTime;
Krishyab0cc1882025-06-09 10:54:09 +080025import java.time.ZoneId;
223011385e9c35a2025-06-04 15:52:45 +080026import java.util.*;
Jin572111b2025-06-09 23:04:12 +080027import java.util.stream.Collectors;
223011385e9c35a2025-06-04 15:52:45 +080028@Service
Jin572111b2025-06-09 23:04:12 +080029@RequiredArgsConstructor
223011385e9c35a2025-06-04 15:52:45 +080030public class UserService {
31
32 @Autowired
33 private UserRepository userRepository;
34
35 @Autowired
36 private UserInviteCodeRepository userInviteCodeRepository;
37
JinGefe5140c2025-06-06 20:07:42 +080038 @Autowired
39 private FriendRelationRepository friendRelationRepository;
Jinf50fba62025-06-09 22:47:24 +080040 private final LoginLogRepository loginLogRepository;
41
Jin572111b2025-06-09 23:04:12 +080042// public UserService(LoginLogRepository loginLogRepository) {
43// this.loginLogRepository = loginLogRepository;
44// }
JinGefe5140c2025-06-06 20:07:42 +080045
223011385e9c35a2025-06-04 15:52:45 +080046 // 生成邀请码
47 public Map<String, Object> generateInviteCode(Long userId) {
48 // 获取用户信息
49 Users user = userRepository.findById(userId).orElse(null);
50
51 // 如果用户不存在,返回错误
52 if (user == null) {
53 Map<String, Object> errorResponse = new HashMap<>();
54 errorResponse.put("status", "failure");
55 errorResponse.put("message", "用户不存在");
56 return errorResponse;
57 }
58
59 // 检查用户的邀请数量
60 if (user.getInviteCount() <= 0) {
61 Map<String, Object> errorResponse = new HashMap<>();
62 errorResponse.put("status", "failure");
63 errorResponse.put("message", "没有剩余的邀请码");
64 return errorResponse;
65 }
66
67 // 生成唯一的邀请码
68 String inviteCode = generateUniqueInviteCode();
69
70 // 将邀请码保存到 `user_invite_code` 表
71 UserInviteCode userInviteCode = new UserInviteCode();
72 userInviteCode.setUserId(userId);
73 userInviteCode.setInviteCode(inviteCode);
74 userInviteCode.setCreatedAt(java.time.LocalDateTime.now());
75
76 userInviteCodeRepository.save(userInviteCode);
77
78 // 更新用户的 `invite_count`,减少1
79 user.setInviteCount(user.getInviteCount() - 1);
80 userRepository.save(user);
81
82 // 返回成功信息
83 Map<String, Object> response = new HashMap<>();
84 response.put("status", "success");
85 response.put("message", "邀请码生成成功");
86 response.put("invite_code", inviteCode);
87
88 return response;
89 }
90
91 // 生成唯一的邀请码,使用26个字母(大小写)
92 private String generateUniqueInviteCode() {
93 String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
94 StringBuilder inviteCode = new StringBuilder();
95
96 Random random = new Random();
97 for (int i = 0; i < 10; i++) {
98 inviteCode.append(characters.charAt(random.nextInt(characters.length())));
99 }
100
101 return inviteCode.toString();
102 }
103
104 public String registerUser(String username, String email, String password, String role, String inviteCode) {
105 // 检查邮箱是否已经注册
106 Optional<Users> existingEmailUser = userRepository.findByEmail(email);
107 if (existingEmailUser.isPresent()) {
108 return "该邮箱已被注册";
109 }
110
111 // 检查用户名是否已经存在
112 Optional<Users> existingUsernameUser = userRepository.findByUsername(username); // 需要根据用户名查询
113 if (existingUsernameUser.isPresent()) {
114 return "该用户名已被注册";
115 }
116
117 // 检查邀请码是否有效
118 if (inviteCode == null || inviteCode.isEmpty()) {
119 return "邀请码不能为空";
120 }
121
122 Optional<UserInviteCode> invite = userInviteCodeRepository.findByInviteCode(inviteCode);
123 if (invite.isEmpty() || invite.get().getIsUsed()) {
124 return "邀请码无效或已被使用";
125 }
126
Jinf50fba62025-06-09 22:47:24 +0800127 // 设置默认等级为1(由于邀请码有效)
128 Long level = 1L;
223011385e9c35a2025-06-04 15:52:45 +0800129
130 // 设置默认头像 URL
131 String avatarUrl = "https://example.com/default-avatar.jpg"; // 默认头像
132
133 // 获取邀请码对应的用户ID
134 UserInviteCode inviteEntity = invite.get();
135 Long inviteUserId = inviteEntity.getUserId();
136
137 // 创建新用户
138 Users newUser = new Users();
139 newUser.setUsername(username);
140 newUser.setEmail(email);
141 newUser.setPassword(password);
142 newUser.setRole(role);
143 newUser.setInviteCount(0); // 初始邀请码数量为 0
144 newUser.setLevel(level);
145 newUser.setAvatarUrl(avatarUrl); // 设置默认头像
146 newUser.setRegistrationDate(new java.util.Date()); // 设置注册日期
147 newUser.setCurrentExperience(0); // 默认经验为 0
148 newUser.setCurrentSeedingHours(0f); // 默认做种时长为 0
149 newUser.setRegistrationTime(new java.util.Date()); // 设置注册时间为当前时间
150
151 // 保存用户信息
152 userRepository.save(newUser);
153
JinGefe5140c2025-06-06 20:07:42 +0800154 FriendRelation newFriendRelation = new FriendRelation();
155 newFriendRelation.setUserId(newUser.getUserId());
156 newFriendRelation.setCreateTime(new Date());
157 newFriendRelation.setFriendId(inviteUserId);
158
159 FriendRelation newFriendRelations = new FriendRelation();
160 newFriendRelations.setUserId(inviteUserId);
161 newFriendRelations.setCreateTime(new Date());
162 newFriendRelations.setFriendId(newUser.getUserId());
163 friendRelationRepository.save(newFriendRelation);
164 friendRelationRepository.save(newFriendRelations);
223011385e9c35a2025-06-04 15:52:45 +0800165 // 更新邀请码的使用状态
166 inviteEntity.setIsUsed(true);
167 userInviteCodeRepository.save(inviteEntity);
168
169 return "用户注册成功";
170 }
22301115cf6dba22025-03-25 19:06:21 +0800171
172
223011385e9c35a2025-06-04 15:52:45 +0800173 public String loginUser(String username, String password) {
174 // 检查用户是否存在
175 Optional<Users> userOptional = userRepository.findByUsername(username);
176 if (userOptional.isEmpty()) {
177 return "用户名不存在";
178 }
22301115cf6dba22025-03-25 19:06:21 +0800179
223011385e9c35a2025-06-04 15:52:45 +0800180 Users user = userOptional.get();
22301115cf6dba22025-03-25 19:06:21 +0800181
223011385e9c35a2025-06-04 15:52:45 +0800182 // 检查密码是否正确
183 if (!user.getPassword().equals(password)) {
184 return "密码错误";
185 }
Jin572111b2025-06-09 23:04:12 +0800186 StpUtil.login(user.getUserId());
223011385e9c35a2025-06-04 15:52:45 +0800187 // 登录成功
188 return "登录成功";
189 }
190
191 public String changePassword(Long userId, String oldPassword, String newPassword, String confirmPassword) {
192 // 查找用户
193 Users user = userRepository.findById(userId).orElse(null);
194
195 if (user == null) {
196 return "用户不存在";
197 }
198
199 // 检查旧密码是否正确
200 if (!user.getPassword().equals(oldPassword)) {
201 return "旧密码错误";
202 }
203
204 // 检查新密码和确认密码是否一致
205 if (!newPassword.equals(confirmPassword)) {
206 return "新密码与确认密码不一致";
207 }
208
209 // 更新密码
210 user.setPassword(newPassword);
211 userRepository.save(user);
212
213 return "密码修改成功";
214 }
215
216 // 获取用户个人资料
217 public Map<String, Object> getProfile(Long userId) {
218 Optional<Users> userOptional = userRepository.findById(userId);
219
220 // 如果用户不存在,返回null或者可以抛出异常
221 if (userOptional.isEmpty()) {
222 return null; // 可以返回 null 或者根据需要返回错误信息
223 }
224
225 Users user = userOptional.get();
226
227 // 将需要的字段放入 Map 中
228 Map<String, Object> profile = new LinkedHashMap<>();
229 profile.put("avatarUrl", user.getAvatarUrl());
230 profile.put("username", user.getUsername());
231 profile.put("email", user.getEmail());
232 profile.put("gender", user.getGender());
233 profile.put("description", user.getDescription());
234 profile.put("hobbies", user.getHobbies());
235 profile.put("level", user.getLevel());
236 profile.put("Experience", user.getCurrentExperience());
237 profile.put("uploadCount", user.getUploadCount());
238 profile.put("downloadCount", user.getDownloadCount());
239 profile.put("shareRate", user.getShareRate());
240 profile.put("registrationTime", user.getRegistrationTime());
241
242 return profile;
243 }
244
245 // 修改用户个人资料
22301138b2533412025-06-05 00:18:58 +0800246 public boolean editProfile(Long userId, String nickname, String gender, String description, String hobbies) {
223011385e9c35a2025-06-04 15:52:45 +0800247 Optional<Users> userOptional = userRepository.findById(userId);
248
249 // 如果用户不存在,返回false
250 if (userOptional.isEmpty()) {
251 return false; // 用户不存在
252 }
253
254 Users user = userOptional.get();
255
256 // 更新用户资料,只有传入值才会更新对应字段
223011385e9c35a2025-06-04 15:52:45 +0800257 if (nickname != null) {
258 user.setUsername(nickname);
259 }
260 if (gender != null) {
261 user.setGender(gender);
262 }
263 if (description != null) {
264 user.setDescription(description);
265 }
266 if (hobbies != null) {
267 user.setHobbies(hobbies);
268 }
269
270 // 保存更新后的用户信息
271 userRepository.save(user);
272
273 return true; // 更新成功
274 }
275
276 public Map<String, Object> calculateShareRate(Long userId) {
277 // 查找用户
278 Users user = userRepository.findById(userId).orElse(null);
279 if (user == null) {
280 return Map.of("status", "error", "message", "用户不存在");
281 }
282
283 // 获取上传量和下载量
284 Float uploadCount = user.getUploadCount();
285 Float downloadCount = user.getDownloadCount();
286
287 // 计算分享率
288 Float shareRate = 0f; // 默认分享率为0
289 if (downloadCount != 0) {
290 shareRate = uploadCount / downloadCount; // 分享率 = 上传量 / 下载量
291 }
292
293 // 更新用户的分享率
294 user.setShareRate(shareRate);
Krishyab0cc1882025-06-09 10:54:09 +0800295 user.setLastupdatetime(new Date());
296
223011385e9c35a2025-06-04 15:52:45 +0800297 userRepository.save(user);
298
299 // 返回结果
300 return Map.of("status", "success", "message", "分享率计算成功", "shareRate", shareRate);
301 }
22301115cf6dba22025-03-25 19:06:21 +0800302
303
22301138b2533412025-06-05 00:18:58 +0800304 private static final String AVATAR_DIR = "uploads/avatarUrl/";
305
306 public Map<String, Object> uploadUserAvatar(Long userId, MultipartFile file) {
307 Users user = userRepository.findById(userId)
308 .orElseThrow(() -> new RuntimeException("用户不存在"));
309
310 try {
311 String avatarUrl = saveAvatar(file, userId);
312 user.setAvatarUrl(avatarUrl);
313 userRepository.save(user);
314
315 Map<String, Object> response = new HashMap<>();
316 response.put("status", "success");
317 response.put("message", "头像上传成功");
318 response.put("userId", user.getUserId());
319 response.put("avatarUrl", avatarUrl);
320 return response;
321
322 } catch (IOException e) {
323 throw new RuntimeException("头像上传失败: " + e.getMessage());
324 }
325 }
326
327 // 保存头像文件并返回可访问 URL
328 public String saveAvatar(MultipartFile file, Long userId) throws IOException {
329 String originalFilename = file.getOriginalFilename();
330 String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
331 String fileName = userId + extension; // 以用户ID作为文件名
332 Path path = Paths.get(AVATAR_DIR + fileName);
333 Files.createDirectories(path.getParent());
334 Files.write(path, file.getBytes());
335 return "/" + AVATAR_DIR + fileName; // 返回相对URL路径
336 }
337
338
Krishyab0cc1882025-06-09 10:54:09 +0800339 @Transactional
340 public String checkUserShareRate(Long userId) {
341 Optional<Users> optionalUser = userRepository.findById(userId);
342
343 if (optionalUser.isEmpty()) {
344 return "用户不存在";
345 }
346
347 Users user = optionalUser.get();
348
349 // 检查用户是否注册超过3天
350 LocalDate registrationDate = user.getRegistrationTime().toInstant()
351 .atZone(ZoneId.systemDefault())
352 .toLocalDate();
353 LocalDate today = LocalDate.now();
354
355 if (today.minusDays(3).isAfter(registrationDate)) {
356 // 用户注册超过3天,检查分享率
357
358 // 假设我们有一个方法可以获取用户最近3天的分享率历史
359 boolean hasLowShareRateForThreeDays = checkShareRateHistory(userId);
360
361 if (hasLowShareRateForThreeDays) {
362 // 分享率连续3天低于0.5
363
364 // 检查是否已发送过警告且超过24小时
365 Date lastUpdateTime = user.getLastupdatetime();
366 if (lastUpdateTime != null) {
367 LocalDate warningDate = lastUpdateTime.toInstant()
368 .atZone(ZoneId.systemDefault())
369 .toLocalDate();
370
371 if (today.minusDays(1).isAfter(warningDate)) {
372 // 已警告超过24小时,执行账号注销
373 userRepository.delete(user);
374 return "用户因分享率长期低于标准,账号已被注销";
375 }
376 }
377
378 // 首次发现或未超过24小时,发送警告
379 user.setLastupdatetime(new Date());
380 userRepository.save(user);
381 return "警告:您的分享率已连续3天低于0.5,请尽快提升,否则账号将被注销";
382 }
383 }
384
385 return "用户状态正常";
386 }
387
388 private boolean checkShareRateHistory(Long userId) {
389 Users user = userRepository.findById(userId).orElse(null);
390 if (user != null && user.getShareRate() < 0.5) {
391 // 实际应用中应查询历史记录,这里简化处理
392 return true;
393 }
394 return false;
395 }
Jin80d879a2025-06-09 20:53:18 +0800396 public String recharge(Long userId, Integer amount) {
397 if (amount == null || amount <= 0) {
398 return "充值金额必须大于0";
399 }
400 Users user = userRepository.findById(userId).orElse(null);
401 if (user == null) {
402 return "用户不存在";
403 }
Jinf50fba62025-06-09 22:47:24 +0800404 // 处理money为BigDecimal
405 BigDecimal currentMoney = user.getMoney() == null ? BigDecimal.ZERO : user.getMoney();
406 BigDecimal rechargeAmount = new BigDecimal(amount);
407 BigDecimal newMoney = currentMoney.add(rechargeAmount);
408
409 user.setMoney(newMoney);
Jin80d879a2025-06-09 20:53:18 +0800410 userRepository.save(user);
411 return "充值成功,当前余额:" + user.getMoney();
412 }
413
Krishyab0cc1882025-06-09 10:54:09 +0800414
Jinf50fba62025-06-09 22:47:24 +0800415 public Users getById(Long userId) {
416 return userRepository.findById(userId).orElse(null);
417 }
418
419
420 /**
421 * 检查用户是否有连续三天的登录记录
422 * @param userId 用户ID
423 * @return 是否有连续三天的登录记录
424 */
425 public boolean hasConsecutiveLoginDays(Long userId) {
426 // 获取用户的所有登录日期
427 List<LocalDateTime> loginDates = loginLogRepository.findDistinctLoginDates(userId);
428
429 if (loginDates.size() < 3) {
430 return false;
431 }
432
433 // 将日期转换为LocalDate并排序
434 List<LocalDate> dates = loginDates.stream()
435 .map(LocalDateTime::toLocalDate)
436 .distinct()
437 .sorted()
438 .toList();
439
440 // 检查是否有连续的三天
441 for (int i = 0; i < dates.size() - 2; i++) {
442 LocalDate date1 = dates.get(i);
443 LocalDate date2 = dates.get(i + 1);
444 LocalDate date3 = dates.get(i + 2);
445
446 if (date2.equals(date1.plusDays(1)) && date3.equals(date2.plusDays(1))) {
447 return true;
448 }
449 }
450
451 return false;
452 }
453
454
22301138b2533412025-06-05 00:18:58 +0800455
223011385e9c35a2025-06-04 15:52:45 +0800456}