corp-mp/src/lib/order.ts

225 lines
6.7 KiB
TypeScript

import { isCorpGoodsType } from './detail'
export type UploadedImage = {
id: string
url: string
preview: string
}
export type OrderLike = {
order_id?: string
goods_type?: string
goods_name?: string
entity_name?: string
entity_phone?: string
entity_extra?: Record<string, unknown>
extra?: Record<string, any>
[key: string]: any
}
export type ResubmitValues = {
entity_name?: string
entity_address_name?: string
entity_address?: string
entity_phone?: string
entity_phone2?: string
}
const OMIT_EXTRA_KEYS = new Set([
'origin_price',
'qrCodeImgUrl',
'remoteIp',
'weixinAppId',
'entity_storefront_image',
'entity_business_license_image',
'entity_storefront_image_url',
'entity_business_license_image_url',
'goods_param',
])
function splitValues (value: unknown) {
if (value === undefined || value === null) return []
return String(value)
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
function isEmptySlotToken (value?: string) {
return !value || value === 'null' || value === '0'
}
function splitSlotValues (value: unknown) {
if (value === undefined || value === null || value === '') return []
return String(value).split(',').map((item) => item.trim())
}
export type StorefrontSlot = UploadedImage | null
export function joinStorefrontSlots (slots: Array<StorefrontSlot | undefined> = []) {
return [0, 1, 2].map((index) => {
const id = slots[index]?.id?.trim()
return id ? id : '0'
}).join(',')
}
export function parseStorefrontSlots (order: OrderLike | null | undefined): StorefrontSlot[] {
const extra = order?.extra || {}
const ids = splitSlotValues(extra.entity_storefront_image)
const urls = splitSlotValues(extra.entity_storefront_image_url)
return [0, 1, 2].map((index) => {
const id = ids[index] || ''
const url = urls[index] || ''
if (isEmptySlotToken(id) && isEmptySlotToken(url)) return null
if (isEmptySlotToken(id) && !isEmptySlotToken(url)) {
return { id: '', url, preview: url }
}
const resolvedUrl = !isEmptySlotToken(url)
? url
: (/^https?:\/\//i.test(id) ? id : '')
return {
id: /^https?:\/\//i.test(id) ? '' : id,
url: resolvedUrl,
preview: resolvedUrl,
}
})
}
export function isCoordinateAddress (address?: string) {
if (!address) return false
const parts = address.split(',').map((part) => part.trim())
if (parts.length !== 2) return false
return parts.every((part) => Number.isFinite(Number(part)))
}
export function parseCoordinate (address?: string) {
if (!isCoordinateAddress(address)) return null
const [lng, lat] = String(address).split(',').map((part) => Number(part.trim()))
return { lng, lat }
}
export function formatMapCoordinate (point: { lng: number, lat: number }) {
return `${point.lng.toFixed(6)},${point.lat.toFixed(6)}`
}
export function formatMapCoordinateDisplay (point: { lng: number, lat: number }) {
return `N${point.lat.toFixed(6)}°, E${point.lng.toFixed(6)}°`
}
export function getOrderAddressName (order: OrderLike) {
const extra = order.extra || {}
return extra.entity_address_name || (!isCoordinateAddress(extra.entity_address) ? extra.entity_address : '') || ''
}
export function getOrderMapAddress (order: OrderLike) {
const extra = order.extra || {}
return isCoordinateAddress(extra.entity_address) ? extra.entity_address : ''
}
export function getInitialPhone2 (order: OrderLike) {
const extra = order.extra || {}
return String(extra.entity_phone2 || extra.entity_phone_2 || extra.phone2 || '')
}
export function getGoodsIcons (order: OrderLike) {
const icons = order.extra?.goods_param?.['client.goods.icon']
return Array.isArray(icons) ? icons.filter((item) => typeof item === 'string') : []
}
export function normalizeOrders<T extends OrderLike> (orders: T[] = []) {
return orders.map((item) => {
const nextItem = { ...item, extra: { ...(item.extra || {}) } }
const goodsParam = nextItem.extra.goods_param
if (typeof goodsParam === 'string') {
try {
nextItem.extra.goods_param = JSON.parse(goodsParam)
} catch {
nextItem.extra.goods_param = null
}
}
return nextItem
})
}
export const MAP_LABEL_DISPLAY_NAME = '商户资料服务'
export function getOrderDisplayName (order?: OrderLike | null) {
if (order?.goods_type === 'map_label') return MAP_LABEL_DISPLAY_NAME
return String(order?.goods_name || '').trim() || '-'
}
export function filterOrdersForProduct<T extends OrderLike> (goodsType: string, orders: T[] = []) {
const normalized = normalizeOrders(orders)
if (isCorpGoodsType(goodsType)) return normalized
const target = goodsType || 'map_label'
return normalized.filter((item) => item.goods_type === target)
}
export function filterMapLabelOrders<T extends OrderLike> (orders: T[] = []) {
return filterOrdersForProduct('map_label', orders)
}
export function getOrderExtraImages (order: OrderLike | null | undefined, keys: string[]): UploadedImage[] {
const extra = order?.extra || {}
for (const key of keys) {
const values = splitValues(extra[key])
const explicitIds = splitValues(extra[`${key}_id`])
const explicitUrls = splitValues(extra[`${key}_url`])
const urls = explicitUrls.length > 0
? explicitUrls
: values.filter((value) => /^https?:\/\//i.test(value))
if (urls.length > 0) {
return urls.map((url, index) => {
const valueId = values[index] && values[index] !== url ? values[index] : ''
return {
id: valueId || explicitIds[index] || '',
url,
preview: url,
}
})
}
}
return []
}
export function joinUploadedImageIds (images: UploadedImage[]) {
return images
.map((image) => image.id.trim())
.filter(Boolean)
.join(',')
}
export function buildResubmitPayload (input: {
order: OrderLike
values: ResubmitValues
storefrontSlots: StorefrontSlot[]
licenseImages: UploadedImage[]
}) {
const existingExtra = Object.entries(input.order.extra || {}).reduce<Record<string, unknown>>((result, [key, value]) => {
if (!OMIT_EXTRA_KEYS.has(key)) {
result[key] = value
}
return result
}, {})
return {
id: String(input.order.order_id || ''),
corp_id: 0,
entity_name: input.values.entity_name || input.order.entity_name || '',
entity_phone: input.values.entity_phone || '',
entity_extra: input.order.entity_extra || {},
extra: {
...existingExtra,
entity_address_name: input.values.entity_address_name || '',
entity_address: input.values.entity_address || '',
entity_phone2: input.values.entity_phone2 || '',
entity_storefront_image: joinStorefrontSlots(input.storefrontSlots),
entity_business_license_image: joinUploadedImageIds(input.licenseImages),
},
}
}