feat: 修复清除数据、语音缓存、排行榜限制,新增项目配置文件
- 修复 cloud.clearAll 静默失败导致重启后数据恢复 - 清除数据弹窗改用自定义 ui-modal 替代 wx.showModal - TTS 语音合成增加 fileID 持久化缓存,重启不再重新合成 - 排行榜限制前15名,maxRank 参数化到 config.js - 新增 config.js 统一管理版本号/更新日期/开发者/排行榜限制 - progress-ring 消除 getSystemInfoSync 弃用警告 - voice.js 增加 InnerAudioContext 错误监听 - storage/util 完善用户资料、打卡日期精度、记录日志
This commit is contained in:
@@ -5,18 +5,28 @@ const _ = db.command
|
||||
|
||||
const COLLECTION = 'plank_data'
|
||||
const PAGE_SIZE = 100
|
||||
const MAX_RANK = 100
|
||||
const DEFAULT_MAX_RANK = 100 // fallback if client doesn't pass maxRank
|
||||
|
||||
const pad = (n) => String(n).padStart(2, '0')
|
||||
|
||||
// Fixed UTC+8 offset — the user base is China-only and `new Date()` in
|
||||
// the cloud function runs in UTC, so without this shift "today" rolls
|
||||
// over at 08:00 Beijing instead of 00:00. If you ever support users in
|
||||
// other timezones, switch back to a client-provided date.
|
||||
const TZ_OFFSET_MS = 8 * 60 * 60 * 1000
|
||||
|
||||
exports.main = async (event) => {
|
||||
const { period } = event
|
||||
const { period, maxRank } = event || {}
|
||||
const limit = Math.max(1, Math.min(parseInt(maxRank) || DEFAULT_MAX_RANK, 500))
|
||||
if (!period) return { err: 'missing period' }
|
||||
|
||||
const now = new Date()
|
||||
const today = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
|
||||
const local = new Date(now.getTime() + TZ_OFFSET_MS)
|
||||
const today = `${local.getUTCFullYear()}-${pad(local.getUTCMonth() + 1)}-${pad(local.getUTCDate())}`
|
||||
const thisMonth = today.substring(0, 7)
|
||||
const thisYear = String(now.getFullYear())
|
||||
// Year is computed from the same shifted date so year boundaries also
|
||||
// align with Beijing's midnight, not UTC's.
|
||||
const thisYear = String(local.getUTCFullYear())
|
||||
const myOpenid = cloud.getWXContext().OPENID
|
||||
|
||||
let prefix, exact
|
||||
@@ -41,7 +51,11 @@ exports.main = async (event) => {
|
||||
if (prefix && !monthKey.startsWith(prefix)) continue
|
||||
for (const r of records[monthKey]) {
|
||||
if (period === 'day') {
|
||||
if (r.date === exact) { duration += r.duration; sessions++ }
|
||||
// Use startsWith rather than === to tolerate date strings with
|
||||
// trailing time/zone info (e.g. "2026-06-11T09:11:24.587Z" if
|
||||
// some legacy path stored an ISO date) and to be timezone-
|
||||
// agnostic when client local date differs from server UTC date.
|
||||
if (r.date.startsWith(exact)) { duration += r.duration; sessions++ }
|
||||
} else {
|
||||
if (r.date.startsWith(prefix)) { duration += r.duration; sessions++ }
|
||||
}
|
||||
@@ -51,11 +65,19 @@ exports.main = async (event) => {
|
||||
if (duration > 0) {
|
||||
const openid = doc._openid || 'unknown'
|
||||
const entry = userMap.get(openid)
|
||||
const profile = doc.profile || {}
|
||||
if (entry) {
|
||||
entry.duration += duration
|
||||
entry.sessions += sessions
|
||||
// Refresh nickname in case the user updated it since last write
|
||||
if (profile.nickname) entry.nickname = profile.nickname
|
||||
} else {
|
||||
userMap.set(openid, { openid, duration, sessions })
|
||||
userMap.set(openid, {
|
||||
openid,
|
||||
duration,
|
||||
sessions,
|
||||
nickname: profile.nickname || ''
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,11 +94,18 @@ exports.main = async (event) => {
|
||||
const myEntry = userMap.get(myOpenid)
|
||||
const myRank = myEntry ? allSorted.findIndex(e => e.openid === myOpenid) + 1 : 0
|
||||
|
||||
// Resolve display name: prefer the user's chosen nickname, fall back to
|
||||
// a masked openid for users who haven't set one.
|
||||
const _displayName = (entry) =>
|
||||
(entry && entry.nickname && entry.nickname.trim()) ||
|
||||
maskOpenid(entry && entry.openid)
|
||||
|
||||
// Top N for the board
|
||||
const ranked = allSorted.slice(0, MAX_RANK).map((item, i) => ({
|
||||
const ranked = allSorted.slice(0, limit).map((item, i) => ({
|
||||
rank: i + 1,
|
||||
openid: item.openid,
|
||||
name: maskOpenid(item.openid),
|
||||
nickname: item.nickname || '',
|
||||
name: _displayName(item),
|
||||
duration: item.duration,
|
||||
sessions: item.sessions
|
||||
}))
|
||||
@@ -88,7 +117,8 @@ exports.main = async (event) => {
|
||||
myEntry: myEntry ? {
|
||||
rank: myRank,
|
||||
openid: myEntry.openid,
|
||||
name: maskOpenid(myEntry.openid),
|
||||
nickname: myEntry.nickname || '',
|
||||
name: _displayName(myEntry),
|
||||
duration: myEntry.duration,
|
||||
sessions: myEntry.sessions
|
||||
} : null,
|
||||
@@ -100,4 +130,4 @@ function maskOpenid(openid) {
|
||||
if (!openid || openid === 'unknown') return '未知用户'
|
||||
if (openid.length <= 4) return '****' + openid
|
||||
return '****' + openid.slice(-4)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user