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,15 @@
import { APP_API_PATHS, normalizeMallAppPayload, postMallApp } from './shared'
import type {
AppAfterSaleApplyRequest,
AppAfterSaleApplyResponse,
} from '@/types/mall'
export const apiAppAfterSaleApply = (
payload: AppAfterSaleApplyRequest,
signal?: AbortSignal
) =>
postMallApp<AppAfterSaleApplyResponse, AppAfterSaleApplyRequest>(
APP_API_PATHS.afterSaleApply,
normalizeMallAppPayload(payload),
signal
)

36
src/apis/mall-app/cart.ts Normal file
View File

@@ -0,0 +1,36 @@
import { APP_API_PATHS, normalizeAppCartListResponse, normalizeMallAppPayload, postMallApp } from './shared'
import type {
AppCartListRequest,
AppCartListResponse,
AppCartRemoveRequest,
AppCartRemoveResponse,
AppCartSaveRequest,
AppCartSaveResponse,
} from '@/types/mall'
export const apiAppCartList = async (payload: AppCartListRequest, signal?: AbortSignal) => {
const response = await postMallApp<AppCartListResponse, AppCartListRequest>(
APP_API_PATHS.cartList,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppCartListResponse(response.data),
}
}
export const apiAppCartSave = (payload: AppCartSaveRequest, signal?: AbortSignal) =>
postMallApp<AppCartSaveResponse, AppCartSaveRequest>(
APP_API_PATHS.cartSave,
normalizeMallAppPayload(payload),
signal
)
export const apiAppCartRemove = (payload: AppCartRemoveRequest, signal?: AbortSignal) =>
postMallApp<AppCartRemoveResponse, AppCartRemoveRequest>(
APP_API_PATHS.cartRemove,
normalizeMallAppPayload(payload),
signal
)

View File

@@ -0,0 +1,39 @@
import {
APP_API_PATHS,
normalizeAppGoodsDetailResponse,
normalizeAppGoodsPageResponse,
normalizeMallAppPayload,
postMallApp,
} from './shared'
import type {
AppGoodsDetailRequest,
AppGoodsDetailResponse,
AppGoodsPageRequest,
AppGoodsPageResponse,
} from '@/types/mall'
export const apiAppGoodsPage = async (payload: AppGoodsPageRequest, signal?: AbortSignal) => {
const response = await postMallApp<AppGoodsPageResponse, AppGoodsPageRequest>(
APP_API_PATHS.goodsPage,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppGoodsPageResponse(response.data),
}
}
export const apiAppGoodsDetail = async (payload: AppGoodsDetailRequest, signal?: AbortSignal) => {
const response = await postMallApp<AppGoodsDetailResponse, AppGoodsDetailRequest>(
APP_API_PATHS.goodsDetail,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppGoodsDetailResponse(response.data),
}
}

67
src/apis/mall-app/home.ts Normal file
View File

@@ -0,0 +1,67 @@
import {
APP_API_PATHS,
normalizeAppHomeMerchantGoodsResponse,
normalizeAppHomeResponse,
normalizeAppRecentNewGoodsResponse,
normalizeMallAppPayload,
postMallApp,
} from './shared'
import type {
AppHomeMerchantGoodsRequest,
AppHomeMerchantGoodsResponse,
AppHomeRequest,
AppHomeResponse,
AppHomeStoreGoodsRequest,
AppRecentNewGoodsRequest,
AppRecentNewGoodsResponse,
} from '@/types/mall'
export const apiAppHome = async (payload: AppHomeRequest = {}, signal?: AbortSignal) => {
const response = await postMallApp<AppHomeResponse, AppHomeRequest>(
APP_API_PATHS.home,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppHomeResponse(response.data),
}
}
export const apiAppRecentNewGoods = async (
payload: AppRecentNewGoodsRequest = {},
signal?: AbortSignal
) => {
const response = await postMallApp<AppRecentNewGoodsResponse, AppRecentNewGoodsRequest>(
APP_API_PATHS.recentNewGoods,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppRecentNewGoodsResponse(response.data),
}
}
export const apiAppHomeMerchantGoods = async (
payload: AppHomeMerchantGoodsRequest = {},
signal?: AbortSignal
) => {
const response = await postMallApp<AppHomeMerchantGoodsResponse, AppHomeMerchantGoodsRequest>(
APP_API_PATHS.homeMerchantGoods,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppHomeMerchantGoodsResponse(response.data),
}
}
export const apiAppHomeStoreGoods = (
payload: AppHomeStoreGoodsRequest = {},
signal?: AbortSignal
) => apiAppHomeMerchantGoods(payload, signal)

