| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /**
- * 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
- }
|