| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- const { getJkesSettings } = require('./jkesSettings')
- const { MANUAL_PACE_MIN_SEC, MANUAL_PACE_MAX_SEC } = require('./paceUtils')
- const DEFAULT_AUTO_RUN_DISTANCE_KM = 2
- function roundToStepKm(value, stepKm) {
- const s = Number(stepKm)
- const v = Number(value)
- if (!Number.isFinite(v)) return DEFAULT_AUTO_RUN_DISTANCE_KM
- if (!Number.isFinite(s) || s <= 0) return Math.round(v * 100) / 100
- return Math.round(Math.ceil(v / s) * s * 100) / 100
- }
- /**
- * 在 [minKm, maxKm] 内按步进离散均匀随机取一值(用于每次自动乐跑)
- */
- function randomAutoRunDistanceKmBetween(minKm, maxKm, cfg) {
- const step = Number(cfg.autoDistanceStepKm) || 0.1
- const maxCap = Math.max(2, Number(cfg.autoSingleRunMaxKm) || 10)
- let lo = Number(minKm)
- let hi = Number(maxKm)
- if (!Number.isFinite(lo) || !Number.isFinite(hi)) {
- lo = DEFAULT_AUTO_RUN_DISTANCE_KM
- hi = DEFAULT_AUTO_RUN_DISTANCE_KM
- }
- if (lo > hi) {
- const t = lo
- lo = hi
- hi = t
- }
- lo = roundToStepKm(lo, step)
- hi = roundToStepKm(hi, step)
- if (lo > hi) {
- const t = lo
- lo = hi
- hi = t
- }
- lo = Math.max(1, Math.min(lo, maxCap))
- hi = Math.max(1, Math.min(hi, maxCap))
- if (lo > hi) {
- const t = lo
- lo = hi
- hi = t
- }
- const span = hi - lo
- const stepCount = Math.max(0, Math.round(span / step + 1e-9))
- const pick = Math.floor(Math.random() * (stepCount + 1))
- const v = lo + pick * step
- return Math.min(hi, Math.round(v * 100) / 100)
- }
- function normalizePaceRangeSecPerKm(rawMin, rawMax, cfg) {
- const floor = MANUAL_PACE_MIN_SEC
- const ceil = MANUAL_PACE_MAX_SEC
- let lo =
- rawMin === undefined || rawMin === null || rawMin === ''
- ? Number(cfg.paceRandomMinSecPerKm)
- : Number(rawMin)
- let hi =
- rawMax === undefined || rawMax === null || rawMax === ''
- ? Number(cfg.paceRandomMaxSecPerKm)
- : Number(rawMax)
- if (!Number.isFinite(lo)) lo = floor
- if (!Number.isFinite(hi)) hi = ceil
- lo = Math.round(lo)
- hi = Math.round(hi)
- if (lo > hi) {
- const t = lo
- lo = hi
- hi = t
- }
- lo = Math.max(floor, Math.min(lo, ceil))
- hi = Math.max(floor, Math.min(hi, ceil))
- if (lo > hi) {
- const t = lo
- lo = hi
- hi = t
- }
- return { paceMinSecPerKm: lo, paceMaxSecPerKm: hi }
- }
- /**
- * 添加/编辑账号:校验自动距离区间与配速区间(body 应已合并库内旧值;兼容仅传 auto_run_distance_km 视为 min=max)
- * @returns {{ ok: true, autoRunDistanceMinKm: number, autoRunDistanceMaxKm: number, paceMinSecPerKm: number, paceMaxSecPerKm: number } | { ok: false, msg: string }}
- */
- function validateAutoRunPresetForSave(body) {
- const cfg = getJkesSettings()
- const maxKm = Math.max(2, Number(cfg.autoSingleRunMaxKm) || 10)
- const stepKm = Number(cfg.autoDistanceStepKm) || 0.1
- const legacy = body.auto_run_distance_km
- const hasMin =
- body.auto_run_distance_min_km !== undefined &&
- body.auto_run_distance_min_km !== null &&
- body.auto_run_distance_min_km !== ''
- const hasMax =
- body.auto_run_distance_max_km !== undefined &&
- body.auto_run_distance_max_km !== null &&
- body.auto_run_distance_max_km !== ''
- let rawMin
- let rawMax
- if (!hasMin && !hasMax && legacy !== undefined && legacy !== null && legacy !== '') {
- rawMin = Number(legacy)
- rawMax = Number(legacy)
- } else {
- rawMin = !hasMin ? DEFAULT_AUTO_RUN_DISTANCE_KM : Number(body.auto_run_distance_min_km)
- rawMax = !hasMax ? DEFAULT_AUTO_RUN_DISTANCE_KM : Number(body.auto_run_distance_max_km)
- if (hasMin && !hasMax) rawMax = rawMin
- if (!hasMin && hasMax) rawMin = rawMax
- }
- if (!Number.isFinite(rawMin) || !Number.isFinite(rawMax)) {
- return { ok: false, msg: '自动乐跑距离区间无效' }
- }
- if (rawMin < 1 || rawMin > maxKm || rawMax < 1 || rawMax > maxKm) {
- return {
- ok: false,
- msg: `自动乐跑距离区间需在 1–${maxKm} 公里`
- }
- }
- if (rawMin > rawMax) {
- return { ok: false, msg: '自动乐跑距离下限不能大于上限' }
- }
- let autoRunDistanceMinKm = Math.min(maxKm, Math.max(1, roundToStepKm(rawMin, stepKm)))
- let autoRunDistanceMaxKm = Math.min(maxKm, Math.max(1, roundToStepKm(rawMax, stepKm)))
- if (autoRunDistanceMinKm > autoRunDistanceMaxKm) {
- const t = autoRunDistanceMinKm
- autoRunDistanceMinKm = autoRunDistanceMaxKm
- autoRunDistanceMaxKm = t
- }
- const rawLo = body.pace_min_sec_per_km
- const rawHi = body.pace_max_sec_per_km
- const lo =
- rawLo === undefined || rawLo === null || rawLo === ''
- ? Number(cfg.paceRandomMinSecPerKm)
- : Number(rawLo)
- const hi =
- rawHi === undefined || rawHi === null || rawHi === ''
- ? Number(cfg.paceRandomMaxSecPerKm)
- : Number(rawHi)
- if (!Number.isFinite(lo) || !Number.isFinite(hi)) {
- return { ok: false, msg: '配速区间无效(请填写每公里秒数,如 300、540)' }
- }
- if (
- lo < MANUAL_PACE_MIN_SEC ||
- lo > MANUAL_PACE_MAX_SEC ||
- hi < MANUAL_PACE_MIN_SEC ||
- hi > MANUAL_PACE_MAX_SEC
- ) {
- return { ok: false, msg: '配速区间需在 3:00–10:00/km(180–600 秒/公里)' }
- }
- if (lo > hi) {
- return { ok: false, msg: '配速区间下限不能大于上限' }
- }
- return {
- ok: true,
- autoRunDistanceMinKm,
- autoRunDistanceMaxKm,
- paceMinSecPerKm: Math.round(lo),
- paceMaxSecPerKm: Math.round(hi)
- }
- }
- function buildAutoRunTaskExtrasFromAccountRow(row) {
- const cfg = getJkesSettings()
- const targetKm = randomAutoRunDistanceKmBetween(
- row?.auto_run_distance_min_km,
- row?.auto_run_distance_max_km,
- cfg
- )
- const { paceMinSecPerKm, paceMaxSecPerKm } = normalizePaceRangeSecPerKm(
- row?.pace_min_sec_per_km,
- row?.pace_max_sec_per_km,
- cfg
- )
- return {
- targetKm,
- autoDoubleSlot: targetKm >= 2,
- paceRandomMinSecPerKm: paceMinSecPerKm,
- paceRandomMaxSecPerKm: paceMaxSecPerKm
- }
- }
- module.exports = {
- DEFAULT_AUTO_RUN_DISTANCE_KM,
- validateAutoRunPresetForSave,
- buildAutoRunTaskExtrasFromAccountRow,
- randomAutoRunDistanceKmBetween,
- normalizePaceRangeSecPerKm
- }
|