feat: 周竞赛预采集

This commit is contained in:
guanchao
2026-10-21 11:11:17 +08:00
parent 18068447c5
commit 347cc6f3d0
2 changed files with 145 additions and 1 deletions
+73 -1
View File
@@ -1,3 +1,4 @@
import { createHash } from 'crypto';
import cloud from '@lafjs/cloud'
const db = cloud.database();
import Utils from "@/Utils";
@@ -45,7 +46,8 @@ export default async function (ctx: FunctionContext) {
return;
}
let totalLevel = userLevel + (endLevelNum / 10000)
await db.collection(dbname).where({ _id: uid }).update({ userLevel: userLevel, endLevelNum: endLevelNum, totalLevel: totalLevel });
const saved = await db.collection(dbname).where({ _id: uid }).update({ userLevel: userLevel, endLevelNum: endLevelNum, totalLevel: totalLevel });
await recordWeeklyProgress(uid, endLevelNum - Number(endLevelNum1), saved);
return { code: 1, data: { userLevel: userLevel, endLevelNum: endLevelNum }, msg: "用户数据更新成功" }
case 'read':
let userLevel1 = res.data?.userLevel;
@@ -60,3 +62,73 @@ export default async function (ctx: FunctionContext) {
}
}
}
/** Monday 09:00 Asia/Shanghai. weekId identifies the period, not midnight. */
function progressWeek(at: number) {
const shifted = new Date(at - 3600000);
const startAt = Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth(), shifted.getUTCDate())
- ((shifted.getUTCDay() + 6) % 7) * 86400000 + 3600000;
return { weekId: new Date(startAt).toISOString().slice(0, 10), startAt, endAt: startAt + 7 * 86400000 };
}
/** Only confirmed legacy saves add positive integral deltas. Do not retry a write
* after an ambiguous error: its $inc may already have reached the database. */
async function recordWeeklyProgress(uid: string, delta: number, saved: any) {
if (!saved || saved.ok === false || saved.error || saved.updated !== 1) return;
if (!Number.isSafeInteger(delta) || delta <= 0 || delta > 20) return;
try {
const environment = process.env.PAYMENT_APP_ENV === 'test' ? 'test' : 'production';
const _id = createHash('sha256').update(JSON.stringify(['weeklyCompetition', environment, uid])).digest('hex');
const table = (cloud.mongo.db as any).collection('weekly_competition');
for (let attempt = 0; attempt < 12; attempt++) {
const week = progressWeek(Date.now());
const row = await table.findOne({ _id });
if (!row) {
try {
// Match the existing competition document shape, without joining a round.
await table.insertOne({
_id, uid, environment, revision: 0, current: null, pending: null, receipts: {},
progress: { weekId: week.weekId, weeklyWins: 0, previousWeeklyWins: 0 }
});
} catch (error: any) {
if (error.code !== 11000) throw error;
// Another request initialized the same document. No score was added here.
}
continue;
}
const progress = row.progress;
const beforeCutoff = { $lt: ['$$NOW', new Date(week.endAt)] };
if (progress?.weekId === week.weekId) {
const result = await table.updateOne({ _id, 'progress.weekId': week.weekId, $expr: beforeCutoff },
{ $inc: { 'progress.weeklyWins': delta } });
if (result.matchedCount === 1) return;
if (result.matchedCount === 0) continue; // Confirmed no write: safe to reread the week.
throw new Error('weekly progress increment result is unknown');
}
let previousWeeklyWins = 0;
const condition: any = { _id, $expr: beforeCutoff };
if (progress == null) {
condition.progress = progress === undefined ? { $exists: false } : null;
} else {
const oldStartAt = Date.parse(progress.weekId + 'T01:00:00Z');
if (!Number.isFinite(oldStartAt) || oldStartAt >= week.startAt
|| !Number.isSafeInteger(progress.weeklyWins) || progress.weeklyWins < 0) {
throw new Error('weekly progress period or score is invalid');
}
if (week.startAt - oldStartAt === 7 * 86400000) previousWeeklyWins = progress.weeklyWins;
// Include the old score so a concurrent old-week $inc cannot be overwritten.
condition['progress.weekId'] = progress.weekId;
condition['progress.weeklyWins'] = progress.weeklyWins;
}
const rotated = await table.updateOne(condition,
{ $set: { progress: { weekId: week.weekId, weeklyWins: 0, previousWeeklyWins } } });
if (rotated.matchedCount !== 0 && rotated.matchedCount !== 1) {
throw new Error('weekly progress rollover result is unknown');
}
// Reread after initialization, rotation or a known conditional-update conflict.
}
throw new Error('weekly progress conditional updates exhausted');
} catch (error) {
console.error('weekly progress recording failed; legacy save unchanged', { uid, delta, error });
}
}
+72
View File
@@ -1,3 +1,4 @@
import { createHash } from 'crypto';
import * as GoldMinerServiceModule from "@/goldMiner/service";
import * as GoldMinerRulesModule from "@/goldMiner/config";
const GoldMinerService: any = GoldMinerServiceModule;
@@ -94,6 +95,7 @@ export default async function (ctx: FunctionContext) {
}
film += count;
let ret = await db.collection(dbname).where({ _id: uid }).update({ address: address, film: film, careerAmount: careerAmount, levelTJ: levelTJ, levelAmount: levelAmount, addLevel: add, timestamp: Date.now() });
await recordWeeklyProgress(uid, count, ret);
if (ctx.body.goldMiner != null) {
try {
GoldMinerService.authorize(res1.data, ctx.body);
@@ -311,3 +313,73 @@ function getProvinceByIP(ip) {
return '其他';
}
}
/** Monday 09:00 Asia/Shanghai. weekId identifies the period, not midnight. */
function progressWeek(at: number) {
const shifted = new Date(at - 3600000);
const startAt = Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth(), shifted.getUTCDate())
- ((shifted.getUTCDay() + 6) % 7) * 86400000 + 3600000;
return { weekId: new Date(startAt).toISOString().slice(0, 10), startAt, endAt: startAt + 7 * 86400000 };
}
/** Only confirmed legacy saves add positive integral deltas. Do not retry a write
* after an ambiguous error: its $inc may already have reached the database. */
async function recordWeeklyProgress(uid: string, delta: number, saved: any) {
if (!saved || saved.ok === false || saved.error || saved.updated !== 1) return;
if (!Number.isSafeInteger(delta) || delta <= 0 || delta > 20) return;
try {
const environment = process.env.PAYMENT_APP_ENV === 'test' ? 'test' : 'production';
const _id = createHash('sha256').update(JSON.stringify(['weeklyCompetition', environment, uid])).digest('hex');
const table = (cloud.mongo.db as any).collection('weekly_competition');
for (let attempt = 0; attempt < 12; attempt++) {
const week = progressWeek(Date.now());
const row = await table.findOne({ _id });
if (!row) {
try {
// Match the existing competition document shape, without joining a round.
await table.insertOne({
_id, uid, environment, revision: 0, current: null, pending: null, receipts: {},
progress: { weekId: week.weekId, weeklyWins: 0, previousWeeklyWins: 0 }
});
} catch (error: any) {
if (error.code !== 11000) throw error;
// Another request initialized the same document. No score was added here.
}
continue;
}
const progress = row.progress;
const beforeCutoff = { $lt: ['$$NOW', new Date(week.endAt)] };
if (progress?.weekId === week.weekId) {
const result = await table.updateOne({ _id, 'progress.weekId': week.weekId, $expr: beforeCutoff },
{ $inc: { 'progress.weeklyWins': delta } });
if (result.matchedCount === 1) return;
if (result.matchedCount === 0) continue; // Confirmed no write: safe to reread the week.
throw new Error('weekly progress increment result is unknown');
}
let previousWeeklyWins = 0;
const condition: any = { _id, $expr: beforeCutoff };
if (progress == null) {
condition.progress = progress === undefined ? { $exists: false } : null;
} else {
const oldStartAt = Date.parse(progress.weekId + 'T01:00:00Z');
if (!Number.isFinite(oldStartAt) || oldStartAt >= week.startAt
|| !Number.isSafeInteger(progress.weeklyWins) || progress.weeklyWins < 0) {
throw new Error('weekly progress period or score is invalid');
}
if (week.startAt - oldStartAt === 7 * 86400000) previousWeeklyWins = progress.weeklyWins;
// Include the old score so a concurrent old-week $inc cannot be overwritten.
condition['progress.weekId'] = progress.weekId;
condition['progress.weeklyWins'] = progress.weeklyWins;
}
const rotated = await table.updateOne(condition,
{ $set: { progress: { weekId: week.weekId, weeklyWins: 0, previousWeeklyWins } } });
if (rotated.matchedCount !== 0 && rotated.matchedCount !== 1) {
throw new Error('weekly progress rollover result is unknown');
}
// Reread after initialization, rotation or a known conditional-update conflict.
}
throw new Error('weekly progress conditional updates exhausted');
} catch (error) {
console.error('weekly progress recording failed; legacy save unchanged', { uid, delta, error });
}
}