From 72e9a7737372bed5dce2089fa2d82a48e010c8e8 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 21 Aug 2025 12:27:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20ui=20=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openisle/controller/AuthController.java | 23 ++- .../com/openisle/service/InviteService.java | 16 +- .../com/openisle/service/PointService.java | 3 + frontend_nuxt/pages/points.vue | 168 ++++++++++++------ 4 files changed, 140 insertions(+), 70 deletions(-) diff --git a/backend/src/main/java/com/openisle/controller/AuthController.java b/backend/src/main/java/com/openisle/controller/AuthController.java index f643e7807..d4a7a07da 100644 --- a/backend/src/main/java/com/openisle/controller/AuthController.java +++ b/backend/src/main/java/com/openisle/controller/AuthController.java @@ -47,7 +47,8 @@ public class AuthController { return ResponseEntity.badRequest().body(Map.of("error", "Invalid captcha")); } if (req.getInviteToken() != null && !req.getInviteToken().isEmpty()) { - if (!inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult result = inviteService.validate(req.getInviteToken()); + if (!result.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "邀请码使用次数过多")); } try { @@ -144,7 +145,8 @@ public class AuthController { @PostMapping("/google") public ResponseEntity loginWithGoogle(@RequestBody GoogleLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = googleAuthService.authenticate( @@ -154,7 +156,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken(), user.getUsername()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" @@ -218,7 +220,8 @@ public class AuthController { @PostMapping("/github") public ResponseEntity loginWithGithub(@RequestBody GithubLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = githubAuthService.authenticate( @@ -229,7 +232,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken(), user.getUsername()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" @@ -265,7 +268,8 @@ public class AuthController { @PostMapping("/discord") public ResponseEntity loginWithDiscord(@RequestBody DiscordLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = discordAuthService.authenticate( @@ -276,7 +280,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken(), user.getUsername()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" @@ -311,7 +315,8 @@ public class AuthController { @PostMapping("/twitter") public ResponseEntity loginWithTwitter(@RequestBody TwitterLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = twitterAuthService.authenticate( @@ -323,7 +328,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken(), user.getUsername()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" diff --git a/backend/src/main/java/com/openisle/service/InviteService.java b/backend/src/main/java/com/openisle/service/InviteService.java index 158772d34..23ca58bd1 100644 --- a/backend/src/main/java/com/openisle/service/InviteService.java +++ b/backend/src/main/java/com/openisle/service/InviteService.java @@ -5,6 +5,7 @@ import com.openisle.model.User; import com.openisle.repository.InviteTokenRepository; import com.openisle.repository.UserRepository; import lombok.RequiredArgsConstructor; +import lombok.Value; import org.springframework.stereotype.Service; import java.time.LocalDate; @@ -18,6 +19,12 @@ public class InviteService { private final JwtService jwtService; private final PointService pointService; + @Value + public class InviteValidateResult { + InviteToken inviteToken; + boolean validate; + } + public String generate(String username) { User inviter = userRepository.findByUsername(username).orElseThrow(); LocalDate today = LocalDate.now(); @@ -35,14 +42,17 @@ public class InviteService { return token; } - public boolean validate(String token) { + public InviteValidateResult validate(String token) { + if (token == null || token.isEmpty()) { + return new InviteValidateResult(null, false); + } try { jwtService.validateAndGetSubjectForInvite(token); } catch (Exception e) { - return false; + return new InviteValidateResult(null, false); } InviteToken invite = inviteTokenRepository.findById(token).orElse(null); - return invite != null && invite.getUsageCount() < 3; + return new InviteValidateResult(invite, invite != null && invite.getUsageCount() < 3); } public void consume(String token, String newUserName) { diff --git a/backend/src/main/java/com/openisle/service/PointService.java b/backend/src/main/java/com/openisle/service/PointService.java index ea6507d98..69a543389 100644 --- a/backend/src/main/java/com/openisle/service/PointService.java +++ b/backend/src/main/java/com/openisle/service/PointService.java @@ -49,6 +49,9 @@ public class PointService { private int addPoint(User user, int amount, PointHistoryType type, Post post, Comment comment, User fromUser) { + if (pointHistoryRepository.countByUser(user) == 0) { + recordHistory(user, PointHistoryType.SYSTEM_ONLINE, 0, null, null, null); + } user.setPoint(user.getPoint() + amount); userRepository.save(user); recordHistory(user, type, amount, post, comment, fromUser); diff --git a/frontend_nuxt/pages/points.vue b/frontend_nuxt/pages/points.vue index 6f8e79fba..663928c66 100644 --- a/frontend_nuxt/pages/points.vue +++ b/frontend_nuxt/pages/points.vue @@ -16,49 +16,52 @@ - @@ -136,6 +155,7 @@ import RedeemPopup from '~/components/RedeemPopup.vue' import BaseTimeline from '~/components/BaseTimeline.vue' import BasePlaceholder from '~/components/BasePlaceholder.vue' import { stripMarkdownLength } from '~/utils/markdown' +import TimeManager from '~/utils/time' const config = useRuntimeConfig() const API_BASE_URL = config.public.apiBaseUrl @@ -161,6 +181,15 @@ const contact = ref('') const loading = ref(false) const selectedGood = ref(null) +const iconMap = { + POST: 'fas fa-file-alt', + COMMENT: 'fas fa-comment', + POST_LIKED: 'fas fa-thumbs-up', + COMMENT_LIKED: 'fas fa-thumbs-up', + INVITE: 'fas fa-user-plus', + SYSTEM_ONLINE: 'fas fa-clock', +} + onMounted(async () => { isLoading.value = true if (authState.loggedIn) { @@ -195,7 +224,10 @@ const loadHistory = async () => { headers: { Authorization: `Bearer ${token}` }, }) if (res.ok) { - histories.value = await res.json() + histories.value = (await res.json()).map((item) => ({ + ...item, + icon: iconMap[item.type], + })) } historyLoading.value = false historyLoaded.value = true @@ -241,12 +273,15 @@ const submitRedeem = async () => {