This commit is contained in:
魔方熊成祥
2026-04-29 21:38:14 +08:00
commit 1c8de9b5f4
48 changed files with 10797 additions and 0 deletions

View File

@@ -0,0 +1,373 @@
import type {
AppCartListResponse,
AppGoodsDetailResponse,
AppGoodsPageResponse,
AppHomeMerchantGoodsResponse,
AppHomeResponse,
AppRecentNewGoodsResponse,
AppOrderDetailResponse,
AppOrderPageResponse,
CartItemInfo,
GoodsDetailInfo,
GoodsSkuInfo,
GoodsSummary,
AppTradePreviewResponse,
HomeMerchantGoodsBlock,
MerchantId,
MerchantInfo,
OrderDetailInfo,
OrderSummary,
RecommendMerchantInfo,
RecentNewGoodsItem,
TradePreviewInfo,
TradePreviewStoreInfo,
} from '@/types/mall'
type MaybeMallUidPayload = {
uid?: number
memberId?: number
}
type MaybeMallMerchantPayload = {
merchantId?: MerchantId | number
storeId?: number | string
}
type RawRecommendMerchantPayload = Partial<RecommendMerchantInfo> & {
id?: RecommendMerchantInfo['id']
merchantId?: MerchantId | number | string
merchantName?: string
logo?: string
notice?: string
status?: string
rankScore?: number | string
goodsCount?: number | string
recentNewGoodsCount?: number | string
orderCount?: number | string
}
type RawMerchantEntityPayload = Partial<MerchantInfo> & {
id?: MerchantInfo['id'] | string
merchantId?: MerchantId | number | string
merchantName?: string
logo?: string
notice?: string
status?: string
}
type RawGoodsSummaryPayload = Partial<GoodsSummary> & {
merchantId?: MerchantId | number | string
createdAt?: GoodsSummary['createdAt'] | string | null
updatedAt?: GoodsSummary['updatedAt'] | string | null
createAt?: number | string | null
updateAt?: number | string | null
created_at?: number | string | null
updated_at?: number | string | null
createTime?: number | string | null
updateTime?: number | string | null
create_time?: number | string | null
update_time?: number | string | null
}
const toMerchantId = (value: MerchantId | number | string | null | undefined): MerchantId =>
value == null ? '' : String(value)
const toNumber = (value: number | string | null | undefined): number => {
const parsed = Number(value)
return Number.isFinite(parsed) ? parsed : 0
}
const pickText = (...values: Array<string | null | undefined>): string => {
for (const value of values) {
if (typeof value === 'string' && value.trim()) {
return value
}
}
return ''
}
const toUnixTime = (value: number | string | null | undefined): number => {
if (typeof value === 'number' && Number.isFinite(value)) {
return value > 1_000_000_000_000 ? Math.floor(value / 1000) : value
}
if (typeof value !== 'string') {
return 0
}
const text = value.trim()
if (!text) {
return 0
}
const numeric = Number(text)
if (Number.isFinite(numeric)) {
return numeric > 1_000_000_000_000 ? Math.floor(numeric / 1000) : numeric
}
const parsed = Date.parse(text.replace(/-/g, '/'))
return Number.isFinite(parsed) ? Math.floor(parsed / 1000) : 0
}
export const normalizeMallAppPayload = <TPayload extends object>(payload: TPayload): TPayload => {
const normalized = { ...payload } as TPayload & MaybeMallUidPayload & MaybeMallMerchantPayload
if (normalized.uid == null && normalized.memberId != null) {
normalized.uid = normalized.memberId
}
delete normalized.memberId
if (normalized.merchantId != null) {
normalized.merchantId = toMerchantId(normalized.merchantId)
}
if (normalized.merchantId == null && normalized.storeId != null) {
normalized.merchantId = toMerchantId(normalized.storeId)
}
delete normalized.storeId
return normalized as TPayload
}
const normalizeGoodsSku = (sku: GoodsSkuInfo): GoodsSkuInfo => ({
...sku,
merchantId: toMerchantId(sku.merchantId),
})
const normalizeGoodsSummary = (goods: RawGoodsSummaryPayload): GoodsSummary => {
const normalized = goods as GoodsSummary
return {
...normalized,
merchantId: toMerchantId(goods.merchantId),
merchantName: pickText(goods.merchantName),
merchantLogo: pickText(goods.merchantLogo),
createdAt: toUnixTime(goods.createdAt ?? goods.createAt ?? goods.created_at ?? goods.createTime ?? goods.create_time),
updatedAt: toUnixTime(goods.updatedAt ?? goods.updateAt ?? goods.updated_at ?? goods.updateTime ?? goods.update_time),
}
}
const normalizeRecentNewGoodsItem = (goods: RecentNewGoodsItem): RecentNewGoodsItem => ({
...goods,
merchantId: goods.merchantId == null ? undefined : toMerchantId(goods.merchantId),
merchantName: pickText(goods.merchantName, goods.storeName),
merchantLogo: goods.merchantLogo?.trim(),
storeName: pickText(goods.storeName, goods.merchantName),
spuId: String(goods.spuId),
createdAt: toUnixTime(goods.createdAt),
})
const normalizeGoodsDetailInfo = (detail: GoodsDetailInfo): GoodsDetailInfo => ({
...detail,
merchantId: toMerchantId(detail.merchantId),
createdAt: toUnixTime(detail.createdAt),
updatedAt: toUnixTime(detail.updatedAt),
skus: (detail.skus ?? []).map(normalizeGoodsSku),
})
const normalizeCartItem = (item: CartItemInfo): CartItemInfo => ({
...item,
merchantId: toMerchantId(item.merchantId),
})
const normalizeTradePreviewGroup = (group: TradePreviewStoreInfo): TradePreviewStoreInfo => ({
...group,
merchantId: toMerchantId(group.merchantId),
})
const normalizeOrderSummary = (order: OrderSummary): OrderSummary => ({
...order,
merchantId: toMerchantId(order.merchantId),
})
const normalizeOrderDetailInfo = (order: OrderDetailInfo): OrderDetailInfo => ({
...order,
merchantId: toMerchantId(order.merchantId),
})
const normalizeRecommendMerchant = (
merchant: RawRecommendMerchantPayload | null | undefined
): RecommendMerchantInfo | undefined => {
if (!merchant) {
return undefined
}
return {
...(merchant as RecommendMerchantInfo),
merchantId: toMerchantId(merchant.merchantId ?? merchant.id),
storeName: pickText(merchant.storeName, merchant.merchantName),
storeLogo: pickText(merchant.storeLogo, merchant.logo),
storeNotice: pickText(merchant.storeNotice, merchant.notice),
storeStatus: (merchant.storeStatus ?? merchant.status ?? 'enabled') as RecommendMerchantInfo['storeStatus'],
rankScore: toNumber(merchant.rankScore),
goodsCount: toNumber(merchant.goodsCount),
recentNewGoodsCount: toNumber(merchant.recentNewGoodsCount),
orderCount: toNumber(merchant.orderCount),
}
}
const normalizeMerchantEntity = (
merchant: RawMerchantEntityPayload | null | undefined
): MerchantInfo | undefined => {
if (!merchant) {
return undefined
}
return {
...(merchant as MerchantInfo),
merchantId: toMerchantId(merchant.merchantId ?? merchant.id),
storeName: pickText(merchant.storeName, merchant.merchantName),
storeLogo: pickText(merchant.storeLogo, merchant.logo),
storeNotice: pickText(merchant.storeNotice, merchant.notice),
storeStatus: (merchant.storeStatus ?? merchant.status ?? 'enabled') as MerchantInfo['storeStatus'],
}
}
const normalizeHomeMerchantGoodsBlock = (
block: HomeMerchantGoodsBlock | undefined
): HomeMerchantGoodsBlock | undefined => {
if (!block) {
return undefined
}
const merchant = normalizeRecommendMerchant(block.merchant ?? block.store)
if (!merchant) {
return undefined
}
return {
...block,
merchant,
goods: block.goods.map(normalizeGoodsSummary),
store: merchant,
}
}
export const normalizeAppGoodsPageResponse = (
payload: AppGoodsPageResponse
): AppGoodsPageResponse => ({
...payload,
list: payload.list.map(normalizeGoodsSummary),
})
export const normalizeAppRecentNewGoodsResponse = (
payload: AppRecentNewGoodsResponse
): AppRecentNewGoodsResponse => ({
...payload,
total: payload.total ?? 0,
list: (payload.list ?? []).map(normalizeRecentNewGoodsItem),
})
export const normalizeAppCartListResponse = (
payload: AppCartListResponse
): AppCartListResponse => ({
...payload,
list: payload.list.map(normalizeCartItem),
})
export const normalizeAppOrderPageResponse = (
payload: AppOrderPageResponse
): AppOrderPageResponse => ({
...payload,
list: payload.list.map(normalizeOrderSummary),
})
export const normalizeAppOrderDetailResponse = (
payload: AppOrderDetailResponse
): AppOrderDetailResponse => ({
...payload,
data: normalizeOrderDetailInfo(payload.data),
})
export const normalizeAppHomeResponse = (payload: AppHomeResponse): AppHomeResponse => {
const recommendMerchants = (payload.recommendMerchants ?? payload.recommendStores ?? [])
.map(normalizeRecommendMerchant)
.filter(Boolean) as RecommendMerchantInfo[]
const featuredMerchants = (payload.featuredMerchants ?? payload.featuredStores ?? [])
.map(normalizeRecommendMerchant)
.filter(Boolean) as RecommendMerchantInfo[]
return {
...payload,
recommendGoods: payload.recommendGoods.map(normalizeGoodsSummary),
recommendMerchants,
featuredMerchants,
recommendStores: recommendMerchants,
featuredStores: featuredMerchants,
}
}
export const normalizeAppHomeMerchantGoodsResponse = (
payload: AppHomeMerchantGoodsResponse
): AppHomeMerchantGoodsResponse => ({
...payload,
data:
normalizeHomeMerchantGoodsBlock(payload.data) ??
({
merchant: {
id: 0,
merchantId: '0',
storeName: '',
storeLogo: '',
storeNotice: '',
storeStatus: 'disabled',
mainCategoryName: '',
recommendReason: '',
rankScore: 0,
goodsCount: 0,
recentNewGoodsCount: 0,
orderCount: 0,
},
store: {
id: 0,
merchantId: '0',
storeName: '',
storeLogo: '',
storeNotice: '',
storeStatus: 'disabled',
mainCategoryName: '',
recommendReason: '',
rankScore: 0,
goodsCount: 0,
recentNewGoodsCount: 0,
orderCount: 0,
},
goods: [],
} satisfies HomeMerchantGoodsBlock),
})
export const normalizeAppGoodsDetailResponse = (
payload: AppGoodsDetailResponse
): AppGoodsDetailResponse => {
const merchant = normalizeMerchantEntity(payload.merchant ?? payload.store)
return {
...payload,
data: normalizeGoodsDetailInfo(payload.data),
merchant: merchant ?? payload.store!,
store: merchant ?? payload.store,
}
}
export const normalizeAppTradePreviewResponse = (
payload: AppTradePreviewResponse
): AppTradePreviewResponse => {
const data = payload.data as TradePreviewInfo & {
uid?: number
memberId?: number
merchantGroups?: TradePreviewInfo['merchantGroups']
storeGroups?: TradePreviewInfo['merchantGroups']
}
const merchantGroups = data.merchantGroups ?? data.storeGroups ?? []
return {
...payload,
data: {
...data,
uid: data.uid ?? data.memberId ?? 0,
memberId: data.memberId ?? data.uid,
merchantGroups: merchantGroups.map(normalizeTradePreviewGroup),
storeGroups: merchantGroups.map(normalizeTradePreviewGroup),
},
}
}