@ccw-api/api 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -10
- package/dist/esm/community-web/comment/page_by_topic.js +1 -1
- package/dist/esm/community-web/great_creation/update.js +13 -0
- package/dist/esm/community-web/great_creation/update.test.js +4 -0
- package/dist/esm/community-web/index.js +8 -0
- package/dist/esm/community-web/smart_contract/list.js +14 -0
- package/dist/esm/community-web/smart_contract/list.test.js +4 -0
- package/dist/esm/community-web/user_package/status.js +14 -0
- package/dist/esm/community-web/user_package/status.test.js +4 -0
- package/dist/esm/community-web/user_package/user_product.js +29 -0
- package/dist/esm/community-web/user_package/user_product.test.js +9 -0
- package/dist/esm/community-web-cloud-database/cloud_variable/save.js +29 -0
- package/dist/esm/community-web-cloud-database/cloud_variable/save.test.js +13 -0
- package/dist/esm/community-web-cloud-database/index.js +5 -0
- package/dist/esm/gandi-main/creation/leaderboards/{oid}/records.js +28 -0
- package/dist/esm/gandi-main/creation/leaderboards/{oid}/records.test.js +11 -0
- package/dist/esm/gandi-main/index.js +3 -0
- package/dist/esm/index.js +5 -1
- package/dist/esm/op-parent-api/captcha/check.js +15 -0
- package/dist/esm/op-parent-api/captcha/check.test.js +12 -0
- package/dist/esm/op-parent-api/captcha/create.js +13 -0
- package/dist/esm/op-parent-api/captcha/create.test.js +13 -0
- package/dist/esm/op-parent-api/index.js +6 -0
- package/dist/esm/sso/index.js +4 -0
- package/dist/esm/sso/web/auth/assistant/captcha/v2/create.js +17 -0
- package/dist/esm/sso/web/auth/assistant/captcha/v2/create.test.js +8 -0
- package/dist/esm/sso/web/auth/login-by-password.js +4 -3
- package/dist/esm/sso/web/auth/v3/login/by-phone.js +33 -0
- package/dist/esm/sso/web/auth/v3/login/by-phone.test.js +7 -0
- package/dist/node/community-web/comment/page_by_topic.js +1 -1
- package/dist/node/community-web/great_creation/update.js +17 -0
- package/dist/node/community-web/great_creation/update.test.js +6 -0
- package/dist/node/community-web/index.js +8 -0
- package/dist/node/community-web/smart_contract/list.js +18 -0
- package/dist/node/community-web/smart_contract/list.test.js +6 -0
- package/dist/node/community-web/user_package/status.js +18 -0
- package/dist/node/community-web/user_package/status.test.js +6 -0
- package/dist/node/community-web/user_package/user_product.js +33 -0
- package/dist/node/community-web/user_package/user_product.test.js +11 -0
- package/dist/node/community-web-cloud-database/cloud_variable/save.js +34 -0
- package/dist/node/community-web-cloud-database/cloud_variable/save.test.js +15 -0
- package/dist/node/community-web-cloud-database/index.js +8 -0
- package/dist/node/gandi-main/creation/leaderboards/{oid}/records.js +33 -0
- package/dist/node/gandi-main/creation/leaderboards/{oid}/records.test.js +13 -0
- package/dist/node/gandi-main/index.js +3 -0
- package/dist/node/index.js +7 -1
- package/dist/node/op-parent-api/captcha/check.js +19 -0
- package/dist/node/op-parent-api/captcha/check.test.js +14 -0
- package/dist/node/op-parent-api/captcha/create.js +17 -0
- package/dist/node/op-parent-api/captcha/create.test.js +15 -0
- package/dist/node/op-parent-api/index.js +9 -0
- package/dist/node/sso/index.js +4 -0
- package/dist/node/sso/web/auth/assistant/captcha/v2/create.js +21 -0
- package/dist/node/sso/web/auth/assistant/captcha/v2/create.test.js +10 -0
- package/dist/node/sso/web/auth/login-by-password.js +4 -3
- package/dist/node/sso/web/auth/v3/login/by-phone.js +37 -0
- package/dist/node/sso/web/auth/v3/login/by-phone.test.js +9 -0
- package/dist/types/community-web/comment/page_by_topic.d.ts +1 -1
- package/dist/types/community-web/great_creation/update.d.ts +12 -0
- package/dist/types/community-web/index.d.ts +8 -0
- package/dist/types/community-web/smart_contract/list.d.ts +49 -0
- package/dist/types/community-web/user_package/status.d.ts +13 -0
- package/dist/types/community-web/user_package/user_product.d.ts +40 -0
- package/dist/types/community-web-cloud-database/cloud_variable/save.d.ts +26 -0
- package/dist/types/community-web-cloud-database/index.d.ts +5 -0
- package/dist/types/gandi-main/creation/leaderboards/{oid}/records.d.ts +62 -0
- package/dist/types/gandi-main/index.d.ts +3 -0
- package/dist/types/index.d.ts +19 -1
- package/dist/types/op-parent-api/captcha/check.d.ts +21 -0
- package/dist/types/op-parent-api/captcha/create.d.ts +24 -0
- package/dist/types/op-parent-api/index.d.ts +6 -0
- package/dist/types/sso/index.d.ts +4 -0
- package/dist/types/sso/web/auth/assistant/captcha/v2/create.d.ts +34 -0
- package/dist/types/sso/web/auth/login-by-password.d.ts +2 -18
- package/dist/types/sso/web/auth/v3/login/by-phone.d.ts +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# @ccw-api/api
|
|
2
2
|
|
|
3
|
-
CCW(创作社区)前后端接口 SDK,整合 `sso.ccw.site
|
|
4
|
-
|
|
3
|
+
CCW(创作社区)前后端接口 SDK,整合 `sso.ccw.site`、`community-web.ccw.site`、
|
|
4
|
+
`gandi-main.ccw.site`、`bfs-web.ccw.site`、`community-web-cloud-database.ccw.site`
|
|
5
|
+
五大服务的常用 API,提供完整的 TypeScript 类型与开箱即用的分页封装。
|
|
5
6
|
|
|
6
7
|
- 包格式:**ESM + CJS + .d.ts 三产物**同时产出
|
|
7
8
|
- 类型:所有请求 / 响应字段**强类型**,后端 key 固定约束(如 `MongoDBId`、`CNameOssUrl`、`UUID`)
|
|
@@ -40,9 +41,15 @@ setToken("abcdefgfoo");
|
|
|
40
41
|
### 2. 调用方式
|
|
41
42
|
|
|
42
43
|
```ts
|
|
43
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
sso,
|
|
46
|
+
communityWeb,
|
|
47
|
+
gandiMain,
|
|
48
|
+
bfsWeb,
|
|
49
|
+
communityWebCloudDatabase,
|
|
50
|
+
} from "@ccw-api/api"; // ✅ 推荐:具名导入
|
|
44
51
|
// 或
|
|
45
|
-
import api from "@ccw-api/api"; // ✅ default 导入:api.sso / api.communityWeb / api.gandiMain / api.bfsWeb
|
|
52
|
+
import api from "@ccw-api/api"; // ✅ default 导入:api.sso / api.communityWeb / api.gandiMain / api.bfsWeb / api.communityWebCloudDatabase
|
|
46
53
|
```
|
|
47
54
|
|
|
48
55
|
---
|
|
@@ -71,7 +78,7 @@ await logoutBySession("64...session_oid");
|
|
|
71
78
|
|
|
72
79
|
## 模块二:Community-Web(community-web.ccw.site)
|
|
73
80
|
|
|
74
|
-
覆盖作品 / 学生 / 星球 / 评论 / 通知 / 任务 / 表情 / 签到 / 云资产 / 学科专区 等业务领域,共 **
|
|
81
|
+
覆盖作品 / 学生 / 星球 / 评论 / 通知 / 任务 / 表情 / 签到 / 云资产 / 学科专区 / 打赏 等业务领域,共 **96 个 API**。
|
|
75
82
|
下面是常用场景示例。
|
|
76
83
|
|
|
77
84
|
### 学生与作品
|
|
@@ -178,6 +185,10 @@ const [allEmoji, emojiPage, categories] = await Promise.all([
|
|
|
178
185
|
// 签到
|
|
179
186
|
await communityWeb.insertCheckInRecord(); // 今日打卡
|
|
180
187
|
const history = await communityWeb.getCheckInRecords();
|
|
188
|
+
|
|
189
|
+
// 作品 / 评论 打赏(需要token,消耗账户金币)
|
|
190
|
+
// 给某个作品打赏 1 金币(objectType 默认 "CREATION")
|
|
191
|
+
await communityWeb.donateTrade(1, "68ce4849811b737483bf7027", "CREATION");
|
|
181
192
|
```
|
|
182
193
|
|
|
183
194
|
### 黑名单、封禁、禁言查询
|
|
@@ -204,10 +215,16 @@ const muted = await communityWeb.getMutedUserDetail("244373873");
|
|
|
204
215
|
|
|
205
216
|
## 模块三:Gandi-Main(gandi-main.ccw.site)
|
|
206
217
|
|
|
207
|
-
|
|
218
|
+
公告、服务器时间与作品排行榜相关,共 **5 个 API**:
|
|
208
219
|
|
|
209
220
|
```ts
|
|
210
|
-
const {
|
|
221
|
+
const {
|
|
222
|
+
getCurrentTimestamp,
|
|
223
|
+
getBulletinsPage,
|
|
224
|
+
getBulletinDetail,
|
|
225
|
+
submitLeaderboardRecord,
|
|
226
|
+
getLeaderboardRecords,
|
|
227
|
+
} = gandiMain;
|
|
211
228
|
|
|
212
229
|
// 服务端当前毫秒级时间戳(比客户端时间更可靠,可用于签到、排行榜等需要严格时间判断的场景)
|
|
213
230
|
const ts = await gandiMain.getCurrentTimestamp();
|
|
@@ -221,16 +238,37 @@ const bulletins = await gandiMain.getBulletinsPage("PUBLISHED", {
|
|
|
221
238
|
|
|
222
239
|
// 某条公告详情
|
|
223
240
|
const detail = await gandiMain.getBulletinDetail(123);
|
|
241
|
+
|
|
242
|
+
// ====== 作品排行榜 ======
|
|
243
|
+
|
|
244
|
+
// 获取排行榜记录(含榜单配置、当前用户排名、完整记录分页列表)
|
|
245
|
+
const board = await gandiMain.getLeaderboardRecords("65a2314daa96bd09b5beae5a");
|
|
246
|
+
console.log(board.title); // "总收益"
|
|
247
|
+
console.log(board.scoreUnit); // "币"
|
|
248
|
+
console.log(board.sortType); // "DESC"
|
|
249
|
+
console.log(board.leaderboardRecordTotalNum); // 834
|
|
250
|
+
console.log(board.curUserLeaderboardRecord?.ranking); // 139
|
|
251
|
+
console.log(board.leaderboardRecords[0].ranking); // 1
|
|
252
|
+
console.log(board.leaderboardRecords[0].score); // 排行榜最高分
|
|
253
|
+
console.log(board.leaderboardRecords[0].user?.nickname); // 榜首昵称
|
|
254
|
+
|
|
255
|
+
// 向排行榜提交分数记录(需要token:userOid必须对应当前登录用户)
|
|
256
|
+
await gandiMain.submitLeaderboardRecord(
|
|
257
|
+
"65a2314daa96bd09b5beae5a", // 排行榜oid
|
|
258
|
+
1500, // 分数
|
|
259
|
+
"6a42413ccc79993c436d04b6", // 用户oid
|
|
260
|
+
"", // 扩展信息(可选)
|
|
261
|
+
);
|
|
224
262
|
```
|
|
225
263
|
|
|
226
264
|
---
|
|
227
265
|
|
|
228
266
|
## 模块四:BFS-Web(bfs-web.ccw.site)
|
|
229
267
|
|
|
230
|
-
用户扩展(extension
|
|
268
|
+
用户扩展(extension)管理与扩展打赏,共 **2 个 API**:
|
|
231
269
|
|
|
232
270
|
```ts
|
|
233
|
-
const { getUserExtensions } = bfsWeb;
|
|
271
|
+
const { getUserExtensions, donateExtension } = bfsWeb;
|
|
234
272
|
|
|
235
273
|
// 分页查询某用户上传/使用过的扩展列表(默认按 updatedAt 倒序)
|
|
236
274
|
const exts = await bfsWeb.getUserExtensions("63c2807d669fa967f17f5559", {
|
|
@@ -238,6 +276,40 @@ const exts = await bfsWeb.getUserExtensions("63c2807d669fa967f17f5559", {
|
|
|
238
276
|
perPage: 20,
|
|
239
277
|
});
|
|
240
278
|
// exts: PagesRes<Extension>
|
|
279
|
+
|
|
280
|
+
// 给某个扩展打赏(需要token,消耗账户金币)
|
|
281
|
+
await bfsWeb.donateExtension(
|
|
282
|
+
"spineAnimation", // 扩展id(eid)
|
|
283
|
+
5, // 打赏金币数量
|
|
284
|
+
);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 模块五:Community-Web-Cloud-Database(community-web-cloud-database.ccw.site)
|
|
290
|
+
|
|
291
|
+
作品 / 用户的云变量(键值对存储)管理,共 **2 个 API**:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
const { saveProjectCloudVariable, saveUserCloudVariable } =
|
|
295
|
+
communityWebCloudDatabase;
|
|
296
|
+
|
|
297
|
+
// 保存作品云变量(无需token,主键是作品id;value结构任意,泛型自动推导返回)
|
|
298
|
+
const saved1 = await communityWebCloudDatabase.saveProjectCloudVariable(
|
|
299
|
+
"651c9a5142f8fb5ada70fad2",
|
|
300
|
+
"backup_65d31eaeb2ce1b785ebea87b_云时间",
|
|
301
|
+
{ v: "2025-07-05T17:40:53.057+0800" },
|
|
302
|
+
);
|
|
303
|
+
// saved1: { v: string }
|
|
304
|
+
// ^? 泛型自动匹配 value 传入的结构
|
|
305
|
+
|
|
306
|
+
// 保存用户云变量(需要token,主键内部自动为 userId 拼接 "-u" 后缀)
|
|
307
|
+
const saved2 = await communityWebCloudDatabase.saveUserCloudVariable(
|
|
308
|
+
"68dddc63d28b025cb53fae25",
|
|
309
|
+
"256053228",
|
|
310
|
+
{ key2: "0" },
|
|
311
|
+
);
|
|
312
|
+
// saved2: { key2: string }
|
|
241
313
|
```
|
|
242
314
|
|
|
243
315
|
---
|
|
@@ -336,7 +408,7 @@ const p: PagesRes<Creation.Creation> = {
|
|
|
336
408
|
# 三产物构建(node / esm / .d.ts)
|
|
337
409
|
npm run build
|
|
338
410
|
|
|
339
|
-
# 跑测试(Jest 30 + ts-jest,
|
|
411
|
+
# 跑测试(Jest 30 + ts-jest,105 suites / 112 tests)
|
|
340
412
|
npm test
|
|
341
413
|
npm run test:dev # watch 模式
|
|
342
414
|
|
|
@@ -9,7 +9,7 @@ const dpa = {
|
|
|
9
9
|
* 根据主题获取评论列表
|
|
10
10
|
* @param {string} subjectOid 主题oid
|
|
11
11
|
* @param {SubjectType} subjectType 主题类型 (如: "POST")
|
|
12
|
-
* @param {
|
|
12
|
+
* @param {SectionType} sectionType 区块类型 (如: "COMMENT")
|
|
13
13
|
* @param {Partial<PageArgs<SortField| T>>} pageArgs_ 分页参数
|
|
14
14
|
* @returns {Promise<Res>} 评论分页结果
|
|
15
15
|
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://community-web.ccw.site/great_creation/update";
|
|
3
|
+
/**
|
|
4
|
+
* 更换代表作
|
|
5
|
+
* @param {MongoDBId|""} creationOid 作品OID 若为""则是取消展示
|
|
6
|
+
* @returns {Promise<Res>} 更换结果
|
|
7
|
+
*/
|
|
8
|
+
export async function updateGreatCreation(creationOid) {
|
|
9
|
+
const req = { creationOid };
|
|
10
|
+
return await ccwAxios
|
|
11
|
+
.post(url, req)
|
|
12
|
+
.then((res) => res.data.body);
|
|
13
|
+
}
|
|
@@ -93,6 +93,10 @@ import { createComment } from "./comment/create";
|
|
|
93
93
|
import { likeComment } from "./comment/like_record/create";
|
|
94
94
|
import { getStudentFollowingPage } from "./student/following/page";
|
|
95
95
|
import { donateTrade } from "./study-trade/trade/donate";
|
|
96
|
+
import { getUserProductsPage } from "./user_package/user_product";
|
|
97
|
+
import { setUserProductStatus } from "./user_package/status";
|
|
98
|
+
import { updateGreatCreation } from "./great_creation/update";
|
|
99
|
+
import { getSmartContractList } from "./smart_contract/list";
|
|
96
100
|
export const communityWeb = {
|
|
97
101
|
acceptAward,
|
|
98
102
|
donateTrade,
|
|
@@ -153,6 +157,7 @@ export const communityWeb = {
|
|
|
153
157
|
getRemixedCreationPage,
|
|
154
158
|
getReputationScoreLogPage: getReputationScoreLog,
|
|
155
159
|
getSearchHotWords,
|
|
160
|
+
getSmartContractList,
|
|
156
161
|
getSplitRuleDetail,
|
|
157
162
|
getSpreadFeedUnreadCount,
|
|
158
163
|
getStudentBlockRecordDetail,
|
|
@@ -176,6 +181,7 @@ export const communityWeb = {
|
|
|
176
181
|
getUserAssetStorageSize,
|
|
177
182
|
getUserCardDetail,
|
|
178
183
|
getUserLabelList,
|
|
184
|
+
getUserProductsPage,
|
|
179
185
|
getCreationsByStudent,
|
|
180
186
|
insertCheckInRecord,
|
|
181
187
|
likeComment,
|
|
@@ -184,9 +190,11 @@ export const communityWeb = {
|
|
|
184
190
|
searchCloudAssets,
|
|
185
191
|
searchCreationsByTag,
|
|
186
192
|
sendEvent,
|
|
193
|
+
setUserProductStatus,
|
|
187
194
|
submitCreation,
|
|
188
195
|
topComment,
|
|
189
196
|
updateCreation,
|
|
197
|
+
updateGreatCreation,
|
|
190
198
|
updateStudent,
|
|
191
199
|
viewCreationStats,
|
|
192
200
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://community-web.ccw.site/smart_contract/list";
|
|
3
|
+
/**
|
|
4
|
+
* 列出作品中的智能金币合约
|
|
5
|
+
* @param {MongoDBId} bizId 业务ID(如作品OID)
|
|
6
|
+
* @param {SmartContractBizType} bizType 业务类型,CREATION
|
|
7
|
+
* @returns {Promise<Res>} 智能合约列表
|
|
8
|
+
*/
|
|
9
|
+
export async function getSmartContractList(bizId, bizType = "CREATION") {
|
|
10
|
+
const req = { bizId, bizType };
|
|
11
|
+
return await ccwAxios
|
|
12
|
+
.post(url, req)
|
|
13
|
+
.then((res) => res.data.body);
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://community-web.ccw.site/user_package/status";
|
|
3
|
+
/**
|
|
4
|
+
* 设置用户商品状态(如佩戴/卸下头像框)
|
|
5
|
+
* @param {number} id 用户商品ID
|
|
6
|
+
* @param {boolean} status 商品状态,true为佩戴,false为卸下
|
|
7
|
+
* @returns {Promise<Res>} 设置结果
|
|
8
|
+
*/
|
|
9
|
+
export async function setUserProductStatus(id, status) {
|
|
10
|
+
const req = { status, id };
|
|
11
|
+
return await ccwAxios
|
|
12
|
+
.post(url, req)
|
|
13
|
+
.then((res) => res.data.body);
|
|
14
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
import { queryPage, DEFAULT_PAGE_ARGS } from "src/queryPages";
|
|
3
|
+
export const url = "https://community-web.ccw.site/user_package/user_product";
|
|
4
|
+
export var ProductCategory;
|
|
5
|
+
(function (ProductCategory) {
|
|
6
|
+
ProductCategory[ProductCategory["AVATAR_FRAME"] = 5] = "AVATAR_FRAME";
|
|
7
|
+
})(ProductCategory || (ProductCategory = {}));
|
|
8
|
+
/**
|
|
9
|
+
* 分页获取用户商品(如头像框)
|
|
10
|
+
* @param {ProductCategory} productCategory 商品分类,默认5为头像框
|
|
11
|
+
* @param {Partial<PageArgs<SortField>>} pageArgs_ 分页参数
|
|
12
|
+
* @returns {Promise<Res>} 分页用户商品数据
|
|
13
|
+
*/
|
|
14
|
+
export async function getUserProductsPage(productCategory = ProductCategory.AVATAR_FRAME, pageArgs_ = {}) {
|
|
15
|
+
const pageArgs = {
|
|
16
|
+
...DEFAULT_PAGE_ARGS,
|
|
17
|
+
...pageArgs_,
|
|
18
|
+
};
|
|
19
|
+
const queryUrl = queryPage(url, pageArgs);
|
|
20
|
+
const req = {
|
|
21
|
+
page: pageArgs.page,
|
|
22
|
+
perPage: pageArgs.perPage,
|
|
23
|
+
sortType: pageArgs.sortType,
|
|
24
|
+
productCategory,
|
|
25
|
+
};
|
|
26
|
+
return await ccwAxios
|
|
27
|
+
.post(queryUrl, req)
|
|
28
|
+
.then((res) => res.data.body);
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getUserProductsPage, ProductCategory } from "./user_product";
|
|
2
|
+
test("should fail without token for avatar frame products", async () => {
|
|
3
|
+
await expect(getUserProductsPage(ProductCategory.AVATAR_FRAME, {
|
|
4
|
+
page: 1,
|
|
5
|
+
perPage: 8,
|
|
6
|
+
sortType: "DESC",
|
|
7
|
+
sortField: "",
|
|
8
|
+
})).rejects.toThrow();
|
|
9
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://community-web-cloud-database.ccw.site/cloud_variable/save";
|
|
3
|
+
async function saveCloudVariableBase(primaryKey, secondaryKey, value) {
|
|
4
|
+
const req = { primaryKey, secondaryKey, value };
|
|
5
|
+
return await ccwAxios
|
|
6
|
+
.post(url, req)
|
|
7
|
+
.then((res) => res.data.body);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 保存作品云变量到云数据库
|
|
11
|
+
* @param {MongoDBId} projectId 作品id
|
|
12
|
+
* @param {string} secondaryKey 次级键 - 变量名或标识
|
|
13
|
+
* @param {T} value 要保存数据
|
|
14
|
+
* @returns {Promise<SaveCloudVariableRes<T>>} 保存后的值对象
|
|
15
|
+
*/
|
|
16
|
+
export async function saveProjectCloudVariable(projectId, secondaryKey, value) {
|
|
17
|
+
return await saveCloudVariableBase(projectId, secondaryKey, { v: value });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 保存数据到用户云数据库
|
|
21
|
+
* @param {MongoDBId} projectId 作品id
|
|
22
|
+
* @param {number} studentNum 次级键 - 用户id
|
|
23
|
+
* @param {T} value 要保存的键值对
|
|
24
|
+
* @returns {Promise<SaveCloudVariableRes<T>>} 保存后的值对象
|
|
25
|
+
*/
|
|
26
|
+
export async function saveUserCloudVariable(projectId, studentNum, value) {
|
|
27
|
+
const primaryKey = `${projectId}-u`;
|
|
28
|
+
return await saveCloudVariableBase(primaryKey, `${studentNum}`, value);
|
|
29
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { saveProjectCloudVariable, saveUserCloudVariable } from "./save";
|
|
2
|
+
test("save project cloud variable should return saved value", async () => {
|
|
3
|
+
const testValue = "2025-07-05T17:40:53.057+0800";
|
|
4
|
+
const res = await saveProjectCloudVariable("68dddc63d28b025cb53fae25", "test", testValue);
|
|
5
|
+
expect(res).toBeDefined();
|
|
6
|
+
expect(res.v).toBe(testValue);
|
|
7
|
+
});
|
|
8
|
+
test("save user cloud variable should ok", async () => {
|
|
9
|
+
const res = await saveUserCloudVariable("68dddc63d28b025cb53fae25", 0, {
|
|
10
|
+
key2: "0",
|
|
11
|
+
});
|
|
12
|
+
expect(res.key2).toEqual("0");
|
|
13
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://gandi-main.ccw.site/creation/leaderboards";
|
|
3
|
+
/**
|
|
4
|
+
* 向作品排行榜提交一条分数记录
|
|
5
|
+
* @param {MongoDBId} leaderboardOid 排行榜id
|
|
6
|
+
* @param {number} score 分数
|
|
7
|
+
* @param {MongoDBId} userOid 用户id
|
|
8
|
+
* @param {string} ext 扩展信息(可选)
|
|
9
|
+
* @returns {Promise<SubmitRes>} 是否提交成功
|
|
10
|
+
*/
|
|
11
|
+
export async function submitLeaderboardRecord(leaderboardOid, score, userOid, ext = "") {
|
|
12
|
+
const req = { score, oid: userOid, ext };
|
|
13
|
+
const requestUrl = `${url}/${leaderboardOid}/records`;
|
|
14
|
+
return await ccwAxios
|
|
15
|
+
.post(requestUrl, req)
|
|
16
|
+
.then((res) => res.data.body);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 获取作品排行榜记录列表
|
|
20
|
+
* @param {MongoDBId} leaderboardOid 排行榜id
|
|
21
|
+
* @returns {Promise<LeaderboardRecordsRes>} 排行榜记录列表
|
|
22
|
+
*/
|
|
23
|
+
export async function getLeaderboardRecords(leaderboardOid) {
|
|
24
|
+
const requestUrl = `${url}/${leaderboardOid}/records`;
|
|
25
|
+
return await ccwAxios
|
|
26
|
+
.get(requestUrl)
|
|
27
|
+
.then((res) => res.data.body);
|
|
28
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getLeaderboardRecords, submitLeaderboardRecord } from "./records";
|
|
2
|
+
test("submit leaderboard record should fail without token", async () => {
|
|
3
|
+
await expect(submitLeaderboardRecord("65a2314daa96bd09b5beae5a", 0, "65a2314daa96bd09b5beae5a", "")).rejects.toThrow();
|
|
4
|
+
});
|
|
5
|
+
test("get leaderboard records should return data", async () => {
|
|
6
|
+
const res = await getLeaderboardRecords("65a2314daa96bd09b5beae5a");
|
|
7
|
+
expect(res.oid).toBe("65a2314daa96bd09b5beae5a");
|
|
8
|
+
expect(res.title).toBeDefined();
|
|
9
|
+
expect(res.leaderboardRecordTotalNum).toBeDefined();
|
|
10
|
+
expect(Array.isArray(res.leaderboardRecords)).toBe(true);
|
|
11
|
+
});
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { getCurrentTimestamp } from "./timestamp/current";
|
|
2
2
|
import { getBulletinsPage } from "./bulletins/page";
|
|
3
3
|
import { getBulletinDetail } from "./bulletins/detail";
|
|
4
|
+
import { getLeaderboardRecords, submitLeaderboardRecord, } from "./creation/leaderboards/{oid}/records";
|
|
4
5
|
export const gandiMain = {
|
|
5
6
|
getCurrentTimestamp,
|
|
6
7
|
getBulletinsPage,
|
|
7
8
|
getBulletinDetail,
|
|
9
|
+
submitLeaderboardRecord,
|
|
10
|
+
getLeaderboardRecords,
|
|
8
11
|
};
|
package/dist/esm/index.js
CHANGED
|
@@ -2,11 +2,15 @@ import { sso } from "./sso/index";
|
|
|
2
2
|
import { communityWeb } from "./community-web";
|
|
3
3
|
import { gandiMain } from "./gandi-main";
|
|
4
4
|
import { bfsWeb } from "./bfs-web";
|
|
5
|
-
|
|
5
|
+
import { communityWebCloudDatabase } from "./community-web-cloud-database";
|
|
6
|
+
import { opParentApi } from "./op-parent-api";
|
|
7
|
+
export { sso, communityWeb, gandiMain, bfsWeb, communityWebCloudDatabase, opParentApi, };
|
|
6
8
|
export { setToken } from "@ccw-api/axios";
|
|
7
9
|
export default {
|
|
8
10
|
sso,
|
|
9
11
|
communityWeb,
|
|
10
12
|
gandiMain,
|
|
11
13
|
bfsWeb,
|
|
14
|
+
communityWebCloudDatabase,
|
|
15
|
+
opParentApi,
|
|
12
16
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://op-parent-api.xiguacity.cn/captcha/check";
|
|
3
|
+
/**
|
|
4
|
+
* 校验图形验证码(拼图滑块坐标)
|
|
5
|
+
* @param {string} token createCaptcha 返回的 token
|
|
6
|
+
* @param {number} x 拼图滑块目标位置的 x 坐标
|
|
7
|
+
* @param {number} y 拼图滑块目标位置的 y 坐标
|
|
8
|
+
* @returns {Promise<Res>} 校验结果
|
|
9
|
+
*/
|
|
10
|
+
export async function checkCaptcha(token, x, y = 5) {
|
|
11
|
+
const req = { data: { x, y }, token };
|
|
12
|
+
return await ccwAxios
|
|
13
|
+
.post(url, req)
|
|
14
|
+
.then((res) => res.data.body);
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { checkCaptcha } from "./check";
|
|
2
|
+
import { createCaptcha } from "./create";
|
|
3
|
+
test("checkCaptcha returns structured result with token and coords without auth", async () => {
|
|
4
|
+
const created = await createCaptcha("BLOCK_PUZZLE");
|
|
5
|
+
const res = await checkCaptcha(created.token, 0, -1);
|
|
6
|
+
expect(res).toHaveProperty("captchaExpired");
|
|
7
|
+
expect(res).toHaveProperty("errorMsg");
|
|
8
|
+
expect(res).toHaveProperty("success");
|
|
9
|
+
expect(res.captchaExpired).toBeFalsy();
|
|
10
|
+
expect(res.success).toBeFalsy();
|
|
11
|
+
expect(res.errorMsg).toEqual("验证失败");
|
|
12
|
+
}, 30000);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://op-parent-api.xiguacity.cn/captcha/create";
|
|
3
|
+
/**
|
|
4
|
+
* 创建图形验证码(拼图滑块)
|
|
5
|
+
* @param {CaptchaType} type 验证码类型,默认 BLOCK_PUZZLE(滑块拼图)
|
|
6
|
+
* @returns {Promise<Res>} 验证码信息:bgImg为背景图base64 data url,jigsawImg为拼图块base64 data url,token用于后续校验
|
|
7
|
+
*/
|
|
8
|
+
export async function createCaptcha(type = "BLOCK_PUZZLE") {
|
|
9
|
+
const req = { type };
|
|
10
|
+
return await ccwAxios
|
|
11
|
+
.post(url, req)
|
|
12
|
+
.then((res) => res.data.body);
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createCaptcha } from "./create";
|
|
2
|
+
test("create BLOCK_PUZZLE captcha returns bgImg, jigsawImg and token without token", async () => {
|
|
3
|
+
const res = await createCaptcha("BLOCK_PUZZLE");
|
|
4
|
+
expect(res).toHaveProperty("data");
|
|
5
|
+
expect(res.data).toHaveProperty("bgImg");
|
|
6
|
+
expect(res.data).toHaveProperty("jigsawImg");
|
|
7
|
+
expect(res).toHaveProperty("token");
|
|
8
|
+
expect(typeof res.token).toBe("string");
|
|
9
|
+
expect(typeof res.data.bgImg).toBe("string");
|
|
10
|
+
expect(typeof res.data.jigsawImg).toBe("string");
|
|
11
|
+
expect(res.data.bgImg.startsWith("data:image/png;base64,")).toBeTruthy();
|
|
12
|
+
expect(res.data.jigsawImg.startsWith("data:image/png;base64,")).toBeTruthy();
|
|
13
|
+
}, 30000);
|
package/dist/esm/sso/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { loginByPassword } from "./web/auth/login-by-password";
|
|
2
2
|
import { logout } from "./web/auth/logout";
|
|
3
3
|
import { logoutBySession } from "./web/auth/logout_by_session";
|
|
4
|
+
import { createSmsCaptcha } from "./web/auth/assistant/captcha/v2/create";
|
|
5
|
+
import { loginByPhone } from "./web/auth/v3/login/by-phone";
|
|
4
6
|
export const sso = {
|
|
5
7
|
loginByPassword,
|
|
6
8
|
logout,
|
|
7
9
|
logoutBySession,
|
|
10
|
+
createSmsCaptcha,
|
|
11
|
+
loginByPhone,
|
|
8
12
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://sso.ccw.site/web/auth/assistant/captcha/v2/create";
|
|
3
|
+
/**
|
|
4
|
+
* 发送短信验证码
|
|
5
|
+
* @param {string} recipient 接收手机号,如 "10011451919"
|
|
6
|
+
* @param {SmsScene} scene 场景,默认 loginbyPhone
|
|
7
|
+
* @param {string} countryCode 国家代码,默认 "86"
|
|
8
|
+
* @param {SmsRuleCode} ruleCode 短信规则编码,默认 "ccw_sms"
|
|
9
|
+
* @param {CaptchaPayload} captcha 图形验证码校验结果(滑块拼图的坐标与token),可选(部分场景未强制要求时可省略,但失败时sendResult!=1)
|
|
10
|
+
* @returns {Promise<Res>} 发送结果:batchId 为短信批次ID,sendResult=1 表示成功
|
|
11
|
+
*/
|
|
12
|
+
export async function createSmsCaptcha(recipient, scene = "loginbyPhone", countryCode = "86", ruleCode = "ccw_sms", captcha) {
|
|
13
|
+
const req = { recipient, countryCode, scene, ruleCode, captcha };
|
|
14
|
+
return await ccwAxios
|
|
15
|
+
.post(url, req)
|
|
16
|
+
.then((res) => res.data.body);
|
|
17
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createSmsCaptcha } from "./create";
|
|
2
|
+
test("createSmsCaptchaV2 returns batchId and sendResult fields without token", async () => {
|
|
3
|
+
const res = await createSmsCaptcha("12345671145", "loginbyPhone", "86", "ccw_sms");
|
|
4
|
+
expect(res).toHaveProperty("batchId");
|
|
5
|
+
expect(res).toHaveProperty("sendResult");
|
|
6
|
+
expect(res.batchId).toBeNull();
|
|
7
|
+
expect(res.sendResult).toBe(2);
|
|
8
|
+
}, 30000);
|
|
@@ -11,13 +11,14 @@ export async function loginByPassword(loginKey, password, reqExtra = {
|
|
|
11
11
|
device: "Node",
|
|
12
12
|
browser: "Node.js",
|
|
13
13
|
}) {
|
|
14
|
-
const
|
|
15
|
-
.post(url, {
|
|
14
|
+
const req = {
|
|
16
15
|
loginKey,
|
|
17
16
|
password,
|
|
18
17
|
clientCode: "STUDY_COMMUNITY",
|
|
19
18
|
extra: JSON.stringify({ ...reqExtra, scene: null }),
|
|
20
|
-
}
|
|
19
|
+
};
|
|
20
|
+
const { extra, ...restBody } = await ccwAxios
|
|
21
|
+
.post(url, req)
|
|
21
22
|
.then((res) => res.data.body);
|
|
22
23
|
const resExtra = JSON.parse(extra);
|
|
23
24
|
return {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ccwAxios } from "@ccw-api/axios";
|
|
2
|
+
export const url = "https://sso.ccw.site/web/auth/v3/login/by_phone";
|
|
3
|
+
/**
|
|
4
|
+
* 通过手机号+短信验证码登录(V3)
|
|
5
|
+
* @param {string} phone 手机号,如 "12345671145"
|
|
6
|
+
* @param {string} code 收到的短信验证码
|
|
7
|
+
* @param {ReqExtra} reqExtra 设备和浏览器信息
|
|
8
|
+
* @param {string} countryCode 国家代码,默认 "86"
|
|
9
|
+
* @param {LoginRuleCode} ruleCode 短信规则编码,默认 "ccw_sms"
|
|
10
|
+
* @param {LoginScene} scene 登录场景,默认 "loginbyPhone"
|
|
11
|
+
* @returns {Promise<Res<Extra>>} session 信息
|
|
12
|
+
*/
|
|
13
|
+
export async function loginByPhone(phone, code, reqExtra = {
|
|
14
|
+
device: "Node",
|
|
15
|
+
browser: "Node.js",
|
|
16
|
+
}, countryCode = "86", ruleCode = "ccw_sms", scene = "loginbyPhone") {
|
|
17
|
+
const { extra, ...restBody } = await ccwAxios
|
|
18
|
+
.post(url, {
|
|
19
|
+
phone,
|
|
20
|
+
code,
|
|
21
|
+
countryCode,
|
|
22
|
+
ruleCode,
|
|
23
|
+
scene,
|
|
24
|
+
clientCode: "STUDY_COMMUNITY",
|
|
25
|
+
extra: JSON.stringify({ ...reqExtra, scene: null }),
|
|
26
|
+
})
|
|
27
|
+
.then((res) => res.data.body);
|
|
28
|
+
const resExtra = JSON.parse(extra);
|
|
29
|
+
return {
|
|
30
|
+
...restBody,
|
|
31
|
+
extra: resExtra,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -13,7 +13,7 @@ const dpa = {
|
|
|
13
13
|
* 根据主题获取评论列表
|
|
14
14
|
* @param {string} subjectOid 主题oid
|
|
15
15
|
* @param {SubjectType} subjectType 主题类型 (如: "POST")
|
|
16
|
-
* @param {
|
|
16
|
+
* @param {SectionType} sectionType 区块类型 (如: "COMMENT")
|
|
17
17
|
* @param {Partial<PageArgs<SortField| T>>} pageArgs_ 分页参数
|
|
18
18
|
* @returns {Promise<Res>} 评论分页结果
|
|
19
19
|
*/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.url = void 0;
|
|
4
|
+
exports.updateGreatCreation = updateGreatCreation;
|
|
5
|
+
const axios_1 = require("@ccw-api/axios");
|
|
6
|
+
exports.url = "https://community-web.ccw.site/great_creation/update";
|
|
7
|
+
/**
|
|
8
|
+
* 更换代表作
|
|
9
|
+
* @param {MongoDBId|""} creationOid 作品OID 若为""则是取消展示
|
|
10
|
+
* @returns {Promise<Res>} 更换结果
|
|
11
|
+
*/
|
|
12
|
+
async function updateGreatCreation(creationOid) {
|
|
13
|
+
const req = { creationOid };
|
|
14
|
+
return await axios_1.ccwAxios
|
|
15
|
+
.post(exports.url, req)
|
|
16
|
+
.then((res) => res.data.body);
|
|
17
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const update_1 = require("./update");
|
|
4
|
+
test("should fail without token when updating great creation", async () => {
|
|
5
|
+
await expect((0, update_1.updateGreatCreation)("69740f1a61b891733d5ee2c6")).rejects.toThrow();
|
|
6
|
+
});
|