/** * JKES 跑步记录汇总(mylist 分页) */ const { jkesRequest } = require('./request.js') function normStatusValue(v) { if (v == null) return '' if (typeof v === 'object' && v.value != null) return String(v.value).toUpperCase() return String(v).toUpperCase() } function isEndRecord(r) { const s = normStatusValue(r.status) return s === 'END' } function isIncampusRecord(r) { const s = normStatusValue(r.dataStatus) return s === 'INCAMPUS' } function parseOneTime(v) { if (v == null) return NaN if (typeof v === 'number' && v > 1e12) return v if (typeof v === 'number' && v > 1e9 && v < 1e12) return v * 1000 const str = String(v).trim() if (/^\d+$/.test(str)) { const n = parseInt(str, 10) if (n > 1e12) return n if (n > 1e9) return n * 1000 } const t = Date.parse(str.replace(/-/g, '/')) return Number.isFinite(t) ? t : NaN } /** 归属月份:优先结束时间,否则开始时间 */ function parseRecordMonthTime(r) { const tEnd = parseOneTime(r.endTime ?? r.end_time ?? r.finishTime) if (Number.isFinite(tEnd)) return tEnd const tBegin = parseOneTime(r.beginTime ?? r.startTime ?? r.createTime ?? r.deviceTime) return Number.isFinite(tBegin) ? tBegin : NaN } /** * 单条记录距离 → 千米(真机 mylist / end 返回的 distance 为米,如 6324.01≈6.324km)。 * END+校内且 distance 为 0:官方尚未写入里程,返回 0(不计入汇总,待列表更新后再算)。 */ function recordDistanceKm(r) { const raw = r.distance ?? r.info?.distance ?? r.runDistance ?? r.mileage ?? r.totalDistance ?? r.length let d = typeof raw === 'string' ? parseFloat(raw.trim()) : Number(raw) if (!Number.isFinite(d) || d <= 0) return 0 const km = d / 1000 if (!Number.isFinite(km) || km <= 0) return 0 return Math.round(km * 1000) / 1000 } function extractPage(apiData) { if (!apiData || apiData.code !== 0 || !apiData.data) return null const d = apiData.data if (d.page && (Array.isArray(d.page.records) || d.page.records === undefined)) { return { records: Array.isArray(d.page.records) ? d.page.records : [], pages: Number(d.page.pages) || 1, current: Number(d.page.current) || 1 } } if (Array.isArray(d.records)) { return { records: d.records, pages: Number(d.pages) || 1, current: Number(d.current) || 1 } } if (Array.isArray(d.list)) { return { records: d.list, pages: Number(d.pages) || 1, current: Number(d.page) || 1 } } return null } function parseRecordSpeed(r) { const raw = r.speed ?? r.info?.speed const n = Number(raw) return Number.isFinite(n) ? n : 0 } /** * 统计本月「结束且校内」的跑步距离之和(千米) */ async function fetchJkesMonthKm(token, year, month1to12) { const monthStart = new Date(year, month1to12 - 1, 1).getTime() const monthEnd = new Date(year, month1to12, 0, 23, 59, 59, 999).getTime() let page = 1 let totalKm = 0 const pageSize = 50 let pages = 1 while (page <= pages) { const url = `/health/runRecord/mylist?pageSize=${pageSize}&page=${page}` const data = await jkesRequest(url, {}, token) const pg = extractPage(data) if (!pg) break pages = Number(pg.pages) || 1 const records = pg.records || [] for (const r of records) { if (!isEndRecord(r)) continue if (!isIncampusRecord(r)) continue const t = parseRecordMonthTime(r) if (!Number.isFinite(t) || t < monthStart || t > monthEnd) continue totalKm += recordDistanceKm(r) } page += 1 if (records.length === 0) break } console.log(`[JKES] 本月跑步距离: ${totalKm} km`) return Math.round(totalKm * 1000) / 1000 } /** * 全量有效记录里程(千米),用于 total_num 近似同步 */ async function fetchJkesTotalKm(token) { let page = 1 let totalKm = 0 const pageSize = 50 let pages = 1 while (page <= pages) { const url = `/health/runRecord/mylist?pageSize=${pageSize}&page=${page}` const data = await jkesRequest(url, {}, token) const pg = extractPage(data) if (!pg) break pages = Number(pg.pages) || 1 const records = pg.records || [] for (const r of records) { if (!isEndRecord(r)) continue if (!isIncampusRecord(r)) continue totalKm += recordDistanceKm(r) } page += 1 if (records.length === 0) break } return Math.round(totalKm * 1000) / 1000 } /** end 接口返回的 info 与列表记录字段兼容 */ function distanceKmFromEndInfo(info) { return recordDistanceKm(info || {}) } /** * 按记录 ID 在 mylist 分页中查找。 * 刚结束时可能出现 END+INCAMPUS 但 distance/speed 尚未更新。 */ async function fetchJkesRecordById(token, recordId, maxPages = 8) { const id = String(recordId || '').trim() if (!id) return null let page = 1 const pageSize = 50 let pages = 1 while (page <= pages && page <= maxPages) { const url = `/health/runRecord/mylist?pageSize=${pageSize}&page=${page}` const data = await jkesRequest(url, {}, token) const pg = extractPage(data) if (!pg) return null pages = Number(pg.pages) || 1 const hit = (pg.records || []).find((x) => String(x?.id) === id) if (hit) return hit page += 1 } return null } function isJkesRecordValidInCampus(r) { return !!r && isEndRecord(r) && isIncampusRecord(r) } /** 完整同步:校内有效 + 距离>0 + 配速/速度>0 */ function isJkesRecordFullySynced(r) { if (!isJkesRecordValidInCampus(r)) return false return recordDistanceKm(r) > 0 && parseRecordSpeed(r) > 0 } module.exports = { fetchJkesMonthKm, fetchJkesTotalKm, recordDistanceKm, distanceKmFromEndInfo, extractPage, fetchJkesRecordById, isJkesRecordValidInCampus, isJkesRecordFullySynced }