nutritionDefaultsMerge.ts 3.16 KB
/**
 * 将管理端保存的营养成分默认值 JSON 合并进 NUTRITION 元素 config(与 Web nutritionManualEntry 字段一致)。
 * `<` 前缀由模板 config 决定,录入 JSON 不包含 LessThan 字段。
 */
import { NUTRITION_FACTS_LAYOUT_ROWS } from '../nutritionFactsLayout'

function fixedLabelForKey(key: string): string {
  const hit = NUTRITION_FACTS_LAYOUT_ROWS.find((x) => x.key === key)
  return hit?.label ?? key
}

function pickManual(manual: Record<string, string>, subKey: string): string {
  return String(manual[subKey] ?? '').trim()
}

export function applyNutritionDefaultJsonToConfig(
  baseCfg: Record<string, unknown>,
  jsonStr: string,
): Record<string, unknown> {
  const t = String(jsonStr ?? '').trim()
  if (!t.startsWith('{')) return baseCfg
  let manual: Record<string, string> = {}
  try {
    manual = JSON.parse(t) as Record<string, string>
  } catch {
    return baseCfg
  }

  const out: Record<string, unknown> = { ...baseCfg }
  const baseFixed = Array.isArray(baseCfg.fixedNutrients)
    ? (baseCfg.fixedNutrients as Record<string, unknown>[])
    : []

  const cal = pickManual(manual, 'calories')
  if (cal) out.calories = cal
  else {
    delete out.calories
    delete out.Calories
  }
  out.servingsPerContainer = pickManual(manual, 'servingsPerContainer')
  out.servingSize = pickManual(manual, 'servingSize')

  const keysInOrder = [...NUTRITION_FACTS_LAYOUT_ROWS.map((r) => r.key)]
  for (const row of baseFixed) {
    const key = String(row.key ?? '').trim()
    if (key && !keysInOrder.includes(key)) keysInOrder.push(key)
  }

  const fixedArr: Record<string, unknown>[] = []
  for (const key of keysInOrder) {
    const baseRow = baseFixed.find((r) => String(r.key ?? '').trim() === key)
    const layout = NUTRITION_FACTS_LAYOUT_ROWS.find((r) => r.key === key)
    const v = pickManual(manual, key)
    const pct = pickManual(manual, `${key}Percent`)
    const lessThan = Boolean(baseRow?.lessThan ?? baseCfg[`${key}LessThan`])
    const label = String(baseRow?.label ?? layout?.label ?? fixedLabelForKey(key))
    fixedArr.push({
      key,
      label,
      value: v,
      unit: '',
      dailyValuePercent: pct,
      lessThan,
    })
    if (v) out[key] = v
    else delete out[key]
    delete out[`${key}Unit`]
    out[`${key}Percent`] = pct
    out[`${key}LessThan`] = lessThan
  }
  out.fixedNutrients = fixedArr

  const newExtras: Array<{ id: string; name: string; value: string; unit: string }> = []
  for (const k of Object.keys(manual)) {
    if (!k.startsWith('extra:') || !k.endsWith(':value')) continue
    const id = k.slice('extra:'.length, -':value'.length)
    newExtras.push({
      id,
      name: String(
        (Array.isArray(out.extraNutrients)
          ? (out.extraNutrients as Record<string, unknown>[]).find((row) => String(row.id ?? '') === id)
          : undefined)?.name ?? 'Other',
      ),
      value: pickManual(manual, k),
      unit: '',
    })
    out[`extra:${id}:percent`] = pickManual(manual, `extra:${id}:percent`)
    out[`extra:${id}:lessThan`] = Boolean(baseCfg[`extra:${id}:lessThan`])
  }
  if (newExtras.length > 0) out.extraNutrients = newExtras

  delete out.ingredientsText
  delete out.IngredientsText
  return out
}