/** * 活动费用展示口径(A3「活动费用展示层口径统一」共用工具) * * 数据契约(A2,字段名与取值对齐 A2 的 ActivityDTO,A2 未合入期间取默认值降级): * audienceScope: 'all'(默认)| 'member_only' —— 是否仅会员可参加 * audienceType : 'adult'(默认)| 'include_child' | 'include_elder' | 'include_child_elder' * * 费用档位(A2 §2.1;长者复用成人价 —— D1 决策:不新增长者价格列): * 成人·非会员 price 成人·会员 memberPrice * 儿童·非会员 childFee 儿童·会员 childMemberFee * * 兼容存量(A2 §2.4):parentFee / parentMemberFee 降级为「成人非会员价」的历史别名 * 成人非会员价 = price > 0 ? price : parentFee * 成人会员价 = memberPrice > 0 ? memberPrice : parentMemberFee * 老活动(只填 parent/parentMember 费用、price 为 0)在展示层仍要正确显示成人价。 * * 注意:ActivityDTO.price 已被后端按会员等级解析(会员拿到 memberPrice), * memberPrice 仍是原始会员价。故本工具取"非会员列"用 price 优先 + parentFee 回落, * 取"会员列"用 memberPrice 优先 + parentMemberFee 回落。 */ export const AUDIENCE_SCOPE_ALL = 'all' export const AUDIENCE_SCOPE_MEMBER_ONLY = 'member_only' export const AUDIENCE_TYPE_ADULT = 'adult' export const AUDIENCE_TYPE_INCLUDE_CHILD = 'include_child' export const AUDIENCE_TYPE_INCLUDE_ELDER = 'include_elder' export const AUDIENCE_TYPE_INCLUDE_CHILD_ELDER = 'include_child_elder' const VALID_AUDIENCE_TYPES = [ AUDIENCE_TYPE_ADULT, AUDIENCE_TYPE_INCLUDE_CHILD, AUDIENCE_TYPE_INCLUDE_ELDER, AUDIENCE_TYPE_INCLUDE_CHILD_ELDER ] export const IDENTITY_ADULT = 'adult' export const IDENTITY_CHILD = 'child' export const IDENTITY_ELDER = 'elder' const IDENTITY_LABELS = { adult: '成人', child: '儿童', elder: '长者' } /* eslint-disable no-restricted-globals */ export function num(value) { if (value === null || value === undefined || value === '') return 0 var n = Number(value) return isNaN(n) ? 0 : n } /* eslint-enable no-restricted-globals */ /** * 归一化 audienceType:空值/非法值一律回落 adult(兼容存量活动与 A2 未合入期) * @param {string} type * @returns {string} 'adult' | 'include_child' | 'include_elder' | 'include_child_elder' */ export function normalizeAudienceType(type) { var t = String(type == null ? '' : type).trim() if (VALID_AUDIENCE_TYPES.indexOf(t) >= 0) return t return AUDIENCE_TYPE_ADULT } /** * 归一化 audienceScope:空值/非法值一律回落 all * @param {string} scope * @returns {string} 'all' | 'member_only' */ export function normalizeAudienceScope(scope) { return scope === AUDIENCE_SCOPE_MEMBER_ONLY ? AUDIENCE_SCOPE_MEMBER_ONLY : AUDIENCE_SCOPE_ALL } /** audienceType 是否含儿童人群 */ export function audienceHasChild(type) { var t = normalizeAudienceType(type) return t === AUDIENCE_TYPE_INCLUDE_CHILD || t === AUDIENCE_TYPE_INCLUDE_CHILD_ELDER } /** audienceType 是否含长者人群 */ export function audienceHasElder(type) { var t = normalizeAudienceType(type) return t === AUDIENCE_TYPE_INCLUDE_ELDER || t === AUDIENCE_TYPE_INCLUDE_CHILD_ELDER } /** * 成人档两列费用(分):{ regular, member } * 非会员列 price 优先、parentFee 回落;会员列 memberPrice 优先、parentMemberFee 回落 */ export function adultFees(activity) { var a = activity || {} var price = num(a.price) var memberPrice = num(a.memberPrice) return { regular: price > 0 ? price : num(a.parentFee), member: memberPrice > 0 ? memberPrice : num(a.parentMemberFee) } } /** 儿童档两列费用(分):{ regular, member } */ export function childFees(activity) { var a = activity || {} return { regular: num(a.childFee), member: num(a.childMemberFee) } } /** 长者档费用:复用成人价(D1:不新增长者价格列) */ export function elderFees(activity) { return adultFees(activity) } /** 身份标签文案:成人 / 儿童 / 长者 */ export function identityLabel(identity) { return IDENTITY_LABELS[identity] || '成人' } /** * 身份判定(A2 §2.4 / D4):age < 18 → 儿童;age >= 60 → 长者; * 其余(含 age 为空/缺失)一律成人 —— 绝不可缺省判为儿童,否则会多收费。 * @param {object} member FamilyMemberVO(需含 age) * @returns {string} 'adult' | 'child' | 'elder' */ export function classifyIdentity(member) { var m = member || {} var age = num(m.age) if (age > 0 && age < 18) return IDENTITY_CHILD if (age >= 60) return IDENTITY_ELDER return IDENTITY_ADULT } /** * 当前用户在某身份下的生效单价(分) * 会员:取会员价列,会员价留空(0)→ 回落非会员价 * 非会员:取非会员价列;长者复用成人价 * @param {object} activity 活动对象(price/memberPrice/childFee/childMemberFee/parentFee/parentMemberFee) * @param {string} identity 'adult' | 'child' | 'elder' * @param {boolean} isMember 当前用户是否会员 * @returns {number} 分值;0 表示免费 */ export function priceForIdentity(activity, identity, isMember) { var fees = identity === IDENTITY_CHILD ? childFees(activity) : adultFees(activity) if (isMember) { return fees.member > 0 ? fees.member : fees.regular } return fees.regular } /** 活动是否收费:任一人群任一档 > 0(纳入 price/memberPrice,修复"只填成人价误显免费") */ export function hasAnyFee(activity) { var adult = adultFees(activity) var child = childFees(activity) return adult.regular > 0 || adult.member > 0 || child.regular > 0 || child.member > 0 } /** 是否存在会员专享价(用于非会员引导开通会员) */ export function hasAnyMemberFee(activity) { var adult = adultFees(activity) var child = childFees(activity) return adult.member > 0 || child.member > 0 } /** * 当前用户可用档位的最小生效单价(分) * 可用档位 = 成人档(恒含)+ 儿童档(audienceType 含 child 时);长者复用成人价,不额外计档 * @returns {number} 0 表示无可用付费档位(免费活动) */ export function minAvailablePrice(activity, isMember) { var a = activity || {} var best = -1 var adult = priceForIdentity(a, IDENTITY_ADULT, isMember) if (adult > 0) best = adult if (audienceHasChild(a.audienceType)) { var child = priceForIdentity(a, IDENTITY_CHILD, isMember) if (child > 0 && (best < 0 || child < best)) best = child } return best < 0 ? 0 : best } /** * 双价展示文案(与活动详情页原 feeText 口径一致) * 会员价更低时:「80.00(会员) / 100.00」;否则只显示主价;regular=0 且 member>0 →「会员免费」 * @param {number} regularFee 非会员价(分) * @param {number} memberFee 会员价(分) * @returns {string} 如 "100.00" / "80.00(会员) / 100.00" / "0.00" / "会员免费" */ export function feeText(regularFee, memberFee) { var regular = num(regularFee) / 100 var member = num(memberFee) / 100 if (member > 0 && member < regular) return member.toFixed(2) + '(会员) / ' + regular.toFixed(2) if (regular === 0 && member === 0) return '0.00' if (regular === 0) return '会员免费' return regular.toFixed(2) } /** * 海报价格文案信息:按 audienceType 选档位标签与价格 * 1) 含儿童人群且儿童档有价 → 儿童价(儿童) * 2) 含长者人群 → 成人价(长者) ← 长者复用成人价 * 3) 其余 → 成人价(成人) * @returns {{label:string, text:string}} label 为 '成人'/'儿童'/'长者'/'免费' */ export function posterPriceInfo(activity) { var a = activity || {} var t = normalizeAudienceType(a.audienceType) var adult = adultFees(a) var child = childFees(a) if (!hasAnyFee(a)) return { label: '免费', text: '免费' } var label = '成人' var regular = adult.regular var member = adult.member if (audienceHasChild(t) && (child.regular > 0 || child.member > 0)) { label = '儿童' regular = child.regular member = child.member } else if (audienceHasElder(t)) { label = '长者' } return { label: label, text: feeText(regular, member) + '(' + label + ')' } } /** * audienceType → 后台多选勾选数组('adult' 恒选) * @param {string} type * @returns {string[]} 如 ['adult', 'child'] */ export function audienceTypeList(type) { var t = normalizeAudienceType(type) var list = ['adult'] if (audienceHasChild(t)) list.push('child') if (audienceHasElder(t)) list.push('elder') return list } /** * 后台多选勾选数组 → audienceType 编码 * @param {string[]} list 如 ['adult', 'child'] * @returns {string} 'adult' | 'include_child' | 'include_elder' | 'include_child_elder' */ export function encodeAudienceType(list) { var arr = list || [] var hasChild = false var hasElder = false for (var i = 0; i < arr.length; i++) { if (arr[i] === 'child') hasChild = true else if (arr[i] === 'elder') hasElder = true } if (hasChild && hasElder) return AUDIENCE_TYPE_INCLUDE_CHILD_ELDER if (hasChild) return AUDIENCE_TYPE_INCLUDE_CHILD if (hasElder) return AUDIENCE_TYPE_INCLUDE_ELDER return AUDIENCE_TYPE_ADULT } /** audienceScope 展示文案 */ export function audienceScopeLabel(scope) { return scope === AUDIENCE_SCOPE_MEMBER_ONLY ? '仅会员参加' : '全部用户' }