mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-08-12 17:10:37 +00:00
feat: 新增云小编抽奖接口
This commit is contained in:
parent
20d3a1a7a5
commit
64a6b9677a
24
module/middle_play_do_lottery.js
Normal file
24
module/middle_play_do_lottery.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 云小编每日抽奖
|
||||||
|
//
|
||||||
|
// activityId:
|
||||||
|
// 默认 6501202
|
||||||
|
//
|
||||||
|
// drawCount:
|
||||||
|
// 默认 1
|
||||||
|
//
|
||||||
|
// checkToken:
|
||||||
|
// 易盾反作弊 Token
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
const data = {
|
||||||
|
activityId: query.activityId || '6501202',
|
||||||
|
drawCount: query.drawCount || '1',
|
||||||
|
}
|
||||||
|
return request(
|
||||||
|
`/api/middle/play/do/lottery`,
|
||||||
|
data,
|
||||||
|
createOption(query, 'eapi', 'v3'),
|
||||||
|
)
|
||||||
|
}
|
||||||
17
module/middle_play_lottery_remain_chance.js
Normal file
17
module/middle_play_lottery_remain_chance.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 云小编抽奖剩余次数查询
|
||||||
|
//
|
||||||
|
// activityId:
|
||||||
|
// 默认 6501202
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
const data = {
|
||||||
|
activityId: query.activityId || '6501202',
|
||||||
|
}
|
||||||
|
return request(
|
||||||
|
`/api/middle/play/lottery/remain/chance`,
|
||||||
|
data,
|
||||||
|
createOption(query, 'eapi'),
|
||||||
|
)
|
||||||
|
}
|
||||||
28
module/rep_ugc_exam_info_get.js
Normal file
28
module/rep_ugc_exam_info_get.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 云小编考试状态
|
||||||
|
//
|
||||||
|
// examType:
|
||||||
|
// 1. 歌曲曲风审核: musicalStyleEnter
|
||||||
|
// 2. 歌曲语种审核: languageEnter
|
||||||
|
// 3. 歌曲原唱审核: oriSingerEnter
|
||||||
|
// 4. 情绪标签审核: emotionEnter
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
if (!query.examType)
|
||||||
|
return Promise.reject({
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
code: 400,
|
||||||
|
message: '参数不足',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const data = {
|
||||||
|
examType: query.examType,
|
||||||
|
}
|
||||||
|
return request(
|
||||||
|
'/api/rep/ugc/exam/info/get',
|
||||||
|
data,
|
||||||
|
createOption(query, 'eapi'),
|
||||||
|
)
|
||||||
|
}
|
||||||
32
module/rep_ugc_exam_question_single_get.js
Normal file
32
module/rep_ugc_exam_question_single_get.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 云小编考试取题
|
||||||
|
//
|
||||||
|
// examType:
|
||||||
|
// 1. 歌曲曲风审核: musicalStyleEnter
|
||||||
|
// 2. 歌曲语种审核: languageEnter
|
||||||
|
// 3. 歌曲原唱审核: oriSingerEnter
|
||||||
|
// 4. 情绪标签审核: emotionEnter
|
||||||
|
//
|
||||||
|
// taskId:
|
||||||
|
// 首次调用 rep/ugc/exam/start 获取,之后调用 rep/ugc/exam/info/get 获取
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
if (!query.examType || !query.taskId)
|
||||||
|
return Promise.reject({
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
code: 400,
|
||||||
|
message: '参数不足',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const data = {
|
||||||
|
examType: query.examType,
|
||||||
|
taskId: query.taskId,
|
||||||
|
}
|
||||||
|
return request(
|
||||||
|
'/api/rep/ugc/exam/question/single/get',
|
||||||
|
data,
|
||||||
|
createOption(query, 'eapi'),
|
||||||
|
)
|
||||||
|
}
|
||||||
32
module/rep_ugc_exam_result_get.js
Normal file
32
module/rep_ugc_exam_result_get.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 云小编考试结果
|
||||||
|
//
|
||||||
|
// examType:
|
||||||
|
// 1. 歌曲曲风审核: musicalStyleEnter
|
||||||
|
// 2. 歌曲语种审核: languageEnter
|
||||||
|
// 3. 歌曲原唱审核: oriSingerEnter
|
||||||
|
// 4. 情绪标签审核: emotionEnter
|
||||||
|
//
|
||||||
|
// taskId:
|
||||||
|
// 首次调用 rep/ugc/exam/start 获取,之后调用 rep/ugc/exam/info/get 获取
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
if (!query.examType || !query.taskId)
|
||||||
|
return Promise.reject({
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
code: 400,
|
||||||
|
message: '参数不足',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const data = {
|
||||||
|
examType: query.examType,
|
||||||
|
taskId: query.taskId,
|
||||||
|
}
|
||||||
|
return request(
|
||||||
|
'/api/rep/ugc/exam/result/get',
|
||||||
|
data,
|
||||||
|
createOption(query, 'eapi'),
|
||||||
|
)
|
||||||
|
}
|
||||||
24
module/rep_ugc_exam_start.js
Normal file
24
module/rep_ugc_exam_start.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 云小编考试开始
|
||||||
|
//
|
||||||
|
// examType:
|
||||||
|
// 1. 歌曲曲风审核: musicalStyleEnter
|
||||||
|
// 2. 歌曲语种审核: languageEnter
|
||||||
|
// 3. 歌曲原唱审核: oriSingerEnter
|
||||||
|
// 4. 情绪标签审核: emotionEnter
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
if (!query.examType)
|
||||||
|
return Promise.reject({
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
code: 400,
|
||||||
|
message: '参数不足',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const data = {
|
||||||
|
examType: query.examType,
|
||||||
|
}
|
||||||
|
return request('/api/rep/ugc/exam/start', data, createOption(query, 'eapi'))
|
||||||
|
}
|
||||||
36
module/rep_ugc_exam_submit.js
Normal file
36
module/rep_ugc_exam_submit.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 云小编考试提交
|
||||||
|
//
|
||||||
|
// examType:
|
||||||
|
// 1. 歌曲曲风审核: musicalStyleEnter
|
||||||
|
// 2. 歌曲语种审核: languageEnter
|
||||||
|
// 3. 歌曲原唱审核: oriSingerEnter
|
||||||
|
// 4. 情绪标签审核: emotionEnter
|
||||||
|
//
|
||||||
|
// taskId:
|
||||||
|
// 首次调用 rep/ugc/exam/start 获取,之后调用 rep/ugc/exam/info/get 获取
|
||||||
|
//
|
||||||
|
// questionId:
|
||||||
|
// 调用 rep/ugc/exam/question/single/get 获取
|
||||||
|
//
|
||||||
|
// answer:
|
||||||
|
// A 对, B 错
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
if (!query.examType || !query.taskId || !query.questionId || !query.answer)
|
||||||
|
return Promise.reject({
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
code: 400,
|
||||||
|
message: '参数不足',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const data = {
|
||||||
|
examType: query.examType,
|
||||||
|
taskId: query.taskId,
|
||||||
|
questionId: query.questionId,
|
||||||
|
answer: query.answer,
|
||||||
|
}
|
||||||
|
return request('/api/rep/ugc/exam/submit', data, createOption(query, 'eapi'))
|
||||||
|
}
|
||||||
@ -5604,6 +5604,104 @@ let data = encodeURIComponent(
|
|||||||
|
|
||||||
**调用例子:** `/rep/ugc/user/collect-vip?activityId=5001`
|
**调用例子:** `/rep/ugc/user/collect-vip?activityId=5001`
|
||||||
|
|
||||||
|
### 云小编 - 剩余抽奖次数
|
||||||
|
|
||||||
|
说明: 登录后调用此接口, 获取今日云小编抽奖剩余次数
|
||||||
|
|
||||||
|
**可选参数:**
|
||||||
|
|
||||||
|
`activityId`: 活动 ID, 默认 `6501202`
|
||||||
|
|
||||||
|
**接口地址:** `/middle/play/lottery/remain/chance`
|
||||||
|
|
||||||
|
**调用例子:** `/middle/play/lottery/remain/chance?activityId=6501202`
|
||||||
|
|
||||||
|
### 云小编 - 每日抽奖
|
||||||
|
|
||||||
|
说明: 登录后调用此接口, 消耗 200 积分进行抽奖, 每日最多抽 3 次
|
||||||
|
|
||||||
|
> 注意: 抽奖失败也消耗每日次数, 请先调用 `/rep/ugc/user/get` 查询可用积分
|
||||||
|
|
||||||
|
**可选参数:**
|
||||||
|
|
||||||
|
`activityId`: 活动 ID, 默认 `6501202`
|
||||||
|
|
||||||
|
`drawCount`: 未知, 默认 `1`
|
||||||
|
|
||||||
|
**接口地址:** `/middle/play/do/lottery`
|
||||||
|
|
||||||
|
**调用例子:** `/middle/play/do/lottery?activityId=6501202&drawCount=1`
|
||||||
|
|
||||||
|
### 云小编 - 考试状态
|
||||||
|
|
||||||
|
说明: 登录后调用此接口, 查询云小编入站考试状态, 进度 (`data.process`): no: 未开始, process: 考试中, whole_exam_end: 已结束, 此时可检查 `data.hasPassExamination` 确认是否通过
|
||||||
|
|
||||||
|
**必选参数:**
|
||||||
|
|
||||||
|
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||||
|
|
||||||
|
**接口地址:** `/rep/ugc/exam/info/get`
|
||||||
|
|
||||||
|
**调用例子:** `/rep/ugc/exam/info/get?examType=emotionEnter`
|
||||||
|
|
||||||
|
### 云小编 - 考试开始
|
||||||
|
|
||||||
|
说明: 未考试或考试失败时调用此接口, 开始新一轮考试
|
||||||
|
|
||||||
|
**必选参数:**
|
||||||
|
|
||||||
|
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||||
|
|
||||||
|
**接口地址:** `/rep/ugc/exam/start`
|
||||||
|
|
||||||
|
**调用例子:** `/rep/ugc/exam/start?examType=emotionEnter`
|
||||||
|
|
||||||
|
### 云小编 - 考试取题
|
||||||
|
|
||||||
|
说明: 考试开始后调用此接口, 获取当前试题
|
||||||
|
|
||||||
|
**必选参数:**
|
||||||
|
|
||||||
|
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||||
|
|
||||||
|
`taskId`: 任务 ID, 首次调用 `/rep/ugc/exam/start` 获取,之后调用 `/rep/ugc/exam/info/get` 获取
|
||||||
|
|
||||||
|
**接口地址:** `/rep/ugc/exam/question/single/get`
|
||||||
|
|
||||||
|
**调用例子:** `/rep/ugc/exam/question/single/get?examType=emotionEnter&taskId=123456`
|
||||||
|
|
||||||
|
### 云小编 - 考试提交
|
||||||
|
|
||||||
|
说明: 取题后调用此接口, 提交作答结果
|
||||||
|
|
||||||
|
**必选参数:**
|
||||||
|
|
||||||
|
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||||
|
|
||||||
|
`taskId`: 任务 ID, 首次调用 `/rep/ugc/exam/start` 获取,之后调用 `/rep/ugc/exam/info/get` 获取
|
||||||
|
|
||||||
|
`questionId`: 试题 ID, 调用 `/rep/ugc/exam/question/single/get` 获取
|
||||||
|
|
||||||
|
`answer`: 判断结果, A: 对, B: 错
|
||||||
|
|
||||||
|
**接口地址:** `/rep/ugc/exam/submit`
|
||||||
|
|
||||||
|
**调用例子:** `/rep/ugc/exam/submit?examType=emotionEnter&taskId=123456&questionId=123456&answer=A`
|
||||||
|
|
||||||
|
### 云小编 - 考试结果
|
||||||
|
|
||||||
|
说明: 考试结束后调用此接口, 获取考试正确数与正确率
|
||||||
|
|
||||||
|
**必选参数:**
|
||||||
|
|
||||||
|
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||||
|
|
||||||
|
`taskId`: 任务 ID, 首次调用 `/rep/ugc/exam/start` 获取,之后调用 `/rep/ugc/exam/info/get` 获取
|
||||||
|
|
||||||
|
**接口地址:** `/rep/ugc/exam/result/get`
|
||||||
|
|
||||||
|
**调用例子:** `/rep/ugc/exam/result/get?examType=emotionEnter&taskId=123456`
|
||||||
|
|
||||||
### 发送/删除评论
|
### 发送/删除评论
|
||||||
|
|
||||||
说明 : 调用此接口,可发送评论或者删除评论
|
说明 : 调用此接口,可发送评论或者删除评论
|
||||||
|
|||||||
@ -99,7 +99,8 @@ curl -s {origin}/search?keywords=网易云</code></pre>
|
|||||||
<a href="/scrobble.html">听歌打卡示例</a> ·
|
<a href="/scrobble.html">听歌打卡示例</a> ·
|
||||||
<a href="/yidun.html">获取 CheckToken</a> ·
|
<a href="/yidun.html">获取 CheckToken</a> ·
|
||||||
<a href="/free_listen.html">免费听权益</a> ·
|
<a href="/free_listen.html">免费听权益</a> ·
|
||||||
<a href="/ugc.html">云小编任务中心</a>
|
<a href="/ugc.html">云小编任务中心</a> ·
|
||||||
|
<a href="/ugc_lottery.html">云小编每日抽奖</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
212
public/ugc.html
212
public/ugc.html
@ -4,6 +4,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
<title>云小编任务中心 - 网易云音乐 API Enhanced</title>
|
<title>云小编任务中心 - 网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -325,6 +327,10 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#userPoint {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Song Review ---- */
|
/* ---- Song Review ---- */
|
||||||
.song-review {
|
.song-review {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
@ -377,7 +383,7 @@
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
max-height: 120px;
|
max-height: 160px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
@ -404,6 +410,8 @@
|
|||||||
<div class="quick-fill">
|
<div class="quick-fill">
|
||||||
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
||||||
<span onclick="clearCookie()">清除 Cookie</span>
|
<span onclick="clearCookie()">清除 Cookie</span>
|
||||||
|
<span onclick="saveCookie()">保存 Cookie</span>
|
||||||
|
<span onclick="loadAll()">一键载入</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -413,8 +421,9 @@
|
|||||||
<button class="btn-sm" id="userGet">获取用户详情</button>
|
<button class="btn-sm" id="userGet">获取用户详情</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-card" id="user">
|
<div class="info-card" id="user">
|
||||||
<img alt="头像" class="avatar" id="userAvatar" />
|
<img src="docs/netease.png" alt="头像" class="avatar" id="userAvatar" />
|
||||||
<span class="info-text" id="userName">未登录</span>
|
<span class="info-text" id="userName">未登录</span>
|
||||||
|
<span id="userPoint">0</span>
|
||||||
<button class="btn-sm green" id="userSign" disabled>签到</button>
|
<button class="btn-sm green" id="userSign" disabled>签到</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -436,7 +445,7 @@
|
|||||||
<button class="btn-sm" id="taskGet">获取任务详情</button>
|
<button class="btn-sm" id="taskGet">获取任务详情</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-card" id="task">
|
<div class="info-card" id="task">
|
||||||
<span class="info-text" id="taskInfo">[<span id="taskCount">0</span>/<span id="taskGoal">0</span>] 审核歌曲赚贡献分 (+66)</span>
|
<span class="info-text" id="taskInfo">[<span id="taskCount">0</span>/<span id="taskGoal">0</span>] 审核歌曲赚贡献分 (+<span id="taskPoint">0</span>)</span>
|
||||||
<button class="btn-sm" id="taskStart" disabled>去完成</button>
|
<button class="btn-sm" id="taskStart" disabled>去完成</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -462,7 +471,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
var k_cookie = 'ncm_cookie';
|
var k_cookie = 'ncm_cookie';
|
||||||
@ -496,6 +504,12 @@
|
|||||||
showResult('已清除本地 Cookie', 'info');
|
showResult('已清除本地 Cookie', 'info');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.saveCookie = function () {
|
||||||
|
var ck = getCookie()
|
||||||
|
if (ck) localStorage.setItem(k_cookie, ck)
|
||||||
|
showResult('已保存本地 Cookie', 'info');
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Result ---- */
|
/* ---- Result ---- */
|
||||||
function hideResult() {
|
function hideResult() {
|
||||||
resultDiv.style.display = 'none';
|
resultDiv.style.display = 'none';
|
||||||
@ -547,6 +561,7 @@
|
|||||||
/// 用户信息 ///
|
/// 用户信息 ///
|
||||||
var userAvatar = document.getElementById('userAvatar');
|
var userAvatar = document.getElementById('userAvatar');
|
||||||
var userName = document.getElementById('userName');
|
var userName = document.getElementById('userName');
|
||||||
|
var userPoint = document.getElementById('userPoint');
|
||||||
var userSign = document.getElementById('userSign');
|
var userSign = document.getElementById('userSign');
|
||||||
var userGet = document.getElementById('userGet');
|
var userGet = document.getElementById('userGet');
|
||||||
|
|
||||||
@ -555,6 +570,7 @@
|
|||||||
var d = res.data;
|
var d = res.data;
|
||||||
if (d.imgUrl) userAvatar.src = d.imgUrl + '?param=50y50';
|
if (d.imgUrl) userAvatar.src = d.imgUrl + '?param=50y50';
|
||||||
userName.textContent = d.name || '游客' + d.id;
|
userName.textContent = d.name || '游客' + d.id;
|
||||||
|
userPoint.textContent = d.availablePoints;
|
||||||
userSign.disabled = d.signed;
|
userSign.disabled = d.signed;
|
||||||
showResult('用户信息加载成功', 'success');
|
showResult('用户信息加载成功', 'success');
|
||||||
});
|
});
|
||||||
@ -567,6 +583,7 @@
|
|||||||
var d = res.data;
|
var d = res.data;
|
||||||
userSign.disabled = d;
|
userSign.disabled = d;
|
||||||
showResult('签到' + (d ? '成功 ✓' : '失败 ✗'), d ? 'success' : 'error');
|
showResult('签到' + (d ? '成功 ✓' : '失败 ✗'), d ? 'success' : 'error');
|
||||||
|
if (d) userGet.click();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -599,21 +616,36 @@
|
|||||||
/// 审核任务 ///
|
/// 审核任务 ///
|
||||||
var taskCount = document.getElementById('taskCount');
|
var taskCount = document.getElementById('taskCount');
|
||||||
var taskGoal = document.getElementById('taskGoal');
|
var taskGoal = document.getElementById('taskGoal');
|
||||||
|
var taskPoint = document.getElementById('taskPoint');
|
||||||
var taskStartBtn = document.getElementById('taskStart');
|
var taskStartBtn = document.getElementById('taskStart');
|
||||||
var taskGetBtn = document.getElementById('taskGet');
|
var taskGetBtn = document.getElementById('taskGet');
|
||||||
|
|
||||||
taskGetBtn.onclick = function () {
|
taskGetBtn.onclick = function () {
|
||||||
request('/rep/ugc/activity/get', {}, function (res) {
|
request('rep/ugc/activity/get', {}, function (res) {
|
||||||
var d = res.data;
|
var d = res.data;
|
||||||
taskCount.textContent = d.count;
|
taskCount.textContent = d.count;
|
||||||
taskGoal.textContent = d.goalCount;
|
taskGoal.textContent = d.goalCount;
|
||||||
taskStartBtn.disabled = d.activityId !== 5001;
|
taskPoint.textContent = d.points;
|
||||||
|
// 注:超过 100 可能有 bug,导致 thinktank/audit/resource/detail 获取的任务 ID 一直是 5002,目标数量也不对,暂时限制前两个任务
|
||||||
|
taskStartBtn.disabled = d.activityId !== 5001 && d.activityId !== 5002; // 5001: 1-50, 5002: 51-100
|
||||||
showResult('任务详情加载成功', 'success');
|
showResult('任务详情加载成功', 'success');
|
||||||
|
// 任务完成,领取积分
|
||||||
|
if (d.count >= d.goalCount) {
|
||||||
|
request('rep/ugc/activity/collect', { activityId: d.activityId }, function (res) {
|
||||||
|
var d = res.data;
|
||||||
|
showResult('积分领取' + (d ? '成功,可以兑换会员了!' : '失败'), d ? 'success' : 'error');
|
||||||
|
// 刷新任务状态
|
||||||
|
if (d) loadAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
taskStartBtn.onclick = function () {
|
taskStartBtn.onclick = function () {
|
||||||
if (!taskStartBtn.disabled) taskStart();
|
if (!taskStartBtn.disabled) {
|
||||||
|
songAudio.pause();
|
||||||
|
examInit();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 歌曲审核区域 ///
|
/// 歌曲审核区域 ///
|
||||||
@ -627,26 +659,113 @@
|
|||||||
var songSkip = document.getElementById('songSkip');
|
var songSkip = document.getElementById('songSkip');
|
||||||
var songLyric = document.getElementById('songLyric');
|
var songLyric = document.getElementById('songLyric');
|
||||||
|
|
||||||
|
var examType = 'emotionEnter';
|
||||||
|
// 考试初始化、检查状态
|
||||||
|
function examInit() {
|
||||||
|
request('rep/ugc/exam/info/get', { examType: examType }, function (res) {
|
||||||
|
var d = res.data;
|
||||||
|
switch (d.process) {
|
||||||
|
case 'whole_exam_end':
|
||||||
|
// 通过考试
|
||||||
|
if (d.hasPassExamination) {
|
||||||
|
taskStart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request('rep/ugc/exam/result/get', { examType: examType, taskId: d.taskId }, function (res) {
|
||||||
|
var d = res.data;
|
||||||
|
showResult('考试未通过:正确数:' + d.wrightNum + ',正确率:' + d.wrightRate, 'error');
|
||||||
|
});
|
||||||
|
case 'no':
|
||||||
|
// 未通过或未开始
|
||||||
|
request('rep/ugc/exam/start', { examType: examType }, function (res) {
|
||||||
|
examStart(res.data.taskId);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'process':
|
||||||
|
// 正在考试
|
||||||
|
examStart(d.taskId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
showResult('未知考试进度:' + d.process, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 在上个信息不为异常时显示考试中提示
|
||||||
|
if (d.process !== 'whole_exam_end' && !resultDiv.className.endsWith('error')) showResult('正在考试,请认真审题!', 'info');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 考试取题开始
|
||||||
|
function examStart(taskId) {
|
||||||
|
request('rep/ugc/exam/question/single/get', { examType: examType, taskId: taskId }, function (res) {
|
||||||
|
var d = res.data;
|
||||||
|
|
||||||
|
songCover.src = d.coverUrl + '?param=100y100';
|
||||||
|
songTitle.textContent = '[' + d.resId + '] ' + d.resName + ' - ' + d.artists;
|
||||||
|
songTag.textContent = '[' + (d.auditCount + 1) + '/' + d.auditTaskCount + '] 当前结果: ' + d.questionContent + '?';
|
||||||
|
songLyric.textContent = d.lyric ? d.transLyric ? lrctran(d.lyric, d.transLyric) : d.lyric : '[00:00.00] 暂无歌词';
|
||||||
|
songLyric.scrollTop = 0;
|
||||||
|
// 有时候就是没有播放链接,只能根据歌词硬审
|
||||||
|
if (d.songUrl) {
|
||||||
|
songAudio.src = d.songUrl;
|
||||||
|
songAudio.play().catch(function () { /* 自动播放被浏览器阻止 */ });
|
||||||
|
} else {
|
||||||
|
songAudio.src = ''; // 清理播放器
|
||||||
|
}
|
||||||
|
songApprove.onclick = function () { if (!songApprove.disabled) examSubmit(taskId, d.questionId, 'A') };
|
||||||
|
songReject.onclick = function () { if (!songReject.disabled) examSubmit(taskId, d.questionId, 'B') };
|
||||||
|
songSkip.onclick = null;
|
||||||
|
|
||||||
|
songApprove.disabled = songReject.disabled = songSkip.disabled = true;
|
||||||
|
song.style.display = 'block';
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
songApprove.disabled = songReject.disabled = false;
|
||||||
|
}, 3e3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 考试提交
|
||||||
|
function examSubmit(taskId, questionId, answer) {
|
||||||
|
songAudio.pause();
|
||||||
|
songApprove.disabled = songReject.disabled = songSkip.disabled = true;
|
||||||
|
request(
|
||||||
|
'rep/ugc/exam/submit',
|
||||||
|
{ examType: examType, taskId: taskId, questionId: questionId, answer: answer },
|
||||||
|
function (res) {
|
||||||
|
var d = res.data;
|
||||||
|
if (d.result) {
|
||||||
|
showResult('回答正确,加载下一首...', 'success');
|
||||||
|
} else {
|
||||||
|
showResult('回答错误:' + d.analysis, 'error');
|
||||||
|
}
|
||||||
|
examInit();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取任务
|
* 获取任务
|
||||||
*/
|
*/
|
||||||
function taskStart() {
|
function taskStart() {
|
||||||
song.style.display = 'none';
|
request('thinktank/audit/resource/detail', { type: '4' }, function (res) {
|
||||||
request('/thinktank/audit/resource/detail', { type: '4' }, function (res) {
|
|
||||||
var d = res.data;
|
var d = res.data;
|
||||||
|
|
||||||
if (d.auditCount >= 50) {
|
if (d.auditCount >= Number(taskGoal.textContent)) {
|
||||||
request('rep/ugc/activity/collect', { activityId: '5001' }, function (res) {
|
showResult('当前阶段任务已完成', 'success');
|
||||||
showResult('积分领取' + (res.data ? '成功,可以兑换会员了!' : '失败'), res.data ? 'success' : 'error');
|
song.style.display = 'none';
|
||||||
});
|
taskGetBtn.click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 没有链接,直接跳过
|
||||||
|
if (!d.songUrl) {
|
||||||
|
showResult('未返回播放链接,3 秒后自动跳过', 'info');
|
||||||
|
setTimeout(function () { taskSubmit(d.taskId, '3') }, 3e3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
songCover.src = d.coverUrl + '?param=100y100';
|
songCover.src = d.coverUrl + '?param=100y100';
|
||||||
songTitle.textContent = '[' + d.resId + '] ' + d.resName + ' - ' + d.artists;
|
songTitle.textContent = '[' + d.resId + '] ' + d.resName + ' - ' + d.artists;
|
||||||
songTag.textContent = '[' + (d.auditCount + 1) + '/' + d.auditTaskCount + '] 当前结果: ' + d.initResult + '?';
|
songTag.textContent = '[' + (d.auditCount + 1) + '/' + d.auditTaskCount + '] 当前结果: ' + d.initResult + '?';
|
||||||
songLyric.textContent = (d.lyric || '') + (d.transLyric || '');
|
songLyric.textContent = d.lyric ? d.transLyric ? lrctran(d.lyric, d.transLyric) : d.lyric : '[00:00.00] 暂无歌词';
|
||||||
if (!songLyric.textContent.trim()) songLyric.textContent = '[00:00.00] 暂无歌词';
|
songLyric.scrollTop = 0;
|
||||||
songAudio.src = d.songUrl;
|
songAudio.src = d.songUrl;
|
||||||
songAudio.play().catch(function () { /* 自动播放被浏览器阻止 */ });
|
songAudio.play().catch(function () { /* 自动播放被浏览器阻止 */ });
|
||||||
|
|
||||||
@ -669,6 +788,7 @@
|
|||||||
* @param {'1'|'2'|'3'} judgement
|
* @param {'1'|'2'|'3'} judgement
|
||||||
*/
|
*/
|
||||||
function taskSubmit(taskId, judgement) {
|
function taskSubmit(taskId, judgement) {
|
||||||
|
songAudio.pause();
|
||||||
songApprove.disabled = songReject.disabled = songSkip.disabled = true;
|
songApprove.disabled = songReject.disabled = songSkip.disabled = true;
|
||||||
request(
|
request(
|
||||||
'thinktank/audit/resource/update',
|
'thinktank/audit/resource/update',
|
||||||
@ -676,7 +796,10 @@
|
|||||||
function (res) {
|
function (res) {
|
||||||
var d = res.data;
|
var d = res.data;
|
||||||
if (d.result) {
|
if (d.result) {
|
||||||
showResult('审核提交成功,加载下一首...', 'success');
|
showResult((d.test ? '抽查' : '审核') + '提交成功,加载下一首...', 'success');
|
||||||
|
taskStart();
|
||||||
|
} else if (d.test) {
|
||||||
|
showResult('抽查失败: [' + d.dayErrorNum + '/' + d.dayErrorNumStandard + '] ' + d.errorAnalysis, 'error');
|
||||||
taskStart();
|
taskStart();
|
||||||
} else {
|
} else {
|
||||||
showResult('未知状态: ' + JSON.stringify(d), 'error');
|
showResult('未知状态: ' + JSON.stringify(d), 'error');
|
||||||
@ -685,8 +808,63 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**解析歌词时间戳和文本 */
|
||||||
|
function lrctrim(lyrics) {
|
||||||
|
var lines = lyrics.split('\n');
|
||||||
|
var data = [];
|
||||||
|
lines.forEach(function (line, index) {
|
||||||
|
var matches = line.match(/\[(\d{2}):(\d{2}[\.:]?\d*)\]/);
|
||||||
|
if (matches) {
|
||||||
|
var minutes = parseInt(matches[1], 10);
|
||||||
|
var seconds = parseFloat(matches[2].replace('.', ':')) || 0;
|
||||||
|
var timestamp = minutes * 6e4 + seconds * 1e3;
|
||||||
|
var text = line.replace(/\[\d{2}:\d{2}[\.:]?\d*\]/g, '').trim();
|
||||||
|
text = text.replace(/\s\s+/g, ' '); // Replace multiple spaces with a single space
|
||||||
|
data.push([timestamp, index, text]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.sort(function (a, b) {
|
||||||
|
return a[0] - b[0];
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
/**合并原文歌词和翻译歌词 */
|
||||||
|
function lrctran(lyric, tlyric) {
|
||||||
|
lyric = lrctrim(lyric);
|
||||||
|
tlyric = lrctrim(tlyric);
|
||||||
|
var len1 = lyric.length;
|
||||||
|
var len2 = tlyric.length;
|
||||||
|
var result = '';
|
||||||
|
for (var i = 0, j = 0; i < len1 && j < len2; i++) {
|
||||||
|
while (lyric[i][0] > tlyric[j][0] && j + 1 < len2) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (lyric[i][0] === tlyric[j][0]) {
|
||||||
|
tlyric[j][2] = tlyric[j][2].replace('/', '');
|
||||||
|
if (tlyric[j][2]) {
|
||||||
|
lyric[i][2] += ' (' + tlyric[j][2] + ')';
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var k = 0; k < len1; k++) {
|
||||||
|
var t = lyric[k][0];
|
||||||
|
result += '['.concat(
|
||||||
|
String(Math.floor(t / 6e4)).padStart(2, '0'),
|
||||||
|
':',
|
||||||
|
String(Math.floor((t % 6e4) / 1e3)).padStart(2, '0'),
|
||||||
|
'.',
|
||||||
|
String(t % 1e3).padStart(3, '0'),
|
||||||
|
']',
|
||||||
|
lyric[k][2],
|
||||||
|
'\n',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// 一键载入 ///
|
/// 一键载入 ///
|
||||||
document.getElementById('loadall').onclick = function () {
|
window.loadAll = function () {
|
||||||
userGet.click();
|
userGet.click();
|
||||||
vipGet.click();
|
vipGet.click();
|
||||||
taskGetBtn.click();
|
taskGetBtn.click();
|
||||||
|
|||||||
506
public/ugc_lottery.html
Normal file
506
public/ugc_lottery.html
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<title>云小编每日抽奖 - 网易云音乐 API Enhanced</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--gold: #fdcb6e;
|
||||||
|
--text-light: #636e72;
|
||||||
|
--fg: #333;
|
||||||
|
--muted: #666;
|
||||||
|
--border: #ddd;
|
||||||
|
--bg: #f5f5f5;
|
||||||
|
--panel: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', system-ui, -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
/* align-items: center; */
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
color: var(--fg);
|
||||||
|
background: var(--bg);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 520px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题 */
|
||||||
|
.header h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header p {
|
||||||
|
margin-top: 4px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片 */
|
||||||
|
.card {
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮 */
|
||||||
|
button {
|
||||||
|
padding: 8px 18px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--panel);
|
||||||
|
color: var(--fg);
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.primary {
|
||||||
|
background: var(--fg);
|
||||||
|
color: var(--panel);
|
||||||
|
border-color: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.primary:hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页脚 */
|
||||||
|
.footer {
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
color: var(--muted);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
color: var(--fg);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a:hover {
|
||||||
|
border-bottom: 1px dotted var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rolling-display {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #c4b5fd;
|
||||||
|
min-height: 4rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin: 0.5rem 0 1rem;
|
||||||
|
word-break: break-all;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rolling-display.idle {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 结果展示 */
|
||||||
|
.winner-display {
|
||||||
|
background: linear-gradient(135deg, #fff9e6, #fff3cd);
|
||||||
|
border: 2px solid var(--gold);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1rem 0 1.5rem;
|
||||||
|
animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-label {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
color: #b8860b;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-name {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #d63031;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-img {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
margin: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-row {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-emoji {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
animation: bounce 0.6s ease infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-emoji:nth-child(2) {
|
||||||
|
animation-delay: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-emoji:nth-child(3) {
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
from {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态提示 */
|
||||||
|
.status-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-light);
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #00b894;
|
||||||
|
animation: pulse 1.5s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot.error {
|
||||||
|
background: #e17055;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式 */
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.rolling-display {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
min-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-name {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="header">
|
||||||
|
<h1>🎉 云小编每日抽奖</h1>
|
||||||
|
<p>每次抽奖消耗 <b>200 个曲库编辑部积分</b>,每日最多可抽奖 3 次</p>
|
||||||
|
</div>
|
||||||
|
<!-- 抽奖 -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="rolling-display idle" id="rollingDisplay">等待抽奖</div>
|
||||||
|
<div id="resultContainer"></div>
|
||||||
|
<button id="lotteryBtn" class="primary">
|
||||||
|
<span>🎲</span> 开始抽奖
|
||||||
|
</button>
|
||||||
|
<button id="resetBtn">
|
||||||
|
<span>🔄</span> 编辑令牌
|
||||||
|
</button>
|
||||||
|
<div class="status-bar" id="statusBar">
|
||||||
|
<span class="status-dot" id="statusDot"></span>
|
||||||
|
<span id="statusText">就绪,点击按钮开始抽奖</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 页脚 -->
|
||||||
|
<div class="footer">
|
||||||
|
<a href="/">← 返回首页</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
// ============ DOM 元素 ============
|
||||||
|
const rollingDisplay = document.getElementById('rollingDisplay');
|
||||||
|
const resultContainer = document.getElementById('resultContainer');
|
||||||
|
const lotteryBtn = document.getElementById('lotteryBtn');
|
||||||
|
const resetBtn = document.getElementById('resetBtn');
|
||||||
|
const statusDot = document.getElementById('statusDot');
|
||||||
|
const statusText = document.getElementById('statusText');
|
||||||
|
|
||||||
|
// ============ 状态 ============
|
||||||
|
let isDrawing = false;
|
||||||
|
let rollingTimer = null;
|
||||||
|
|
||||||
|
// ============ 状态栏更新 ============
|
||||||
|
function setStatus(type, message) {
|
||||||
|
statusText.textContent = message;
|
||||||
|
statusDot.className = 'status-dot';
|
||||||
|
if (type === 'error') {
|
||||||
|
statusDot.classList.add('error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 重置结果 ============
|
||||||
|
function resetResult() {
|
||||||
|
resultContainer.innerHTML = '';
|
||||||
|
rollingDisplay.textContent = '等待抽奖';
|
||||||
|
rollingDisplay.className = 'rolling-display idle';
|
||||||
|
setStatus('info', '就绪,点击按钮开始抽奖');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 本地令牌管理 ============
|
||||||
|
const cookieKey = 'ncm_cookie';
|
||||||
|
let cookie = localStorage.getItem(cookieKey) || '';
|
||||||
|
function setCookie() {
|
||||||
|
const ck = prompt('请输入令牌:', cookie);
|
||||||
|
if (ck !== null) {
|
||||||
|
cookie = ck;
|
||||||
|
localStorage.setItem(cookieKey, cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 调用后端接口 ============
|
||||||
|
async function request(url, params) {
|
||||||
|
try {
|
||||||
|
url += '?timestamp=' + Date.now() + params;
|
||||||
|
if (cookie) url += '&cookie=' + encodeURIComponent(cookie);
|
||||||
|
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var res = await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('数据解析错误:', error);
|
||||||
|
throw new Error('接口请求错误: ' + response.status + ': ' + response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code !== 200) {
|
||||||
|
throw new Error('接口返回错误: ' + res.code + ': ' + (res.message || '未知错误'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('接口请求失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLotteryResult() {
|
||||||
|
const user = await request('rep/ugc/user/get', '');
|
||||||
|
|
||||||
|
if ((user.data?.availablePoints ?? 0) < 200) {
|
||||||
|
throw new Error('用户剩余积分不足,请先完成任务获取积分');
|
||||||
|
}
|
||||||
|
|
||||||
|
const chance = await request('middle/play/lottery/remain/chance', '&activityId=6501202');
|
||||||
|
|
||||||
|
if (chance.data === 0) {
|
||||||
|
throw new Error('今日机会已用完,明天再来吧');
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await request('middle/play/do/lottery', '&activityId=6501202&drawCount=1');
|
||||||
|
|
||||||
|
// 根据你的后端返回格式提取中奖者名称
|
||||||
|
const prizeMap = res.data?.prizeDetailInfoMap || {};
|
||||||
|
for (var k in prizeMap) break; // 取第一个奖品 ID
|
||||||
|
const prize = k ? prizeMap[k] : null;
|
||||||
|
if (!prize) {
|
||||||
|
throw new Error('无法解析响应数据');
|
||||||
|
}
|
||||||
|
|
||||||
|
// [抽奖结果, 剩余机会, 奖品图片]
|
||||||
|
return [prize.prizeName, res.data.restChance, prize.prizeImgList[0] || ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 滚动动画 ============
|
||||||
|
function startRollingAnimation(finalWinner, img) {
|
||||||
|
const placeholderNames = ['🎰', '🎲', '🎯', '✨', '💫', '🌟', '🎪', '🎭'];
|
||||||
|
const totalDuration = 2000;
|
||||||
|
const startInterval = 50;
|
||||||
|
const endInterval = 280;
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
rollingDisplay.className = 'rolling-display';
|
||||||
|
resultContainer.innerHTML = '';
|
||||||
|
|
||||||
|
function roll() {
|
||||||
|
const elapsed = Date.now() - startTime;
|
||||||
|
const progress = Math.min(elapsed / totalDuration, 1.0);
|
||||||
|
const easeOutProgress = 1 - Math.pow(1 - progress, 3);
|
||||||
|
const currentInterval = startInterval + (endInterval - startInterval) * easeOutProgress;
|
||||||
|
|
||||||
|
// 随机显示占位符
|
||||||
|
const randomPlaceholder = placeholderNames[Math.floor(Math.random() * placeholderNames.length)];
|
||||||
|
rollingDisplay.textContent = randomPlaceholder;
|
||||||
|
|
||||||
|
if (progress >= 1.0) {
|
||||||
|
// 动画结束,显示真实结果
|
||||||
|
clearTimeout(rollingTimer);
|
||||||
|
rollingDisplay.textContent = finalWinner;
|
||||||
|
rollingDisplay.style.display = 'none';
|
||||||
|
showWinnerResult(finalWinner, img);
|
||||||
|
finishDrawing();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rollingTimer = setTimeout(roll, currentInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
roll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWinnerResult(winner, img) {
|
||||||
|
resultContainer.innerHTML = `
|
||||||
|
<div class="winner-display">
|
||||||
|
<div class="winner-label">🏆 恭喜中奖</div>
|
||||||
|
<div class="winner-name">${escapeHTML(winner)}</div>
|
||||||
|
<img src="${img}" alt="奖品图片" class="winner-img" />
|
||||||
|
<div class="confetti-row">
|
||||||
|
<span class="confetti-emoji">🎉</span>
|
||||||
|
<span class="confetti-emoji">🎊</span>
|
||||||
|
<span class="confetti-emoji">✨</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHTML(str) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.appendChild(document.createTextNode(str));
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishDrawing() {
|
||||||
|
isDrawing = false;
|
||||||
|
lotteryBtn.disabled = false;
|
||||||
|
resetBtn.disabled = false;
|
||||||
|
// setStatus('info', '抽奖完成!点击重置可再次抽奖');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 主抽奖流程 ============
|
||||||
|
async function startLottery() {
|
||||||
|
if (isDrawing) return;
|
||||||
|
|
||||||
|
// 开始前状态
|
||||||
|
isDrawing = true;
|
||||||
|
lotteryBtn.disabled = true;
|
||||||
|
resetBtn.disabled = true;
|
||||||
|
resultContainer.innerHTML = '';
|
||||||
|
rollingDisplay.className = 'rolling-display';
|
||||||
|
rollingDisplay.textContent = '🎰';
|
||||||
|
rollingDisplay.style.display = 'flex';
|
||||||
|
setStatus('info', '正在获取抽奖结果...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 调用后端接口获取中奖者
|
||||||
|
const res = await fetchLotteryResult();
|
||||||
|
setStatus('info', '抽奖成功,剩余次数 ' + res[1]);
|
||||||
|
// 启动滚动动画,最终定格在中奖者
|
||||||
|
startRollingAnimation(res[0], res[2]);
|
||||||
|
} catch (error) {
|
||||||
|
// 请求失败处理
|
||||||
|
rollingDisplay.textContent = '抽奖失败';
|
||||||
|
rollingDisplay.className = 'rolling-display idle';
|
||||||
|
setStatus('error', '抽奖失败: ' + error.message);
|
||||||
|
isDrawing = false;
|
||||||
|
lotteryBtn.disabled = false;
|
||||||
|
resetBtn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 事件绑定 ============
|
||||||
|
lotteryBtn.addEventListener('click', startLottery);
|
||||||
|
resetBtn.addEventListener('click', setCookie);
|
||||||
|
|
||||||
|
// 空格键快捷抽奖
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!isDrawing && !lotteryBtn.disabled) {
|
||||||
|
startLottery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ============ 初始化 ============
|
||||||
|
resetResult();
|
||||||
|
|
||||||
|
window.demo = function (name, chance, img) {
|
||||||
|
rollingDisplay.style.display = 'flex';
|
||||||
|
setStatus('info', '抽奖成功,剩余次数 ' + (chance || '0'));
|
||||||
|
startRollingAnimation(name || '演示', img || 'https://p5.music.126.net/obj/worDmcOCw6HCnzzCmsKx/61891591760/8d1c/528a/e977/10cbaa3fb39e8057be5d2560182cc5c7.png');
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user