View File

@@ -0,0 +1,7 @@
export * from './shared'
export * from './home'
export * from './goods'
export * from './cart'
export * from './trade'
export * from './order'
export * from './afterSale'

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),
},
}
}

View File

@@ -0,0 +1,48 @@
import {
APP_API_PATHS,
normalizeAppOrderDetailResponse,
normalizeAppOrderPageResponse,
normalizeMallAppPayload,
postMallApp,
} from './shared'
import type {
AppOrderConfirmRequest,
AppOrderConfirmResponse,
AppOrderDetailRequest,
AppOrderDetailResponse,
AppOrderPageRequest,
AppOrderPageResponse,
} from '@/types/mall'
export const apiAppOrderPage = async (payload: AppOrderPageRequest, signal?: AbortSignal) => {
const response = await postMallApp<AppOrderPageResponse, AppOrderPageRequest>(
APP_API_PATHS.orderPage,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppOrderPageResponse(response.data),
}
}
export const apiAppOrderDetail = async (payload: AppOrderDetailRequest, signal?: AbortSignal) => {
const response = await postMallApp<AppOrderDetailResponse, AppOrderDetailRequest>(
APP_API_PATHS.orderDetail,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppOrderDetailResponse(response.data),
}
}
export const apiAppOrderConfirm = (payload: AppOrderConfirmRequest, signal?: AbortSignal) =>
postMallApp<AppOrderConfirmResponse, AppOrderConfirmRequest>(
APP_API_PATHS.orderConfirm,
normalizeMallAppPayload(payload),
signal
)

View File

@@ -0,0 +1,34 @@
import axiosInstance from '@/apis/base/axiosInstance'
import type { IBaseResponse } from '@/apis/base/types'
export const APP_API_PATHS = {
home: '/api/v1/mall/app/home',
recentNewGoods: '/api/v1/mall/app/recent_new_goods',
homeMerchantGoods: '/api/v1/mall/app/home_merchant_goods',
homeStoreGoods: '/api/v1/mall/app/home_merchant_goods',
goodsPage: '/api/v1/mall/app/goods_page',
goodsDetail: '/api/v1/mall/app/goods_detail',
cartList: '/api/v1/mall/app/cart_list',
cartSave: '/api/v1/mall/app/cart_save',
cartRemove: '/api/v1/mall/app/cart_remove',
tradePreview: '/api/v1/mall/app/trade_preview',
tradeCreate: '/api/v1/mall/app/trade_create',
orderPage: '/api/v1/mall/app/order_page',
orderDetail: '/api/v1/mall/app/order_detail',
orderConfirm: '/api/v1/mall/app/order_confirm',
afterSaleApply: '/api/v1/mall/app/after_sale_apply',
} as const
export const postMallApp = async <TData, TPayload>(
path: string,
payload: TPayload,
signal?: AbortSignal
): Promise<IBaseResponse<TData>> => {
const response = await axiosInstance.post<IBaseResponse<TData>>(path, payload, {
signal,
})
return response.data
}
export * from './normalize'

View File

@@ -0,0 +1,32 @@
import {
APP_API_PATHS,
normalizeAppTradePreviewResponse,
normalizeMallAppPayload,
postMallApp,
} from './shared'
import type {
AppTradeCreateRequest,
AppTradeCreateResponse,
AppTradePreviewRequest,
AppTradePreviewResponse,
} from '@/types/mall'
export const apiAppTradePreview = async (payload: AppTradePreviewRequest, signal?: AbortSignal) => {
const response = await postMallApp<AppTradePreviewResponse, AppTradePreviewRequest>(
APP_API_PATHS.tradePreview,
normalizeMallAppPayload(payload),
signal
)
return {
...response,
data: normalizeAppTradePreviewResponse(response.data),
}
}
export const apiAppTradeCreate = (payload: AppTradeCreateRequest, signal?: AbortSignal) =>
postMallApp<AppTradeCreateResponse, AppTradeCreateRequest>(
APP_API_PATHS.tradeCreate,
normalizeMallAppPayload(payload),
signal
)