scanCheckin.js 4.09 KB
/**
 * @description 解析小程序码进入扫码关卡详情页时的 scene 参数
 * @param {string} rawScene - scene 原始值,兼容 `detailId,activityId` 和 querystring 两种格式
 * @returns {{activityId:string, detailId:string, rawScene:string}}
 */
export const parseScanCheckinSceneParams = (rawScene = '') => {
  const normalizedScene = safeDecodeURIComponent(String(rawScene || '').trim())

  if (!normalizedScene) {
    return {
      activityId: '',
      detailId: '',
      rawScene: '',
    }
  }

  if (!normalizedScene.includes('=') && normalizedScene.includes(',')) {
    const [detailId = '', activityId = ''] = normalizedScene.split(',').map(item => item.trim())

    return {
      activityId,
      detailId,
      rawScene: normalizedScene,
    }
  }

  const rawParams = parseQueryString(normalizedScene)

  return {
    activityId: pickFirstAvailableValue(rawParams, ['activity_id', 'activityId', 'id']),
    detailId: pickFirstAvailableValue(rawParams, ['detail_id', 'detailId', 'stage_id', 'stageId']),
    rawScene: normalizedScene,
  }
}

/**
 * @description 从扫码结果中提取打卡接口所需参数
 * @param {string} rawScanResult - 微信扫码返回的原始结果,可能是 path、完整URL,也可能是纯查询串
 * @returns {{activityId:string, detailId:string, rawParams:Object, rawScene:string}}
 */
export const parseScanCheckinParams = (rawScanResult = '') => {
  const normalized = String(rawScanResult || '').trim()

  if (!normalized) {
    return {
      activityId: '',
      detailId: '',
      rawParams: {},
      rawScene: '',
    }
  }

  const querySource = extractQuerySource(normalized)
  const rawParams = parseQueryString(querySource)
  // 兼容 `pages/xxx?scene=detailId,activityId` 这种太阳码 path 场景。
  const sceneParams = parseScanCheckinSceneParams(rawParams.scene || '')

  return {
    activityId:
      sceneParams.activityId ||
      pickFirstAvailableValue(rawParams, ['activity_id', 'activityId', 'id']),
    detailId:
      sceneParams.detailId ||
      pickFirstAvailableValue(rawParams, ['detail_id', 'detailId', 'stage_id', 'stageId']),
    rawParams,
    rawScene: sceneParams.rawScene,
  }
}

/**
 * @description 判断当前详情页和扫码结果是否指向同一个打卡点
 * @param {{activityId:string|number, detailId:string|number}} currentTarget
 * @param {{activityId:string|number, detailId:string|number}} scannedTarget
 * @returns {boolean}
 */
export const isSameScanCheckinTarget = (currentTarget = {}, scannedTarget = {}) => {
  const currentActivityId = normalizeTargetValue(currentTarget.activityId)
  const currentDetailId = normalizeTargetValue(currentTarget.detailId)
  const scannedActivityId = normalizeTargetValue(scannedTarget.activityId)
  const scannedDetailId = normalizeTargetValue(scannedTarget.detailId)

  if (!currentActivityId || !currentDetailId || !scannedActivityId || !scannedDetailId) {
    return false
  }

  return currentActivityId === scannedActivityId && currentDetailId === scannedDetailId
}

const extractQuerySource = input => {
  const questionMarkIndex = input.indexOf('?')

  if (questionMarkIndex >= 0) {
    return input.slice(questionMarkIndex + 1)
  }

  return input
}

const parseQueryString = (queryString = '') => {
  const hashRemoved = String(queryString || '').split('#')[0]
  const pairs = hashRemoved.split('&').filter(Boolean)

  return pairs.reduce((result, pair) => {
    const [rawKey, ...rest] = pair.split('=')
    const rawValue = rest.join('=')
    const key = safeDecodeURIComponent(rawKey)
    const value = safeDecodeURIComponent(rawValue)

    if (key) {
      result[key] = value
    }

    return result
  }, {})
}

const pickFirstAvailableValue = (params, keys = []) => {
  for (const key of keys) {
    const value = params?.[key]
    if (value !== '' && value !== undefined && value !== null) {
      return String(value)
    }
  }

  return ''
}

const normalizeTargetValue = value => String(value || '').trim()

const safeDecodeURIComponent = (value = '') => {
  try {
    return decodeURIComponent(String(value || '').replace(/\+/g, '%20'))
  } catch (error) {
    return String(value || '')
  }
}