refactor(passport): 移除统一扫码页面中的核销功能
- 移除扫码页面中的核销类型相关代码 - 移除核销成功后的提示弹窗逻辑 - 移除核销相关的标签显示 - 更新页面描述文案,移除核销相关内容 - 移除核销类型的历史记录展示逻辑 - 简化扫码结果处理流程 - 移除大量已删除的工具函数和组件文件 - 恢复开发环境API配置为本地地址 - 移除支付类型枚举和支付处理器类 - 移除订单商品数据标准化工具函数 - 移除商品列表和订单列表组件 - 移除优惠券数据转换和计算相关工具函数 - 移除支付方式API接口和模型定义 - 移除规格选择器组件
This commit is contained in:
@@ -1,200 +0,0 @@
|
||||
import { ShopUserCoupon } from '@/api/shop/shopUserCoupon/model'
|
||||
import { CouponCardProps } from '@/components/CouponCard'
|
||||
|
||||
/**
|
||||
* 将后端优惠券数据转换为前端组件所需格式
|
||||
*/
|
||||
export const transformCouponData = (coupon: ShopUserCoupon): CouponCardProps => {
|
||||
// 解析金额
|
||||
let amount = 0
|
||||
if (coupon.type === 10) {
|
||||
// 满减券:使用reducePrice
|
||||
amount = parseFloat(coupon.reducePrice || '0')
|
||||
} else if (coupon.type === 20) {
|
||||
// 折扣券:使用discount
|
||||
amount = coupon.discount || 0
|
||||
} else if (coupon.type === 30) {
|
||||
// 免费券:金额为0
|
||||
amount = 0
|
||||
}
|
||||
|
||||
// 解析最低消费金额
|
||||
const minAmount = parseFloat(coupon.minPrice || '0')
|
||||
|
||||
// 确定主题颜色
|
||||
const getTheme = (type?: number): CouponCardProps['theme'] => {
|
||||
switch (type) {
|
||||
case 10: return 'red' // 满减券-红色
|
||||
case 20: return 'orange' // 折扣券-橙色
|
||||
case 30: return 'green' // 免费券-绿色
|
||||
default: return 'blue'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: coupon.id,
|
||||
amount,
|
||||
minAmount: minAmount > 0 ? minAmount : undefined,
|
||||
type: coupon.type as 10 | 20 | 30,
|
||||
status: coupon.status as 0 | 1 | 2,
|
||||
statusText: coupon.statusText,
|
||||
title: coupon.name || coupon.description || '优惠券',
|
||||
description: coupon.description,
|
||||
startTime: coupon.startTime,
|
||||
endTime: coupon.endTime,
|
||||
isExpiringSoon: coupon.isExpiringSoon,
|
||||
daysRemaining: coupon.daysRemaining,
|
||||
hoursRemaining: coupon.hoursRemaining,
|
||||
theme: getTheme(coupon.type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算优惠券折扣金额
|
||||
*/
|
||||
export const calculateCouponDiscount = (
|
||||
coupon: CouponCardProps,
|
||||
totalAmount: number
|
||||
): number => {
|
||||
// 检查是否满足使用条件
|
||||
if (coupon.minAmount && totalAmount < coupon.minAmount) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 检查优惠券状态
|
||||
if (coupon.status !== 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch (coupon.type) {
|
||||
case 10: // 满减券
|
||||
return coupon.amount
|
||||
case 20: // 折扣券
|
||||
return totalAmount * (1 - coupon.amount / 10)
|
||||
case 30: // 免费券
|
||||
return totalAmount
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查优惠券是否可用
|
||||
*/
|
||||
export const isCouponUsable = (
|
||||
coupon: CouponCardProps,
|
||||
totalAmount: number
|
||||
): boolean => {
|
||||
// 状态检查
|
||||
if (coupon.status !== 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 金额条件检查
|
||||
if (coupon.minAmount && totalAmount < coupon.minAmount) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取优惠券不可用原因
|
||||
*/
|
||||
export const getCouponUnusableReason = (
|
||||
coupon: CouponCardProps,
|
||||
totalAmount: number
|
||||
): string => {
|
||||
if (coupon.status === 1) {
|
||||
return '优惠券已使用'
|
||||
}
|
||||
|
||||
if (coupon.status === 2) {
|
||||
return '优惠券已过期'
|
||||
}
|
||||
|
||||
if (coupon.minAmount && totalAmount < coupon.minAmount) {
|
||||
return `需满${coupon.minAmount}元才能使用`
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化优惠券标题
|
||||
*/
|
||||
export const formatCouponTitle = (coupon: CouponCardProps): string => {
|
||||
if (coupon.title) {
|
||||
return coupon.title
|
||||
}
|
||||
|
||||
switch (coupon.type) {
|
||||
case 10: // 满减券
|
||||
if (coupon.minAmount && coupon.minAmount > 0) {
|
||||
return `满${coupon.minAmount}减${coupon.amount}`
|
||||
}
|
||||
return `立减${coupon.amount}元`
|
||||
case 20: // 折扣券
|
||||
if (coupon.minAmount && coupon.minAmount > 0) {
|
||||
return `满${coupon.minAmount}享${coupon.amount}折`
|
||||
}
|
||||
return `${coupon.amount}折优惠`
|
||||
case 30: // 免费券
|
||||
return '免费券'
|
||||
default:
|
||||
return '优惠券'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序优惠券列表
|
||||
* 按照优惠金额从大到小排序,同等优惠金额按过期时间排序
|
||||
*/
|
||||
export const sortCoupons = (
|
||||
coupons: CouponCardProps[],
|
||||
totalAmount: number
|
||||
): CouponCardProps[] => {
|
||||
return [...coupons].sort((a, b) => {
|
||||
// 先按可用性排序
|
||||
const aUsable = isCouponUsable(a, totalAmount)
|
||||
const bUsable = isCouponUsable(b, totalAmount)
|
||||
|
||||
if (aUsable && !bUsable) return -1
|
||||
if (!aUsable && bUsable) return 1
|
||||
|
||||
// 都可用或都不可用时,按优惠金额排序
|
||||
const aDiscount = calculateCouponDiscount(a, totalAmount)
|
||||
const bDiscount = calculateCouponDiscount(b, totalAmount)
|
||||
|
||||
if (aDiscount !== bDiscount) {
|
||||
return bDiscount - aDiscount // 优惠金额大的在前
|
||||
}
|
||||
|
||||
// 优惠金额相同时,按过期时间排序(即将过期的在前)
|
||||
if (a.endTime && b.endTime) {
|
||||
return new Date(a.endTime).getTime() - new Date(b.endTime).getTime()
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤可用优惠券
|
||||
*/
|
||||
export const filterUsableCoupons = (
|
||||
coupons: CouponCardProps[],
|
||||
totalAmount: number
|
||||
): CouponCardProps[] => {
|
||||
return coupons.filter(coupon => isCouponUsable(coupon, totalAmount))
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤不可用优惠券
|
||||
*/
|
||||
export const filterUnusableCoupons = (
|
||||
coupons: CouponCardProps[],
|
||||
totalAmount: number
|
||||
): CouponCardProps[] => {
|
||||
return coupons.filter(coupon => !isCouponUsable(coupon, totalAmount))
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { ShopOrderGoods } from '@/api/shop/shopOrderGoods/model';
|
||||
|
||||
/**
|
||||
* Normalize order goods data returned by the order/page API.
|
||||
*
|
||||
* In practice different backends may return different field names (orderGoods/orderGoodsList/goodsList...),
|
||||
* and the item fields can also differ (goodsName/title/name, totalNum/quantity, etc.).
|
||||
*
|
||||
* We normalize them to ShopOrderGoods so list pages can render without doing N+1 requests per order.
|
||||
*/
|
||||
export const normalizeOrderGoodsList = (order: any): ShopOrderGoods[] => {
|
||||
const raw =
|
||||
order?.orderGoods ||
|
||||
order?.orderGoodsList ||
|
||||
order?.goodsList ||
|
||||
order?.goods ||
|
||||
[];
|
||||
if (!Array.isArray(raw)) return [];
|
||||
|
||||
return raw.map((g: any) => ({
|
||||
...g,
|
||||
goodsId: g?.goodsId ?? g?.itemId ?? g?.goods_id,
|
||||
skuId: g?.skuId ?? g?.sku_id,
|
||||
// When the API returns minimal fields, fall back to order title to avoid blank names.
|
||||
goodsName: g?.goodsName ?? g?.goodsTitle ?? g?.title ?? g?.name ?? order?.title ?? '商品',
|
||||
image: g?.image ?? g?.goodsImage ?? g?.cover ?? g?.pic,
|
||||
spec: g?.spec ?? g?.specInfo ?? g?.spec_name,
|
||||
totalNum: g?.totalNum ?? g?.quantity ?? g?.num ?? g?.count,
|
||||
price: g?.price ?? g?.payPrice ?? g?.goodsPrice ?? g?.unitPrice
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -1,514 +0,0 @@
|
||||
import Taro from '@tarojs/taro';
|
||||
import { createOrder, WxPayResult } from '@/api/shop/shopOrder';
|
||||
import { OrderCreateRequest } from '@/api/shop/shopOrder/model';
|
||||
import { getSelectedStoreFromStorage, getSelectedStoreIdFromStorage } from '@/utils/storeSelection';
|
||||
import type { ShopStoreRider } from '@/api/shop/shopStoreRider/model';
|
||||
import type { ShopStoreWarehouse } from '@/api/shop/shopStoreWarehouse/model';
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 支付类型枚举
|
||||
*/
|
||||
export enum PaymentType {
|
||||
BALANCE = 0, // 余额支付
|
||||
WECHAT = 1, // 微信支付
|
||||
ALIPAY = 3, // 支付宝支付
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付结果回调
|
||||
*/
|
||||
export interface PaymentCallback {
|
||||
onSuccess?: () => void;
|
||||
onError?: (error: string) => void;
|
||||
onComplete?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一支付处理类
|
||||
*/
|
||||
export class PaymentHandler {
|
||||
// 简单缓存,避免频繁请求(小程序单次运行生命周期内有效)
|
||||
private static storeRidersCache = new Map<number, ShopStoreRider[]>();
|
||||
private static warehousesCache: ShopStoreWarehouse[] | null = null;
|
||||
|
||||
/**
|
||||
* 执行支付
|
||||
* @param orderData 订单数据
|
||||
* @param paymentType 支付类型
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
static async pay(
|
||||
orderData: OrderCreateRequest,
|
||||
paymentType: PaymentType,
|
||||
callback?: PaymentCallback
|
||||
): Promise<void> {
|
||||
Taro.showLoading({ title: '支付中...' });
|
||||
|
||||
try {
|
||||
// 若调用方未指定门店,则自动注入“已选门店”,用于订单门店归属/统计。
|
||||
if (orderData.storeId === undefined || orderData.storeId === null) {
|
||||
const storeId = getSelectedStoreIdFromStorage();
|
||||
if (storeId) {
|
||||
orderData.storeId = storeId;
|
||||
}
|
||||
}
|
||||
if (!orderData.storeName) {
|
||||
const store = getSelectedStoreFromStorage();
|
||||
if (store?.name) {
|
||||
orderData.storeName = store.name;
|
||||
}
|
||||
}
|
||||
|
||||
// 自动派单:按门店骑手优先级(dispatchPriority)选择 riderId(不覆盖手动指定)
|
||||
if ((orderData.riderId === undefined || orderData.riderId === null) && orderData.storeId) {
|
||||
const riderUserId = await this.pickRiderUserIdForStore(orderData.storeId);
|
||||
if (riderUserId) {
|
||||
orderData.riderId = riderUserId;
|
||||
}
|
||||
}
|
||||
|
||||
// 仓库选择:若未指定 warehouseId,则按“离门店最近”兜底选择一个(不覆盖手动指定)
|
||||
if ((orderData.warehouseId === undefined || orderData.warehouseId === null) && orderData.storeId) {
|
||||
const warehouseId = await this.pickWarehouseIdForStore(orderData.storeId);
|
||||
if (warehouseId) {
|
||||
orderData.warehouseId = warehouseId;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置支付类型
|
||||
orderData.payType = paymentType;
|
||||
|
||||
console.log('创建订单请求:', orderData);
|
||||
|
||||
// 创建订单
|
||||
const result = await createOrder(orderData);
|
||||
|
||||
console.log('订单创建结果:', result);
|
||||
|
||||
if (!result) {
|
||||
throw new Error('创建订单失败');
|
||||
}
|
||||
|
||||
// 验证订单创建结果
|
||||
if (!result.orderNo) {
|
||||
throw new Error('订单号获取失败');
|
||||
}
|
||||
|
||||
let paymentSuccess = false;
|
||||
|
||||
// 根据支付类型处理
|
||||
switch (paymentType) {
|
||||
case PaymentType.WECHAT:
|
||||
await this.handleWechatPay(result);
|
||||
paymentSuccess = true;
|
||||
break;
|
||||
case PaymentType.BALANCE:
|
||||
paymentSuccess = await this.handleBalancePay(result);
|
||||
break;
|
||||
case PaymentType.ALIPAY:
|
||||
await this.handleAlipay(result);
|
||||
paymentSuccess = true;
|
||||
break;
|
||||
default:
|
||||
throw new Error('不支持的支付方式');
|
||||
}
|
||||
|
||||
// 只有确认支付成功才显示成功提示和跳转
|
||||
if (paymentSuccess) {
|
||||
console.log('支付成功,订单号:', result.orderNo);
|
||||
|
||||
Taro.showToast({
|
||||
title: '支付成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
callback?.onSuccess?.();
|
||||
|
||||
// 跳转到订单页面
|
||||
setTimeout(() => {
|
||||
Taro.navigateTo({ url: '/user/order/order' });
|
||||
}, 2000);
|
||||
} else {
|
||||
throw new Error('支付未完成');
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('支付失败:', error);
|
||||
|
||||
// 获取详细错误信息
|
||||
const errorMessage = this.getErrorMessage(error);
|
||||
|
||||
Taro.showToast({
|
||||
title: errorMessage,
|
||||
icon: 'error'
|
||||
});
|
||||
|
||||
// 标记错误已处理,避免上层重复处理
|
||||
error.handled = true;
|
||||
callback?.onError?.(errorMessage);
|
||||
|
||||
// 重新抛出错误,让上层知道支付失败
|
||||
throw error;
|
||||
} finally {
|
||||
Taro.hideLoading();
|
||||
callback?.onComplete?.();
|
||||
}
|
||||
}
|
||||
|
||||
private static parseLngLat(raw: string | undefined): { lng: number; lat: number } | null {
|
||||
const text = (raw || '').trim();
|
||||
if (!text) return null;
|
||||
const parts = text.split(/[,\s]+/).filter(Boolean);
|
||||
if (parts.length < 2) return null;
|
||||
const a = parseFloat(parts[0]);
|
||||
const b = parseFloat(parts[1]);
|
||||
if (Number.isNaN(a) || Number.isNaN(b)) return null;
|
||||
const looksLikeLngLat = Math.abs(a) <= 180 && Math.abs(b) <= 90;
|
||||
const looksLikeLatLng = Math.abs(a) <= 90 && Math.abs(b) <= 180;
|
||||
if (looksLikeLngLat) return { lng: a, lat: b };
|
||||
if (looksLikeLatLng) return { lng: b, lat: a };
|
||||
return null;
|
||||
}
|
||||
|
||||
private static distanceMeters(a: { lng: number; lat: number }, b: { lng: number; lat: number }) {
|
||||
const toRad = (x: number) => (x * Math.PI) / 180;
|
||||
const R = 6371000;
|
||||
const dLat = toRad(b.lat - a.lat);
|
||||
const dLng = toRad(b.lng - a.lng);
|
||||
const lat1 = toRad(a.lat);
|
||||
const lat2 = toRad(b.lat);
|
||||
const sin1 = Math.sin(dLat / 2);
|
||||
const sin2 = Math.sin(dLng / 2);
|
||||
const h = sin1 * sin1 + Math.cos(lat1) * Math.cos(lat2) * sin2 * sin2;
|
||||
return 2 * R * Math.asin(Math.min(1, Math.sqrt(h)));
|
||||
}
|
||||
|
||||
private static async getRidersForStore(storeId: number): Promise<ShopStoreRider[]> {
|
||||
const cached = this.storeRidersCache.get(storeId);
|
||||
if (cached) return cached;
|
||||
|
||||
// 后端字段可能叫 dealerId 或 storeId,这里都带上,服务端忽略未知字段即可。
|
||||
// 这里做一次路径兼容(camel vs kebab),避免接口路径不一致导致整单失败。
|
||||
const list = await this.listByCompatEndpoint<ShopStoreRider>(
|
||||
['/shop/shop-store-rider'],
|
||||
{
|
||||
storeId: storeId,
|
||||
status: 1
|
||||
}
|
||||
);
|
||||
|
||||
const usable = (list || []).filter(r => r?.isDelete !== 1 && (r.status === undefined || r.status === 1));
|
||||
this.storeRidersCache.set(storeId, usable);
|
||||
return usable;
|
||||
}
|
||||
|
||||
private static async pickRiderUserIdForStore(storeId: number): Promise<number | undefined> {
|
||||
const riders = await this.getRidersForStore(storeId);
|
||||
if (!riders.length) return undefined;
|
||||
|
||||
// 优先:启用 + 在线 + 自动派单,再按 dispatchPriority 由高到低
|
||||
const score = (r: ShopStoreRider) => {
|
||||
const enabled = (r.status === undefined || r.status === 1) ? 1 : 0;
|
||||
const online = r.workStatus === 1 ? 1 : 0;
|
||||
const auto = r.autoDispatchEnabled === 1 ? 1 : 0;
|
||||
const p = typeof r.dispatchPriority === 'number' ? r.dispatchPriority : 0;
|
||||
return enabled * 1000 + online * 100 + auto * 10 + p;
|
||||
};
|
||||
|
||||
const sorted = [...riders].sort((a, b) => score(b) - score(a));
|
||||
return sorted[0]?.userId;
|
||||
}
|
||||
|
||||
private static async getWarehouses(): Promise<ShopStoreWarehouse[]> {
|
||||
if (this.warehousesCache) return this.warehousesCache;
|
||||
const list = await this.listByCompatEndpoint<ShopStoreWarehouse>(
|
||||
['/shop/shop-store-warehouse'],
|
||||
{}
|
||||
);
|
||||
const usable = (list || []).filter(w => w?.isDelete !== 1 && (w.status === undefined || w.status === 1));
|
||||
this.warehousesCache = usable;
|
||||
return usable;
|
||||
}
|
||||
|
||||
private static async pickWarehouseIdForStore(storeId: number): Promise<number | undefined> {
|
||||
const store = getSelectedStoreFromStorage();
|
||||
if (!store?.id || store.id !== storeId) return undefined;
|
||||
|
||||
// 一门店一默认仓库:优先使用门店自带的 warehouseId
|
||||
if (store.warehouseId) return store.warehouseId;
|
||||
|
||||
const storeCoords = this.parseLngLat(store.lngAndLat || store.location);
|
||||
if (!storeCoords) return undefined;
|
||||
|
||||
const warehouses = await this.getWarehouses();
|
||||
if (!warehouses.length) return undefined;
|
||||
|
||||
// 优先选择“门店仓”,否则选最近的任意仓库
|
||||
const candidates = warehouses.filter(w => w.type?.includes('门店') || w.type?.includes('门店仓'));
|
||||
const list = candidates.length ? candidates : warehouses;
|
||||
|
||||
const withDistance = list
|
||||
.map(w => {
|
||||
const coords = this.parseLngLat(w.lngAndLat);
|
||||
if (!coords) return { w, d: Number.POSITIVE_INFINITY };
|
||||
return { w, d: this.distanceMeters(storeCoords, coords) };
|
||||
})
|
||||
.sort((a, b) => a.d - b.d);
|
||||
|
||||
return withDistance[0]?.w?.id;
|
||||
}
|
||||
|
||||
private static async listByCompatEndpoint<T>(
|
||||
urls: string[],
|
||||
params: Record<string, any>
|
||||
): Promise<T[]> {
|
||||
for (const url of urls) {
|
||||
try {
|
||||
const res: any = await (request as any).get(url, params, { showError: false });
|
||||
if (res?.code === 0 && Array.isArray(res?.data)) {
|
||||
return res.data as T[];
|
||||
}
|
||||
} catch (_e) {
|
||||
// try next
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理微信支付
|
||||
*/
|
||||
private static async handleWechatPay(result: WxPayResult): Promise<void> {
|
||||
console.log('处理微信支付:', result);
|
||||
|
||||
if (!result) {
|
||||
throw new Error('微信支付参数错误');
|
||||
}
|
||||
|
||||
// 验证微信支付必要参数
|
||||
if (!result.timeStamp || !result.nonceStr || !result.package || !result.paySign) {
|
||||
throw new Error('微信支付参数不完整');
|
||||
}
|
||||
|
||||
try {
|
||||
await Taro.requestPayment({
|
||||
timeStamp: result.timeStamp,
|
||||
nonceStr: result.nonceStr,
|
||||
package: result.package,
|
||||
signType: result.signType as any, // 类型转换,因为微信支付的signType是字符串
|
||||
paySign: result.paySign,
|
||||
});
|
||||
|
||||
console.log('微信支付成功');
|
||||
} catch (payError: any) {
|
||||
console.error('微信支付失败:', payError);
|
||||
|
||||
// 处理微信支付特定错误
|
||||
if (payError.errMsg) {
|
||||
if (payError.errMsg.includes('cancel')) {
|
||||
throw new Error('用户取消支付');
|
||||
} else if (payError.errMsg.includes('fail')) {
|
||||
throw new Error('微信支付失败,请重试');
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('微信支付失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理余额支付
|
||||
*/
|
||||
private static async handleBalancePay(result: any): Promise<boolean> {
|
||||
console.log('处理余额支付:', result);
|
||||
|
||||
if (!result || !result.orderNo) {
|
||||
throw new Error('余额支付参数错误');
|
||||
}
|
||||
|
||||
// 检查支付状态 - 根据后端返回的字段调整
|
||||
if (result.payStatus === false || result.payStatus === 0 || result.payStatus === '0') {
|
||||
throw new Error('余额不足或支付失败');
|
||||
}
|
||||
|
||||
// 检查订单状态 - 1表示已付款
|
||||
if (result.orderStatus !== undefined && result.orderStatus !== 1) {
|
||||
throw new Error('订单状态异常,支付可能未成功');
|
||||
}
|
||||
|
||||
// 验证实际扣款金额
|
||||
if (result.payPrice !== undefined) {
|
||||
const payPrice = parseFloat(result.payPrice);
|
||||
if (payPrice <= 0) {
|
||||
throw new Error('支付金额异常');
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有错误信息字段,检查是否有错误
|
||||
if (result.error || result.errorMsg) {
|
||||
throw new Error(result.error || result.errorMsg);
|
||||
}
|
||||
|
||||
console.log('余额支付验证通过');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理支付宝支付
|
||||
*/
|
||||
private static async handleAlipay(_result: any): Promise<void> {
|
||||
// 支付宝支付逻辑,根据实际情况实现
|
||||
throw new Error('支付宝支付暂未实现');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详细错误信息
|
||||
*/
|
||||
private static getErrorMessage(error: any): string {
|
||||
if (!error.message) {
|
||||
return '支付失败,请重试';
|
||||
}
|
||||
|
||||
const message = error.message;
|
||||
|
||||
// 配送范围/电子围栏相关错误(优先于“地址信息有误”的兜底)
|
||||
if (
|
||||
message.includes('不在配送范围') ||
|
||||
message.includes('配送范围') ||
|
||||
message.includes('电子围栏') ||
|
||||
message.includes('围栏')
|
||||
) {
|
||||
// Toast 文案尽量短(小程序 showToast 标题长度有限),更详细的引导可在业务页面用 Modal 呈现。
|
||||
return '暂不支持配送';
|
||||
}
|
||||
|
||||
// 余额相关错误
|
||||
if (message.includes('余额不足') || message.includes('balance')) {
|
||||
return '账户余额不足,请充值后重试';
|
||||
}
|
||||
|
||||
// 优惠券相关错误
|
||||
if (message.includes('优惠券') || message.includes('coupon')) {
|
||||
return '优惠券使用失败,请重新选择';
|
||||
}
|
||||
|
||||
// 库存相关错误
|
||||
if (message.includes('库存') || message.includes('stock')) {
|
||||
return '商品库存不足,请减少购买数量';
|
||||
}
|
||||
|
||||
// 地址相关错误
|
||||
if (message.includes('地址') || message.includes('address')) {
|
||||
return '收货地址信息有误,请重新选择';
|
||||
}
|
||||
|
||||
// 订单相关错误
|
||||
if (message.includes('订单') || message.includes('order')) {
|
||||
return '订单创建失败,请重试';
|
||||
}
|
||||
|
||||
// 网络相关错误
|
||||
if (message.includes('网络') || message.includes('network') || message.includes('timeout')) {
|
||||
return '网络连接异常,请检查网络后重试';
|
||||
}
|
||||
|
||||
// 微信支付相关错误
|
||||
if (message.includes('微信') || message.includes('wechat') || message.includes('wx')) {
|
||||
return '微信支付失败,请重试';
|
||||
}
|
||||
|
||||
// 返回原始错误信息
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷支付方法
|
||||
*/
|
||||
export const quickPay = {
|
||||
/**
|
||||
* 微信支付
|
||||
*/
|
||||
wechat: (orderData: OrderCreateRequest, callback?: PaymentCallback) => {
|
||||
return PaymentHandler.pay(orderData, PaymentType.WECHAT, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 余额支付
|
||||
*/
|
||||
balance: (orderData: OrderCreateRequest, callback?: PaymentCallback) => {
|
||||
return PaymentHandler.pay(orderData, PaymentType.BALANCE, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 支付宝支付
|
||||
*/
|
||||
alipay: (orderData: OrderCreateRequest, callback?: PaymentCallback) => {
|
||||
return PaymentHandler.pay(orderData, PaymentType.ALIPAY, callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建单商品订单数据
|
||||
*/
|
||||
export function buildSingleGoodsOrder(
|
||||
goodsId: number,
|
||||
quantity: number = 1,
|
||||
addressId?: number,
|
||||
options?: {
|
||||
comments?: string;
|
||||
deliveryType?: number;
|
||||
couponId?: any;
|
||||
selfTakeMerchantId?: number;
|
||||
skuId?: number;
|
||||
specInfo?: string;
|
||||
buyerRemarks?: string;
|
||||
sendStartTime?: string;
|
||||
}
|
||||
): OrderCreateRequest {
|
||||
return {
|
||||
goodsItems: [
|
||||
{
|
||||
goodsId,
|
||||
quantity,
|
||||
skuId: options?.skuId,
|
||||
specInfo: options?.specInfo
|
||||
}
|
||||
],
|
||||
addressId,
|
||||
payType: PaymentType.WECHAT, // 默认微信支付,会被PaymentHandler覆盖
|
||||
comments: options?.buyerRemarks || options?.comments || '',
|
||||
sendStartTime: options?.sendStartTime,
|
||||
deliveryType: options?.deliveryType || 0,
|
||||
couponId: options?.couponId,
|
||||
selfTakeMerchantId: options?.selfTakeMerchantId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建购物车订单数据
|
||||
*/
|
||||
export function buildCartOrder(
|
||||
cartItems: Array<{ goodsId: number; quantity: number }>,
|
||||
addressId?: number,
|
||||
options?: {
|
||||
comments?: string;
|
||||
deliveryType?: number;
|
||||
couponId?: number;
|
||||
selfTakeMerchantId?: number;
|
||||
}
|
||||
): OrderCreateRequest {
|
||||
return {
|
||||
goodsItems: cartItems.map(item => ({
|
||||
goodsId: item.goodsId,
|
||||
quantity: item.quantity
|
||||
})),
|
||||
addressId,
|
||||
payType: PaymentType.WECHAT, // 默认微信支付,会被PaymentHandler覆盖
|
||||
comments: options?.comments || '购物车下单',
|
||||
deliveryType: options?.deliveryType || 0,
|
||||
couponId: options?.couponId,
|
||||
selfTakeMerchantId: options?.selfTakeMerchantId
|
||||
};
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import {User} from "@/api/system/user/model";
|
||||
export const TEMPLATE_ID = '10579';
|
||||
// 服务接口 - 请根据实际情况修改
|
||||
export const SERVER_API_URL = 'https://server.websoft.top/api';
|
||||
// export const SERVER_API_URL = 'http://127.0.0.1:8000/api';
|
||||
// export const SERVER_API_URL = 'http://127.0.0.1:9200/api';
|
||||
/**
|
||||
* 保存用户信息到本地存储
|
||||
* @param token
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import Taro from '@tarojs/taro';
|
||||
import type { ShopStore } from '@/api/shop/shopStore/model';
|
||||
|
||||
export const SELECTED_STORE_STORAGE_KEY = 'SelectedStore';
|
||||
|
||||
export function getSelectedStoreFromStorage(): ShopStore | null {
|
||||
try {
|
||||
const raw = Taro.getStorageSync(SELECTED_STORE_STORAGE_KEY);
|
||||
if (!raw) return null;
|
||||
return (typeof raw === 'string' ? JSON.parse(raw) : raw) as ShopStore;
|
||||
} catch (_e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveSelectedStoreToStorage(store: ShopStore | null) {
|
||||
if (!store) {
|
||||
Taro.removeStorageSync(SELECTED_STORE_STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
Taro.setStorageSync(SELECTED_STORE_STORAGE_KEY, store);
|
||||
}
|
||||
|
||||
export function getSelectedStoreIdFromStorage(): number | undefined {
|
||||
return getSelectedStoreFromStorage()?.id;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user