feat: 完成分龄页开发

This commit is contained in:
R524809
2026-05-06 17:28:51 +08:00
parent 50394154f7
commit 9bdf850f20
54 changed files with 3499 additions and 1021 deletions
@@ -0,0 +1,178 @@
const cloud = require('wx-server-sdk');
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
const BATCH_SIZE = 10;
function toInt(value, fallback = 0) {
const n = Number(value);
if (!Number.isFinite(n)) return fallback;
return Math.round(n);
}
/** 与 miniprogram/utils/debugPublish.inferGradeFromAge 保持一致 */
function inferGradeFromAge(ageMin, ageMax) {
const centerAge = Math.round((ageMin + ageMax) / 2);
const ageGradeMap = {
2: -4,
3: -3,
4: -2,
5: -1,
6: 0,
7: 1,
8: 2,
9: 3,
10: 4,
11: 5,
12: 6,
};
return ageGradeMap[centerAge] ?? 0;
}
function normalizeTags(value) {
if (!Array.isArray(value)) return null;
return value.map((t) => String(t || '').trim()).filter(Boolean);
}
function buildUpdateData(db, patch) {
const id = String(patch.id || '').trim();
if (!id) return { id: null, ok: false, error: '缺少 id' };
const data = { updatedAt: db.serverDate() };
let hasField = false;
const hasAgeMin = patch.ageMin !== undefined && patch.ageMin !== null;
const hasAgeMax = patch.ageMax !== undefined && patch.ageMax !== null;
if (hasAgeMin !== hasAgeMax) {
return {
id,
ok: false,
error: 'ageMin 与 ageMax 必须同时提供',
};
}
if (hasAgeMin && hasAgeMax) {
const ageMin = toInt(patch.ageMin);
const ageMax = toInt(patch.ageMax);
if (ageMin < 0 || ageMax < ageMin) {
return { id, ok: false, error: '年龄范围不合法' };
}
data.ageMin = ageMin;
data.ageMax = ageMax;
data.grade =
patch.grade !== undefined && patch.grade !== null
? toInt(patch.grade)
: inferGradeFromAge(ageMin, ageMax);
hasField = true;
}
if (patch.tags !== undefined) {
const tags = normalizeTags(patch.tags);
if (tags === null) {
return { id, ok: false, error: 'tags 须为数组' };
}
data.tags = tags;
hasField = true;
}
if (!hasField) {
return {
id,
ok: false,
error: '未包含可更新字段(ageMin/ageMax 或 tags',
};
}
return { id, ok: true, data };
}
async function runInBatches(items, batchSize, worker) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const chunk = items.slice(i, i + batchSize);
results.push(...(await Promise.all(chunk.map(worker))));
}
return results;
}
exports.main = async (event) => {
try {
const db = cloud.database();
const patches = Array.isArray(event.patches) ? event.patches : [];
const dryRun = !!event.dryRun;
const invalidResults = [];
const validUpdates = [];
for (const p of patches) {
const update = buildUpdateData(db, p);
if (dryRun) {
invalidResults.push(
update.ok
? {
id: update.id,
ok: true,
dryRun: true,
wouldSet: {
...update.data,
updatedAt: '[serverDate]',
},
}
: update,
);
continue;
}
if (!update.ok) {
invalidResults.push(update);
continue;
}
validUpdates.push(update);
}
const updateResults = dryRun
? []
: await runInBatches(
validUpdates,
BATCH_SIZE,
async ({ id, data }) => {
try {
await db
.collection('worksheets')
.doc(id)
.update({ data });
return { id, ok: true };
} catch (error) {
const message =
error instanceof Error
? error.message
: '更新失败';
return {
id,
ok: false,
error: /document.*not.*exist|not.*found/i.test(
message,
)
? 'worksheet 不存在'
: message,
};
}
},
);
const results = [...invalidResults, ...updateResults];
const failed = results.filter((r) => !r.ok).length;
return {
success: failed === 0,
dryRun,
total: results.length,
failed,
results,
};
} catch (error) {
return {
success: false,
message: error instanceof Error ? error.message : '批量更新失败',
};
}
};