From 898997a5939055b0c04550e06986459aab6bd915 Mon Sep 17 00:00:00 2001 From: Yui Date: Wed, 17 Jun 2026 16:27:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=A7=AF=E5=88=86=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=88=86=E9=A1=B5=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- API.md | 50 ++++++++ .../symphony/processor/UserProcessor.java | 112 ++++++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/API.md b/API.md index e70578d5..ecf2e369 100644 --- a/API.md +++ b/API.md @@ -479,6 +479,56 @@ curl --location --request POST 'https://fishpi.cn/report' \ `GET /user/{用户名}/point` +### 查询积分记录 + +`GET /api/user/points` + +分页查询当前用户积分记录。管理员可通过 `userId` 查询指定用户。 + +请求: + +| Key | 说明 | 示例 | +| ------ | ----------------------- | -------------------------------- | +| apiKey | 通用密钥 | oXTQTD4ljryXoIxa1lySgEl6aObrIhSS | +| p | 页码,默认 1 | 1 | +| size | 每页数量,默认 20,最大 200 | 20 | +| userId | 用户 oId,仅管理员可用 | 1659430635383 | + +请求示例: + +```bash +curl --location --request GET 'https://fishpi.cn/api/user/points?apiKey=oXTQTD4ljryXoIxa1lySgEl6aObrIhSS&p=1&size=20' \ +--header 'User-Agent: Mozilla/5.0' +``` + +响应: + +| Key | 说明 | 示例 | +| -------------------------------- | -------------------- | ------------- | +| code | 0 为成功,-1 为失败 | 0 | +| msg | 错误消息 | | +| data | 响应数据 | | +| - userId | 用户 oId | 1659430635383 | +| - records | 积分记录列表 | | +| -- oId | 记录 oId | 1760000000000 | +| -- fromId | 支出用户 oId | 1659430635383 | +| -- toId | 收入用户 oId | sys | +| -- sum | 积分数量 | 20 | +| -- type | 记录类型 | 8 | +| -- time | 记录时间 | 1760000000000 | +| -- dataId | 关联数据 | 1760000000000 | +| -- memo | 备注 | hello | +| -- operation | 收支方向 | + | +| -- balance | 操作后余额 | 183939 | +| -- displayType | 类型名称 | 活动收益 | +| -- description | 记录描述 | 签到奖励 | +| - pagination | 分页信息 | | +| -- paginationCurrentPageNum | 当前页 | 1 | +| -- paginationPageSize | 每页数量 | 20 | +| -- paginationRecordCount | 总记录数 | 100 | +| -- paginationPageCount | 总页数 | 5 | +| -- paginationPageNums | 页码列表 | [1,2,3,4,5] | + ### 查询用户勋章 `GET /user/{用户名}/medal` diff --git a/src/main/java/org/b3log/symphony/processor/UserProcessor.java b/src/main/java/org/b3log/symphony/processor/UserProcessor.java index 06dbbf03..8f3c520d 100644 --- a/src/main/java/org/b3log/symphony/processor/UserProcessor.java +++ b/src/main/java/org/b3log/symphony/processor/UserProcessor.java @@ -256,6 +256,7 @@ public static void register() { Dispatcher.post("/user/edit/points", userProcessor::adjustPoint); Dispatcher.post("/user/edit/notification", userProcessor::sendSystemNotification); Dispatcher.post("/user/identify", userProcessor::submitIdentify, loginCheck::handle); + Dispatcher.get("/api/user/points", userProcessor::getUserPointRecords, loginCheck::handle); Dispatcher.get("/api/user/{userName}/articles", userProcessor::userArticles, loginCheck::handle); Dispatcher.get("/api/user/{userName}/breezemoons", userProcessor::userBreezemoons, loginCheck::handle); } @@ -1666,6 +1667,117 @@ public void showHomePoints(final RequestContext context) { dataModel.put(Common.TYPE, "points"); } + /** + * Gets user point records. + * + * @param context the specified context + */ + public void getUserPointRecords(final RequestContext context) { + final JSONObject currentUser = (JSONObject) context.attr(User.USER); + if (null == currentUser) { + context.sendError(401); + return; + } + + final boolean isAdmin = Role.ROLE_ID_C_ADMIN.equals(currentUser.optString(User.USER_ROLE)); + final String currentUserId = currentUser.optString(Keys.OBJECT_ID); + String targetUserId = StringUtils.trim(context.param("userId")); + if (StringUtils.isBlank(targetUserId)) { + targetUserId = currentUserId; + } else if (!StringUtils.isNumeric(targetUserId) || targetUserId.length() > 19) { + renderUserPointRecordsError(context, "参数错误"); + return; + } + + if (!isAdmin && !currentUserId.equals(targetUserId)) { + renderUserPointRecordsError(context, "无权限"); + return; + } + + final JSONObject targetUser = userQueryService.getUser(targetUserId); + if (null == targetUser) { + renderUserPointRecordsError(context, "用户不存在"); + return; + } + + final int pageNum = getPositiveIntParam(context, "p", 1); + final int pageSize = Math.min(getPositiveIntParam(context, "size", Symphonys.USER_HOME_LIST_CNT), 200); + final int windowSize = Symphonys.USER_HOME_LIST_WIN_SIZE; + + final JSONObject userPointsResult = pointtransferQueryService.getUserPoints(targetUserId, pageNum, pageSize); + if (null == userPointsResult) { + renderUserPointRecordsError(context, "查询失败"); + return; + } + + final int recordCount = userPointsResult.optInt(Pagination.PAGINATION_RECORD_COUNT); + final int pageCount = (int) Math.ceil(recordCount / (double) pageSize); + final List pageNums = Paginator.paginate(pageNum, pageSize, pageCount, windowSize); + + final JSONArray records = new JSONArray(); + final JSONArray points = userPointsResult.optJSONArray(Keys.RESULTS); + if (null != points) { + for (int i = 0; i < points.length(); i++) { + final Object item = points.opt(i); + if (!(item instanceof JSONObject)) { + continue; + } + + final JSONObject point = (JSONObject) item; + records.put(new JSONObject() + .put(Keys.OBJECT_ID, point.optString(Keys.OBJECT_ID)) + .put(Pointtransfer.FROM_ID, point.optString(Pointtransfer.FROM_ID)) + .put(Pointtransfer.TO_ID, point.optString(Pointtransfer.TO_ID)) + .put(Pointtransfer.SUM, point.optInt(Pointtransfer.SUM)) + .put(Pointtransfer.TYPE, point.optInt(Pointtransfer.TYPE)) + .put(Pointtransfer.TIME, point.optLong(Pointtransfer.TIME)) + .put(Pointtransfer.DATA_ID, point.optString(Pointtransfer.DATA_ID)) + .put(Pointtransfer.MEMO, point.optString(Pointtransfer.MEMO)) + .put(Common.OPERATION, point.optString(Common.OPERATION)) + .put(Common.BALANCE, point.optInt(Common.BALANCE)) + .put(Common.DISPLAY_TYPE, point.optString(Common.DISPLAY_TYPE)) + .put(Common.DESCRIPTION, point.optString(Common.DESCRIPTION))); + } + } + + final JSONObject pagination = new JSONObject() + .put(Pagination.PAGINATION_CURRENT_PAGE_NUM, pageNum) + .put(Pagination.PAGINATION_PAGE_SIZE, pageSize) + .put(Pagination.PAGINATION_RECORD_COUNT, recordCount) + .put(Pagination.PAGINATION_PAGE_COUNT, pageCount) + .put(Pagination.PAGINATION_PAGE_NUMS, pageNums); + final JSONObject data = new JSONObject() + .put("userId", targetUserId) + .put("records", records) + .put(Pagination.PAGINATION, pagination); + + context.renderJSON(new JSONObject() + .put(Keys.CODE, StatusCodes.SUCC) + .put(Keys.MSG, "") + .put(Common.DATA, data)); + } + + private int getPositiveIntParam(final RequestContext context, final String key, final int defaultValue) { + final String value = StringUtils.trim(context.param(key)); + if (StringUtils.isBlank(value)) { + return defaultValue; + } + + try { + final int ret = Integer.parseInt(value); + return ret > 0 ? ret : defaultValue; + } catch (final NumberFormatException e) { + return defaultValue; + } + } + + private void renderUserPointRecordsError(final RequestContext context, final String msg) { + context.renderJSON(new JSONObject() + .put(Keys.CODE, StatusCodes.ERR) + .put(Keys.MSG, msg) + .put(Common.DATA, new JSONObject())); + } + /** * List usernames. *