From cc69ac1fee454518dffbc85d0ebde4a7b3e424ee Mon Sep 17 00:00:00 2001 From: zyh Date: Fri, 29 Aug 2025 23:55:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E7=94=A8=E6=88=B7=E7=A7=AF=E5=88=86=E4=BD=99?= =?UTF-8?q?=E9=A2=9D=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 9 + .../controller/admin/AccountController.java | 27 ++- .../dto/account/PointsBalanceResponse.java | 50 ++++++ .../service/account/AccountService.java | 19 +++ test_target_score.http | 32 ++++ 积分余额接口文档.md | 155 ++++++++++++++++++ 6 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 .claude/settings.local.json create mode 100644 src/main/java/com/gameplatform/server/model/dto/account/PointsBalanceResponse.java create mode 100644 test_target_score.http create mode 100644 积分余额接口文档.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..5557eae --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(git add:*)" + ], + "deny": [], + "ask": [] + } +} \ No newline at end of file diff --git a/src/main/java/com/gameplatform/server/controller/admin/AccountController.java b/src/main/java/com/gameplatform/server/controller/admin/AccountController.java index 8ae16f0..38c9971 100644 --- a/src/main/java/com/gameplatform/server/controller/admin/AccountController.java +++ b/src/main/java/com/gameplatform/server/controller/admin/AccountController.java @@ -3,6 +3,7 @@ package com.gameplatform.server.controller.admin; import com.gameplatform.server.model.dto.account.*; import com.gameplatform.server.model.dto.common.PageResult; import com.gameplatform.server.service.account.AccountService; +import com.gameplatform.server.security.JwtService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -16,9 +17,11 @@ import reactor.core.publisher.Mono; @Tag(name = "管理员账户管理", description = "管理员账户的增删改查操作") public class AccountController { private final AccountService accountService; + private final JwtService jwtService; - public AccountController(AccountService accountService) { + public AccountController(AccountService accountService, JwtService jwtService) { this.accountService = accountService; + this.jwtService = jwtService; } @GetMapping @@ -72,5 +75,27 @@ public class AccountController { public Mono resetPassword(@Parameter(description = "账户ID") @PathVariable Long id, @Valid @RequestBody ResetPasswordRequest req) { return accountService.resetPassword(id, req.getNewPassword(), Boolean.TRUE.equals(req.getForceLogout())); } + + @GetMapping("/me/points-balance") + @Operation(summary = "获取当前用户积分余额", description = "根据token解析用户ID并获取当前用户的积分余额") + public Mono getCurrentUserPointsBalance( + @Parameter(hidden = true) @RequestHeader("Authorization") String authHeader) { + return Mono.fromCallable(() -> { + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + throw new IllegalArgumentException("Authorization header is required"); + } + + String token = authHeader.substring(7); + io.jsonwebtoken.Claims claims = jwtService.parse(token); + Long userId = claims.get("userId", Long.class); + + if (userId == null) { + throw new IllegalArgumentException("Invalid token: userId not found"); + } + + return userId; + }) + .flatMap(accountService::getCurrentUserPointsBalance); + } } diff --git a/src/main/java/com/gameplatform/server/model/dto/account/PointsBalanceResponse.java b/src/main/java/com/gameplatform/server/model/dto/account/PointsBalanceResponse.java new file mode 100644 index 0000000..1a1ad97 --- /dev/null +++ b/src/main/java/com/gameplatform/server/model/dto/account/PointsBalanceResponse.java @@ -0,0 +1,50 @@ +package com.gameplatform.server.model.dto.account; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "用户积分余额响应") +public class PointsBalanceResponse { + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "用户名") + private String username; + + @Schema(description = "用户类型") + private String userType; + + @Schema(description = "积分余额") + private Long pointsBalance; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUserType() { + return userType; + } + + public void setUserType(String userType) { + this.userType = userType; + } + + public Long getPointsBalance() { + return pointsBalance; + } + + public void setPointsBalance(Long pointsBalance) { + this.pointsBalance = pointsBalance; + } +} \ No newline at end of file diff --git a/src/main/java/com/gameplatform/server/service/account/AccountService.java b/src/main/java/com/gameplatform/server/service/account/AccountService.java index 95cdcaf..5f8a4ab 100644 --- a/src/main/java/com/gameplatform/server/service/account/AccountService.java +++ b/src/main/java/com/gameplatform/server/service/account/AccountService.java @@ -4,6 +4,7 @@ import com.gameplatform.server.mapper.account.UserAccountMapper; import com.gameplatform.server.model.dto.account.AccountCreateRequest; import com.gameplatform.server.model.dto.account.AccountResponse; import com.gameplatform.server.model.dto.account.AccountUpdateRequest; +import com.gameplatform.server.model.dto.account.PointsBalanceResponse; import com.gameplatform.server.model.dto.common.PageResult; import com.gameplatform.server.model.entity.account.UserAccount; import org.springframework.security.crypto.password.PasswordEncoder; @@ -144,6 +145,24 @@ public class AccountService { .then(); } + public Mono getCurrentUserPointsBalance(Long userId) { + return Mono.fromCallable(() -> { + UserAccount account = mapper.selectById(userId); + if (account == null) { + throw new IllegalArgumentException("用户不存在"); + } + + PointsBalanceResponse response = new PointsBalanceResponse(); + response.setUserId(account.getId()); + response.setUsername(account.getUsername()); + response.setUserType(account.getUserType()); + response.setPointsBalance(account.getPointsBalance()); + + return response; + }) + .subscribeOn(Schedulers.boundedElastic()); + } + private AccountResponse toResp(UserAccount a) { if (a == null) return null; AccountResponse r = new AccountResponse(); diff --git a/test_target_score.http b/test_target_score.http new file mode 100644 index 0000000..43c208d --- /dev/null +++ b/test_target_score.http @@ -0,0 +1,32 @@ +### 获取目标点数接口测试 + +# 测试获取目标点数 +GET http://localhost:8080/api/link/target-score?codeNo=YOUR_CODE_NO_HERE +Accept: application/json + +### + +# 示例:使用实际的codeNo测试 +# GET http://localhost:8080/api/link/target-score?codeNo=ABC123DEF456 +# Accept: application/json + +### + +# 响应格式示例: +# 成功时: +# { +# "success": true, +# "completedPoints": 13750, +# "machineId": "device123", +# "codeNo": "ABC123DEF456", +# "errorMessage": null +# } +# +# 失败时: +# { +# "success": false, +# "completedPoints": null, +# "machineId": "device123", +# "codeNo": "ABC123DEF456", +# "errorMessage": "网络繁忙,稍后再试" +# } diff --git a/积分余额接口文档.md b/积分余额接口文档.md new file mode 100644 index 0000000..14135f3 --- /dev/null +++ b/积分余额接口文档.md @@ -0,0 +1,155 @@ +# 用户积分余额接口文档 + +## 接口概述 + +获取当前登录用户的积分余额信息,通过JWT token自动识别用户身份。 + +--- + +## 接口详情 + +### 基本信息 +- **接口路径**: `/api/admin/accounts/me/points-balance` +- **请求方法**: `GET` +- **接口描述**: 根据token解析用户ID并获取当前用户的积分余额 +- **认证方式**: JWT Bearer Token + +--- + +## 请求参数 + +### Headers +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| Authorization | string | 是 | Bearer {token},JWT认证令牌 | + +### 请求示例 +```http +GET /api/admin/accounts/me/points-balance HTTP/1.1 +Host: localhost:8080 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c +Content-Type: application/json +``` + +--- + +## 响应结果 + +### 成功响应 (200 OK) + +```json +{ + "userId": 12345, + "username": "agent001", + "userType": "AGENT", + "pointsBalance": 15000 +} +``` + +### 响应字段说明 +| 字段名 | 类型 | 说明 | +|--------|------|------| +| userId | Long | 用户ID | +| username | String | 用户名 | +| userType | String | 用户类型,ADMIN(管理员)或 AGENT(代理) | +| pointsBalance | Long | 积分余额(单位:积分点数) | + +--- + +## 错误响应 + +### 401 未授权 +```json +{ + "error": "Unauthorized", + "message": "Authorization header is required" +} +``` + +### 400 请求错误 +```json +{ + "error": "Bad Request", + "message": "Invalid token: userId not found" +} +``` + +### 404 用户不存在 +```json +{ + "error": "Not Found", + "message": "用户不存在" +} +``` + +--- + +## 调用示例 + +### JavaScript (fetch) +```javascript +const token = 'your-jwt-token-here'; + +fetch('/api/admin/accounts/me/points-balance', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } +}) +.then(response => response.json()) +.then(data => { + console.log('用户积分余额:', data.pointsBalance); + console.log('用户信息:', data); +}) +.catch(error => { + console.error('获取积分余额失败:', error); +}); +``` + +### curl +```bash +curl -X GET "http://localhost:8080/api/admin/accounts/me/points-balance" \ + -H "Authorization: Bearer your-jwt-token-here" \ + -H "Content-Type: application/json" +``` + +### Java (Spring WebFlux) +```java +WebClient webClient = WebClient.create("http://localhost:8080"); + +Mono response = webClient + .get() + .uri("/api/admin/accounts/me/points-balance") + .header("Authorization", "Bearer " + jwtToken) + .retrieve() + .bodyToMono(PointsBalanceResponse.class); + +response.subscribe( + pointsBalance -> System.out.println("积分余额: " + pointsBalance.getPointsBalance()), + error -> System.err.println("请求失败: " + error.getMessage()) +); +``` + +--- + +## 注意事项 + +1. **Token有效性**: JWT token必须有效且未过期 +2. **用户类型**: + - ADMIN用户的积分余额通常为0 + - AGENT用户才有实际的积分余额 +3. **权限控制**: 只能查询当前登录用户自己的积分余额 +4. **数据格式**: 积分余额以长整型返回,单位为积分点数 + +--- + +## 状态码说明 + +| 状态码 | 说明 | +|--------|------| +| 200 | 请求成功,返回积分余额信息 | +| 400 | 请求参数错误或token无效 | +| 401 | 未提供认证信息或认证失败 | +| 404 | 用户不存在 | +| 500 | 服务器内部错误 | \ No newline at end of file