@pisell/pisellos
Version:
一个可扩展的前端模块化SDK框架,支持插件系统
1,310 lines (1,309 loc) • 130 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/solution/Checkout/index.ts
var Checkout_exports = {};
__export(Checkout_exports, {
CheckoutImpl: () => CheckoutImpl
});
module.exports = __toCommonJS(Checkout_exports);
var import_BaseModule = require("../../modules/BaseModule");
var import_decimal = __toESM(require("decimal.js"));
var import_Order = require("../../modules/Order");
var import_Payment = require("../../modules/Payment");
var import_types = require("./types");
var import_types2 = require("../../modules/Payment/types");
var import_utils = require("./utils");
var import_utils2 = require("../../modules/Cart/utils");
__reExport(Checkout_exports, require("./types"), module.exports);
var CheckoutImpl = class extends import_BaseModule.BaseModule {
constructor(name, version) {
super(name || "checkout", version || "1.0.0");
this.defaultName = "checkout";
this.defaultVersion = "1.0.0";
this.isSolution = true;
this.otherParams = {};
// LoggerManager 实例
// 计算缓存(用于性能优化)
this.calculationCache = {};
// 同步订单到后端锁(按订单维度),防止短时间内重复触发导致同一订单多次同步
this.syncOrderToBackendInFlightByOrderKey = /* @__PURE__ */ new Map();
}
async initialize(core, options) {
this.core = core;
this.otherParams = options.otherParams || {};
this.request = core.getPlugin("request");
if (!this.request) {
throw new Error("Checkout 解决方案需要 request 插件支持");
}
const appPlugin = core.getPlugin("app");
if (!appPlugin) {
throw new Error("Checkout 解决方案需要 app 插件支持");
}
const app = appPlugin.getApp();
this.logger = app.logger;
this.order = new import_Order.OrderModule();
this.payment = new import_Payment.PaymentModule();
this.store = {
cartItems: [],
paymentMethods: [],
stateAmount: "0.00",
balanceDueAmount: "0.00",
cartSummary: void 0,
isOrderSynced: false,
currentCustomer: void 0
};
await this.initializeSubModules(core, options);
await this.preloadPaymentMethods();
await this.cleanupExpiredOrdersAsync();
console.log("[Checkout] 初始化完成");
await this.core.effects.emit(`${this.name}:onCheckoutInitialized`, {
timestamp: Date.now()
});
this.logInfo("CheckoutModule initialized successfully");
}
/**
* 记录信息日志
*/
logInfo(title, metadata) {
if (this.logger) {
this.logger.addLog({
type: "info",
title: `[CheckoutModule] ${this.getCurrentOrderId()} ${title}`,
metadata: metadata || {}
});
}
}
/**
* 记录警告日志
*/
logWarning(title, metadata) {
if (this.logger) {
this.logger.addLog({
type: "warning",
title: `[CheckoutModule] ${title}`,
metadata: metadata || {}
});
}
}
/**
* 记录错误日志
*/
logError(title, metadata) {
if (this.logger) {
this.logger.addLog({
type: "error",
title: `[CheckoutModule] ${title}`,
metadata: metadata || {}
});
}
}
/**
* 初始化子模块
*/
async initializeSubModules(core, options) {
var _a, _b;
core.registerModule(this.order, {
store: ((_a = options.store) == null ? void 0 : _a.order) || {},
otherParams: { ...this.otherParams, fatherModule: this.name }
});
core.registerModule(this.payment, {
store: ((_b = options.store) == null ? void 0 : _b.payment) || {},
otherParams: { ...this.otherParams, fatherModule: this.name }
});
this.setupPaymentEventListeners();
}
/**
* 设置支付模块事件监听
*/
setupPaymentEventListeners() {
this.core.effects.on("payment:onPaymentSyncSuccess", async (data) => {
var _a;
if (((_a = this.store.currentOrder) == null ? void 0 : _a.uuid) === data.orderUuid) {
await this.handlePaymentSuccess(data);
}
});
this.core.effects.on("payment:onPaymentSyncError", async (data) => {
var _a;
if (((_a = this.store.currentOrder) == null ? void 0 : _a.uuid) === data.orderUuid) {
await this.handlePaymentError(data);
}
});
this.core.effects.on(
import_types2.PaymentHooks.OnPaymentMethodsLoaded,
async (methods) => {
console.log("[Checkout] 收到支付方式加载完成事件:", methods.length);
this.store.paymentMethods = methods;
}
);
this.core.effects.on(
import_types2.PaymentHooks.OnPaymentMethodsChanged,
async (data) => {
console.log("[Checkout] 收到支付方式变更事件:", {
oldCount: data.oldMethods.length,
newCount: data.newMethods.length
});
this.store.paymentMethods = data.newMethods;
}
);
}
/**
* 初始化结账流程
*/
async initializeCheckoutAsync(params) {
var _a;
this.logInfo("initializeCheckoutAsync called", {
cartItemsCount: ((_a = params.cartItems) == null ? void 0 : _a.length) || 0,
orderType: params.orderType,
platform: params.platform
});
try {
const validation = this.validateCheckoutParams(params);
if (!validation.valid) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
`参数验证失败: ${validation.errors.join(", ")}`
);
}
this.store.cartItems = params.cartItems;
console.log("[Checkout] 结账流程初始化完成");
} catch (error) {
await this.handleError(error, import_types.CheckoutErrorType.UnknownError);
throw error;
}
}
getHolderIdFromBooking(obj) {
var _a;
if (!obj)
return void 0;
let ret = obj.holder_id || ((_a = obj.holder) == null ? void 0 : _a.form_record);
if (Array.isArray(ret)) {
return ret.length > 0 ? Number(ret[0]) : void 0;
}
return Number(ret) || void 0;
}
getProductListByOrder() {
var _a, _b, _c, _d, _e;
if (!this.store.currentOrder) {
return [];
}
const getDiscountListAmount = (list) => {
return list.reduce((pre, item) => {
return pre + (item.amount || 0);
}, 0);
};
const formatProduct = (items) => {
return items.map(
(item) => ({
product_id: item.product_id,
product_variant_id: item.product_variant_id,
quantity: item.num,
is_price_include_tax: item.is_price_include_tax,
// 商品是否含税:1;0
is_charge_tax: item.is_charge_tax ?? 0,
// 若商品不含税,计算得到的税费,单位(元)
tax_fee: item.tax_fee,
// 整个商品折扣后的总金额
selling_price: item.calculated_selling_price,
// 使用者id
holder_id: item.holder_id,
// 整个商品折扣前的总金额
original_price: item.calculated_original_price,
// 主商品折扣前金额,不包含套餐子商品
main_product_original_price: item.price,
// 主商品折扣后金额,不包含套餐子商品
main_product_selling_price: item.main_product_selling_price,
metadata: {
// 主商品+非原价(加减价)子商品税费
"main_product_attached_bundle_tax_fee": item.metadata.main_product_attached_bundle_tax_fee,
// 主商品+非原价(加减价)子商品附加费
"main_product_attached_bundle_surcharge_fee": item.metadata.main_product_attached_bundle_surcharge_fee,
// 可选,附加费均摊舍入金额
"surcharge_rounding_remainder": item.metadata.surcharge_rounding_remainder
},
product_bundle: (item.product_bundle || []).map((bundle) => {
return {
is_price_include_tax: item.is_price_include_tax,
bundle_id: bundle.bundle_id,
bundle_product_id: bundle.bundle_product_id,
bundle_variant_id: bundle.bundle_variant_id,
price_type: bundle.price_type,
price_type_ext: bundle.price_type_ext,
// 套餐子商品总价,不包含折扣金额
bundle_sum_price: bundle.bundle_sum_price,
// 套餐子商品折扣后金额
bundle_selling_price: bundle.bundle_selling_price,
num: bundle.num,
is_charge_tax: bundle.is_charge_tax,
tax_fee: bundle.tax_fee,
metadata: {
// 子商品单数量附加费
"surcharge_fee": bundle.metadata.surcharge_fee,
// 可选,附加费均摊舍入金额
"surcharge_rounding_remainder": bundle.metadata.surcharge_rounding_remainder
}
};
})
})
) || [];
};
const productList = formatProduct(
((_c = (_b = (_a = this.store.currentOrder.order_info) == null ? void 0 : _a.original_order_data) == null ? void 0 : _b.bookings) == null ? void 0 : _c.map(
(item) => {
return {
...item.product,
holder_id: this.getHolderIdFromBooking(item)
};
}
)) || []
);
const relationProducts = formatProduct(((_e = (_d = this.store.currentOrder.order_info) == null ? void 0 : _d.original_order_data) == null ? void 0 : _e.relation_products) || []);
return [...productList, ...relationProducts];
}
async initWalletData(params) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
this.logInfo("initWalletData called", {
params
});
try {
const amountInfo = (_b = (_a = this.store.currentOrder) == null ? void 0 : _a.order_info) == null ? void 0 : _b.amount_breakdown;
if (!amountInfo) {
this.logInfo("initWalletData amountInfo not found", {
params
});
return;
}
const walletBusinessData = {
customer_id: ((_c = this.store.currentCustomer) == null ? void 0 : _c.customer_id) ? Number(this.store.currentCustomer.customer_id) : void 0,
holder: (_f = (_e = (_d = this.store.currentOrder) == null ? void 0 : _d.order_info) == null ? void 0 : _e.original_order_data) == null ? void 0 : _f.holder,
amountInfo: {
totalAmount: amountInfo.totalAmount,
subTotal: amountInfo.subTotal,
depositAmount: (_g = this.store.currentOrder) == null ? void 0 : _g.deposit_amount,
isDeposit: (_h = this.store.currentOrder) == null ? void 0 : _h.is_deposit
},
products: this.getProductListByOrder(),
is_price_include_tax: this.otherParams.is_price_include_tax,
// core 有
...params
};
if (this.store.isOrderSynced) {
walletBusinessData.payment_order_id = (_i = this.store.currentOrder) == null ? void 0 : _i.order_id;
}
this.logInfo("开始拉取:initializeWalletDataFromBusinessAsync", {
walletBusinessData
});
await this.payment.wallet.initializeWalletDataFromBusinessAsync(
walletBusinessData
);
this.logInfo("调用结束:initializeWalletDataFromBusinessAsync", {
walletBusinessData
});
if (!this.store.currentOrder) {
return;
}
this.logInfo("initWalletData currentOrder found", {
currentOrder: this.store.currentOrder
});
await this.core.effects.emit(`${this.name}:onWalletDataInitialized`, {
orderUuid: (_j = this.store.currentOrder) == null ? void 0 : _j.uuid,
customerId: walletBusinessData.customer_id,
walletBusinessData: {
customer_id: walletBusinessData.customer_id,
amountInfo: walletBusinessData.amountInfo,
order_wait_pay_amount: params == null ? void 0 : params.order_wait_pay_amount
},
timestamp: Date.now()
});
} catch (error) {
this.logError("initWalletData error", {
error: error instanceof Error ? error.message : String(error)
});
throw error;
}
}
checkIsNeedDepositAsync(bookings, relationProducts) {
const summaryDeposit = {
total: 0,
protocols: [],
hasDeposit: false
};
bookings == null ? void 0 : bookings.forEach((item) => {
const total = item.product.calculated_selling_price;
const deposit = (0, import_utils2.getProductDeposit)({
cartItem: {
total
},
product: item.product,
bundle: item.bundle,
options: item.options,
num: item.num
});
item.deposit = deposit;
if ((deposit == null ? void 0 : deposit.total) && Number(deposit.total) > 0) {
summaryDeposit.protocols = summaryDeposit.protocols.concat(
deposit.protocols
);
summaryDeposit.total = new import_decimal.default(summaryDeposit.total).plus(deposit.total).toNumber();
summaryDeposit.hasDeposit = true;
}
});
relationProducts == null ? void 0 : relationProducts.forEach((item) => {
const deposit = (0, import_utils2.getProductDeposit)({
cartItem: {
total: item.price * item.num
},
product: item,
bundle: item.product_bundle,
options: item.product_option_item,
num: item.num
});
item.deposit = deposit;
if ((deposit == null ? void 0 : deposit.total) && Number(deposit.total) > 0) {
summaryDeposit.total = new import_decimal.default(summaryDeposit.total).plus(deposit.total).toNumber();
item.discount_list.forEach((discount) => {
summaryDeposit.total = new import_decimal.default(summaryDeposit.total).minus(discount.amount || 0).toNumber();
});
summaryDeposit.protocols = summaryDeposit.protocols.concat(
deposit.protocols
);
summaryDeposit.hasDeposit = true;
}
});
summaryDeposit.total = new import_decimal.default(summaryDeposit.total).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
return summaryDeposit;
}
/**
* 创建本地订单 (前端模拟下单流程)
*
* 此方法用于在前端模拟整个下单流程,创建一个本地的虚拟订单。
* 用户在购物车点击下单时,会把购物车参数传递给此方法,
* 方法会记录参数并创建本地虚拟订单,然后用 Payment 模块管理支付流程。
*/
async createLocalOrderAsync(params) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
this.logInfo("createLocalOrderAsync called", {
orderDataType: (_a = params.orderData) == null ? void 0 : _a.type,
platform: (_b = params.orderData) == null ? void 0 : _b.platform,
bookingsCount: ((_d = (_c = params.orderData) == null ? void 0 : _c.bookings) == null ? void 0 : _d.length) || 0,
relationProductsCount: ((_f = (_e = params.orderData) == null ? void 0 : _e.relation_products) == null ? void 0 : _f.length) || 0,
customerId: (_g = params.orderData) == null ? void 0 : _g.customer_id,
autoPayment: params.autoPayment,
cartSummaryCount: ((_h = params.cartSummary) == null ? void 0 : _h.length) || 0,
totalInfoKeys: params.totalInfo ? Object.keys(params.totalInfo) : []
});
try {
this.resetStoreState();
const validation = (0, import_utils.validateLocalOrderData)(params.orderData);
if (!validation.valid) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
`订单数据验证失败: ${validation.errors.join(", ")}`
);
}
const localOrderId = (0, import_utils.generateLocalOrderId)();
const amountInfo = (0, import_utils.extractAmountFromCartSummary)(params.cartSummary);
params.orderData.platform = "pos";
params.orderData.created_at = (0, import_utils.formatDateTime)(/* @__PURE__ */ new Date());
params.orderData.surcharge_fee = (_j = (_i = params.totalInfo) == null ? void 0 : _i.total) == null ? void 0 : _j.otherAmount;
params.orderData.surcharges = (_l = (_k = params.totalInfo) == null ? void 0 : _k.total) == null ? void 0 : _l.surcharge;
params.orderData.shop_discount = (_n = (_m = params.totalInfo) == null ? void 0 : _m.total) == null ? void 0 : _n.shopDiscount;
params.orderData.tax_fee = (_p = (_o = params.totalInfo) == null ? void 0 : _o.total) == null ? void 0 : _p.tax;
this.store.localOrderData = params.orderData;
this.store.cartSummary = params.cartSummary;
const customerInfo = {
customer_id: params.orderData.customer_id,
customer_name: params.orderData.customer_name
};
if (customerInfo.customer_id || customerInfo.customer_name) {
this.store.currentCustomer = customerInfo;
console.log("[Checkout] 保存客户信息:", customerInfo);
} else {
this.store.currentCustomer = void 0;
console.log("[Checkout] 未提供客户信息");
}
const isNeedDeposit = this.checkIsNeedDepositAsync(
params.orderData.bookings,
params.orderData.relation_products
);
const paymentOrder = await this.payment.createPaymentOrderAsync({
order_id: localOrderId,
total_amount: amountInfo.totalAmount,
is_deposit: isNeedDeposit.hasDeposit ? 1 : 0,
deposit_amount: isNeedDeposit.total ? isNeedDeposit.total.toString() : "0.00",
order_info: {
original_order_data: params.orderData,
cart_summary: params.cartSummary,
created_at: (/* @__PURE__ */ new Date()).toISOString(),
platform: params.orderData.platform,
type: params.orderData.type,
schedule_date: params.orderData.schedule_date,
shop_note: params.orderData.shop_note,
amount_breakdown: amountInfo
}
});
this.store.currentOrder = paymentOrder;
this.core.effects.emit(`${this.name}:onOrderCreated`, {
order: paymentOrder,
timestamp: Date.now()
});
const updateAmountStartTime = Date.now();
await this.updateStateAmountToRemaining(false);
const updateAmountDuration = Date.now() - updateAmountStartTime;
this.logInfo("updateStateAmountToRemaining operation completed", {
operation: "updateStateAmountToRemaining",
orderUuid: paymentOrder.uuid,
orderId: paymentOrder.order_id,
duration: `${updateAmountDuration}ms`,
performance: updateAmountDuration > 200 ? "slow" : updateAmountDuration > 100 ? "medium" : "fast"
});
this.logInfo("本地订单创建成功:", {
localOrderId,
uuid: paymentOrder.uuid,
totalAmount: amountInfo.totalAmount,
subTotal: amountInfo.subTotal,
taxAmount: amountInfo.taxAmount,
stateAmount: this.store.stateAmount
});
this.initWalletData();
return paymentOrder;
} catch (error) {
await this.handleError(
error,
import_types.CheckoutErrorType.OrderCreationFailed
);
this.logError("本地订单创建失败:", error);
this.core.effects.emit(`${this.name}:onOrderCreationFailed`, {
error: this.store.lastError,
timestamp: Date.now()
});
throw error;
}
}
/**
* 更新本地订单(已同步后端的订单)并设置为当前订单
*
* 通过传入真实的 orderId,对已缓存的订单数据进行更新,
* 会覆盖 order_info 与金额等字段,并重新计算待付金额,最后设置为 currentOrder。
*/
async updateLocalOrderAsync(params) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
this.payment.wallet.clearAllCache();
this.logInfo("updateLocalOrderAsync called", {
orderId: params.orderId,
orderDataType: (_a = params.orderData) == null ? void 0 : _a.type,
bookingsCount: ((_c = (_b = params.orderData) == null ? void 0 : _b.bookings) == null ? void 0 : _c.length) || 0,
cartSummaryCount: ((_d = params.cartSummary) == null ? void 0 : _d.length) || 0,
totalInfoKeys: params.totalInfo ? Object.keys(params.totalInfo) : []
});
const validation = (0, import_utils.validateLocalOrderData)(params.orderData);
if (!validation.valid) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
`订单数据验证失败: ${validation.errors.join(", ")}`
);
}
const amountInfo = (0, import_utils.extractAmountFromCartSummary)(params.cartSummary);
params.orderData.created_at = (0, import_utils.formatDateTime)(/* @__PURE__ */ new Date());
params.orderData.platform = "pos";
params.orderData.surcharge_fee = (_f = (_e = params.totalInfo) == null ? void 0 : _e.total) == null ? void 0 : _f.otherAmount;
params.orderData.surcharges = (_h = (_g = params.totalInfo) == null ? void 0 : _g.total) == null ? void 0 : _h.surcharge;
params.orderData.shop_discount = (_j = (_i = params.totalInfo) == null ? void 0 : _i.total) == null ? void 0 : _j.shopDiscount;
params.orderData.tax_fee = (_l = (_k = params.totalInfo) == null ? void 0 : _k.total) == null ? void 0 : _l.tax;
this.store.localOrderData = params.orderData;
this.store.cartSummary = params.cartSummary;
const customerInfo = {
customer_id: params.orderData.customer_id,
customer_name: params.orderData.customer_name
};
if (customerInfo.customer_id || customerInfo.customer_name) {
this.store.currentCustomer = customerInfo;
} else {
this.store.currentCustomer = void 0;
}
const allOrders = await this.payment.getOrderListAsync();
const existingOrder = allOrders.find(
(o) => String(o.id) === String(params.orderId) || String(o.order_id) === String(params.orderId)
);
if (existingOrder) {
if (params.existPayment && params.existPayment.length > 0) {
this.logInfo("检测到云端支付项,将替换本地订单的支付项列表", {
orderId: params.orderId,
existingPaymentCount: ((_m = existingOrder.payment) == null ? void 0 : _m.length) || 0,
newPaymentCount: params.existPayment.length
});
this.store.isOrderSynced = true;
const currentTime = (0, import_utils.formatDateTime)(/* @__PURE__ */ new Date());
const formattedExistPayments = params.existPayment.map((payment) => ({
...payment,
isSynced: true,
// 标记为已同步
status: payment.status || "active",
created_at: payment.created_at || currentTime,
updated_at: payment.updated_at || currentTime
}));
const isNeedDeposit3 = this.checkIsNeedDepositAsync(
params.orderData.bookings,
params.orderData.relation_products
);
await this.payment.updateOrderAsync(existingOrder.uuid, {
total_amount: amountInfo.totalAmount,
is_deposit: isNeedDeposit3.hasDeposit ? 1 : 0,
deposit_amount: isNeedDeposit3.total ? isNeedDeposit3.total.toString() : "0.00",
payment: formattedExistPayments,
// 🔧 替换支付项列表
order_info: {
original_order_data: params.orderData,
cart_summary: params.cartSummary,
created_at: (/* @__PURE__ */ new Date()).toISOString(),
platform: params.orderData.platform,
type: params.orderData.type,
schedule_date: params.orderData.schedule_date,
shop_note: params.orderData.shop_note,
amount_breakdown: amountInfo
}
});
const updated2 = await this.payment.getPaymentOrderByUuidAsync(
existingOrder.uuid
);
if (!updated2)
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.UnknownError,
"订单更新失败"
);
this.store.currentOrder = updated2;
await this.updateStateAmountToRemaining(false);
this.logInfo("本地订单更新成功(已替换云端支付项):", {
orderId: params.orderId,
uuid: updated2.uuid,
payments: ((_n = updated2.payment) == null ? void 0 : _n.length) || 0,
totalAmount: updated2.total_amount,
expectAmount: updated2.expect_amount,
isOrderSynced: true
});
await this.core.effects.emit(`${this.name}:onOrderCreated`, {
order: updated2,
timestamp: Date.now()
});
const syncedPayments = updated2.payment.filter((p) => p.isSynced && p.status !== "voided");
const syncedAmount2 = syncedPayments.reduce((sum, p) => {
let amt = new import_decimal.default(p.amount || "0");
const rounding = new import_decimal.default(Number(p.rounding_amount) > 0 ? 0 : p.rounding_amount || "0").abs();
return sum.plus(amt).plus(rounding);
}, new import_decimal.default(0));
const totalAmount2 = new import_decimal.default(amountInfo.totalAmount || "0");
const remainingExpectAmount2 = import_decimal.default.max(0, totalAmount2.minus(syncedAmount2)).toFixed(2);
let depositDiffAmount2 = new import_decimal.default(0).toFixed(2);
if (updated2.is_deposit === 1) {
depositDiffAmount2 = new import_decimal.default(updated2.deposit_amount).minus(syncedAmount2).toFixed(2);
}
this.initWalletData({
order_wait_pay_amount: Number(depositDiffAmount2) > 0 ? Number(depositDiffAmount2) : Number(remainingExpectAmount2)
});
return updated2;
}
const totalAmount = new import_decimal.default(amountInfo.totalAmount || "0");
const activePayments = (existingOrder.payment || []).filter(
(p) => p.status !== "voided" && (!p.voucher_id || p.voucher_id && p.isSynced)
);
const paidAmount = activePayments.reduce((sum, p) => {
const amt = new import_decimal.default(p.amount || "0");
const rounding = new import_decimal.default(Number(p.rounding_amount) > 0 ? 0 : p.rounding_amount || "0").abs();
return sum.plus(amt).plus(rounding);
}, new import_decimal.default(0));
const remaining = import_decimal.default.max(0, totalAmount.minus(paidAmount)).toFixed(
2
);
const isNeedDeposit2 = this.checkIsNeedDepositAsync(
params.orderData.bookings,
params.orderData.relation_products
);
await this.payment.updateOrderAsync(existingOrder.uuid, {
total_amount: amountInfo.totalAmount,
is_deposit: isNeedDeposit2.hasDeposit ? 1 : 0,
deposit_amount: isNeedDeposit2.total ? isNeedDeposit2.total.toString() : "0.00",
expect_amount: remaining,
order_info: {
original_order_data: params.orderData,
cart_summary: params.cartSummary,
created_at: (/* @__PURE__ */ new Date()).toISOString(),
platform: params.orderData.platform,
type: params.orderData.type,
schedule_date: params.orderData.schedule_date,
shop_note: params.orderData.shop_note,
amount_breakdown: amountInfo
}
});
const updated = await this.payment.getPaymentOrderByUuidAsync(
existingOrder.uuid
);
if (!updated)
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.UnknownError,
"订单更新失败"
);
this.store.currentOrder = updated;
await this.updateStateAmountToRemaining(false);
this.logInfo("本地订单更新成功(保留支付项):", {
orderId: params.orderId,
uuid: updated.uuid,
payments: ((_o = updated.payment) == null ? void 0 : _o.length) || 0,
totalAmount: updated.total_amount,
expectAmount: updated.expect_amount
});
await this.core.effects.emit(`${this.name}:onOrderCreated`, {
order: updated,
timestamp: Date.now()
});
const syncedAmount = activePayments.reduce((sum, p) => {
const amt = new import_decimal.default(p.amount || "0");
const rounding = new import_decimal.default(Number(p.rounding_amount) > 0 ? 0 : p.rounding_amount || "0").abs();
return sum.plus(amt).plus(rounding);
}, new import_decimal.default(0));
const remainingExpectAmount = import_decimal.default.max(0, totalAmount.minus(syncedAmount)).toFixed(
2
);
let depositDiffAmount = new import_decimal.default(0).toFixed(2);
if (updated.is_deposit === 1) {
depositDiffAmount = new import_decimal.default(updated.deposit_amount).minus(syncedAmount).toFixed(2);
}
this.initWalletData({
order_wait_pay_amount: Number(depositDiffAmount) > 0 ? Number(depositDiffAmount) : Number(remainingExpectAmount)
});
return updated;
}
const isNeedDeposit = this.checkIsNeedDepositAsync(
params.orderData.bookings,
params.orderData.relation_products
);
const created = await this.payment.createPaymentOrderAsync({
order_id: String(params.orderId),
total_amount: amountInfo.totalAmount,
is_deposit: isNeedDeposit.hasDeposit ? 1 : 0,
deposit_amount: isNeedDeposit.total ? isNeedDeposit.total.toString() : "0.00",
existPayment: params.existPayment,
// 🔧 新增: 传递云端支付项
order_info: {
original_order_data: params.orderData,
cart_summary: params.cartSummary,
created_at: (/* @__PURE__ */ new Date()).toISOString(),
platform: params.orderData.platform,
type: params.orderData.type,
schedule_date: params.orderData.schedule_date,
shop_note: params.orderData.shop_note,
amount_breakdown: amountInfo
}
});
this.store.currentOrder = created;
if (params.existPayment && params.existPayment.length > 0 || ((_p = this.store.currentOrder) == null ? void 0 : _p.order_id) && !(0, import_utils.isVirtualOrderId)((_q = this.store.currentOrder) == null ? void 0 : _q.order_id)) {
this.store.isOrderSynced = true;
const syncedPayments = created.payment.filter((p) => p.isSynced && p.status !== "voided");
const syncedAmount = syncedPayments.reduce((sum, p) => {
const amt = new import_decimal.default(p.amount || "0");
const rounding = new import_decimal.default(Number(p.rounding_amount) > 0 ? 0 : p.rounding_amount || "0").abs();
return sum.plus(amt).plus(rounding);
}, new import_decimal.default(0));
this.logInfo("云端订单初始化钱包数据", {
orderId: params.orderId,
totalAmount: created.total_amount,
expectAmount: created.expect_amount,
syncedAmount: syncedAmount.toFixed(2),
syncedPaymentCount: syncedPayments.length,
isOrderSynced: true
});
this.initWalletData({
order_wait_pay_amount: Number(created.expect_amount)
});
} else {
this.initWalletData();
}
return created;
}
/**
* 完成结账
*/
async completeCheckoutAsync() {
try {
if (!this.store.currentOrder) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
"未找到当前订单"
);
}
const order = await this.payment.getPaymentOrderByUuidAsync(
this.store.currentOrder.uuid
);
if (!order || order.payment_status !== import_types2.PaymentStatus.Finished) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
"订单支付未完成,无法完成结账"
);
}
this.payment.wallet.clearAllCache();
await this.core.effects.emit(`${this.name}:onCheckoutCompleted`, {
orderId: order.id,
timestamp: Date.now()
});
console.log("[Checkout] 结账流程完成:", order.id);
this.resetStoreState();
return {
success: true,
orderId: String(order.id)
};
} catch (error) {
await this.handleError(error, import_types.CheckoutErrorType.UnknownError);
return { success: false };
}
}
/**
* 获取订单原始数据
*/
getOrderOriginalData() {
var _a;
return (_a = this.store.currentOrder) == null ? void 0 : _a.order_info;
}
/**
* 获取当前订单基础信息
*
* 返回当前订单的基础信息,包括订单状态、金额、支付状态等
*/
getCurrentOrderInfo() {
try {
const { currentOrder, cartItems, localOrderData } = this.store;
if (!currentOrder) {
console.log("[Checkout] 当前没有活跃订单");
return null;
}
const orderInfo = currentOrder.order_info || {};
const createdAt = orderInfo.created_at || (/* @__PURE__ */ new Date()).toISOString();
const orderType = (localOrderData == null ? void 0 : localOrderData.type) || orderInfo.type || "virtual";
const platform = (localOrderData == null ? void 0 : localOrderData.platform) || orderInfo.platform || "pc";
const result = {
uuid: currentOrder.uuid,
orderId: currentOrder.order_id,
totalAmount: currentOrder.total_amount,
remainingAmount: currentOrder.expect_amount,
paymentStatus: currentOrder.payment_status,
isDeposit: Boolean(currentOrder.is_deposit),
depositAmount: currentOrder.deposit_amount,
orderType,
platform,
createdAt,
itemCount: cartItems.length,
hasLocalOrderData: Boolean(localOrderData)
};
return result;
} catch (error) {
console.error("[Checkout] 获取当前订单基础信息失败:", error);
return null;
}
}
/**
* 获取当前订单的支付项
*
* 返回当前订单的所有支付项,包括活跃和已撤销的支付项
*/
async getCurrentOrderPaymentItemsAsync() {
try {
const { currentOrder } = this.store;
if (!currentOrder) {
console.log("[Checkout] 当前没有活跃订单,无法获取支付项");
return [];
}
const paymentItems = await this.payment.getPaymentItemsAsync(
currentOrder.uuid
);
return paymentItems;
} catch (error) {
console.error("[Checkout] 获取当前订单支付项失败:", error);
return [];
}
}
/**
* 获取订单模块
*/
getOrderModule() {
return this.order;
}
/**
* 获取支付模块
*/
getPaymentModule() {
return this.payment;
}
/**
* 替换本地订单ID为真实订单ID
*
* 当后端订单创建完成后,调用此方法将本地虚拟订单ID替换为真实订单ID
*
* @param newOrderId 后端返回的真实订单ID
* @returns 更新后的订单对象,如果当前没有订单则返回null
*/
async replaceLocalOrderIdAsync(newOrderId) {
try {
if (!this.store.currentOrder) {
console.warn("[Checkout] 没有当前订单,无法替换订单ID");
return null;
}
const previousOrder = this.store.currentOrder;
const latestPaymentStatus = previousOrder.payment_status;
const updatedOrder = await this.payment.replaceOrderIdByUuidAsync(
previousOrder.uuid,
newOrderId
);
if (updatedOrder) {
this.store.currentOrder = {
...previousOrder,
...updatedOrder,
payment_status: latestPaymentStatus
};
if (latestPaymentStatus)
await this.payment.updateOrderAsync(previousOrder.uuid, {
payment_status: latestPaymentStatus
});
await this.core.effects.emit(`${this.name}:onOrderCreated`, {
order: updatedOrder,
timestamp: Date.now()
});
console.log("[Checkout] 本地订单ID替换成功:", {
uuid: updatedOrder.uuid,
newOrderId: updatedOrder.id
});
this.store.isOrderSynced = true;
await this.updateStateAmountToRemaining(false);
}
return updatedOrder;
} catch (error) {
console.error("[Checkout] 替换订单ID失败:", error);
await this.handleError(error, import_types.CheckoutErrorType.UnknownError);
return null;
}
}
/**
* 设置自定义支付金额
*
* 允许UI自定义本次支付的金额,通常用于部分支付或调整支付金额场景
*
* @param amount 自定义支付金额,必须是有效的数字字符串
*/
async setStateAmountAsync(amount) {
try {
const numAmount = parseFloat(amount);
if (isNaN(numAmount) || numAmount < 0) {
throw new Error(`无效的支付金额: ${amount}`);
}
const oldAmount = this.store.stateAmount;
const formattedAmount = numAmount.toFixed(2);
if (oldAmount === formattedAmount) {
return;
}
this.logInfo("[Checkout] 设置自定义支付金额:", {
oldAmount,
newAmount: formattedAmount
});
this.store.stateAmount = formattedAmount;
await this.core.effects.emit(`${this.name}:onStateAmountChanged`, {
oldAmount,
newAmount: formattedAmount,
timestamp: Date.now()
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "设置支付金额失败";
console.error("[Checkout] 设置自定义支付金额失败:", errorMessage);
await this.handleError(
new Error(errorMessage),
import_types.CheckoutErrorType.ValidationFailed
);
throw error;
}
}
/**
* 获取当前自定义支付金额
*
* @returns 当前设置的自定义支付金额
*/
getStateAmount() {
return this.store.stateAmount;
}
/**
* 获取系统计算的待付金额(只读)
*
* 此方法返回系统内部计算的实际待付金额,不允许外部修改
*
* @returns 当前系统计算的待付金额
*/
getBalanceDueAmount() {
return this.store.balanceDueAmount;
}
/**
* 获取购物车小计数据
*
* 返回当前结账流程中的购物车小计项数据,包含各种费用明细
*
* @returns 购物车小计数据数组,如果没有则返回 null
*/
getCartSummary() {
if (!this.store.cartSummary || this.store.cartSummary.length === 0) {
console.warn("[Checkout] 没有可用的购物车小计数据");
return null;
}
this.logInfo("获取购物车小计数据:", {
itemCount: this.store.cartSummary.length,
items: this.store.cartSummary.map((item) => ({
key: item.key,
value: item.value,
label: item.label
}))
});
return [...this.store.cartSummary];
}
/**
* 获取支付方式列表
*
* 优先使用 store 中的缓存数据,避免重复接口调用。
* 如果 store 中没有数据,则调用 Payment 模块的方法获取。
*
* @returns 支付方式列表
*/
async getPaymentMethodsAsync() {
if (this.store.paymentMethods && this.store.paymentMethods.length > 0) {
this.logInfo(
`使用缓存的支付方式数据,共 ${this.store.paymentMethods.length} 种`
);
return this.store.paymentMethods;
}
this.logInfo("store 中无缓存,从 Payment 模块获取支付方式...");
try {
const methods = await this.payment.getPayMethodListAsync();
this.store.paymentMethods = methods;
this.logInfo(
`从 Payment 模块获取到 ${methods.length} 种支付方式,已更新缓存`
);
return methods;
} catch (error) {
this.logError("获取支付方式失败:", error);
return [];
}
}
/**
* 为当前订单添加支付项
*
* 向当前活跃订单添加一个支付项,支付项包含支付方式信息和金额
*
* @param paymentItem 支付项数据
* @throws 当前没有活跃订单时抛出错误
*/
async addPaymentItemAsync(paymentItem, orderUuid) {
var _a, _b, _c, _d, _e;
this.logInfo("addPaymentItemAsync called", { paymentItem, orderUuid });
const isEftposPayment = ((_a = paymentItem.type) == null ? void 0 : _a.toLowerCase()) === "eftpos" || ((_b = paymentItem.code) == null ? void 0 : _b.toUpperCase().includes("EFTPOS"));
const shouldRepairFromIndexDb = !!orderUuid && isEftposPayment && (!this.store.currentOrder || this.store.currentOrder.uuid !== orderUuid);
if (shouldRepairFromIndexDb) {
this.logError("EFTPOS push payment repair triggered", {
orderUuid,
currentOrderUuid: (_c = this.store.currentOrder) == null ? void 0 : _c.uuid,
paymentCode: paymentItem.code,
paymentType: paymentItem.type
});
try {
await this.repairEftposPaymentFromIndexDbAsync({
orderUuid,
paymentItem
});
} catch (error) {
this.logError("EFTPOS push payment repair failed", {
orderUuid,
error: error instanceof Error ? error.message : String(error)
});
}
return;
}
try {
if (!this.store.currentOrder) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
"未找到当前订单,无法添加支付项"
);
}
const orderPaymentType = this.store.currentOrder.is_deposit === 1 ? "deposit" : "normal";
const processedPaymentItem = await this.processCashPaymentItem(paymentItem);
const metadata = {
...processedPaymentItem.metadata,
rounding_rule: this.otherParams.order_rounding_setting,
shop_wallet_pass_id: this.otherParams.shop_wallet_pass_id
};
const paymentItemWithType = {
...processedPaymentItem,
order_payment_type: orderPaymentType,
metadata
};
await this.payment.addPaymentItemAsync(
this.store.currentOrder.uuid,
paymentItemWithType
);
this.logInfo("支付项添加成功");
this.clearCalculationCache();
await this.updateStateAmountToRemaining();
const remainingAmount = await this.calculateRemainingAmountAsync();
if (Number(remainingAmount) > 0) {
this.logInfo("订单金额还有待付的,同步 EFTPOS 支付");
const isEftposPayment2 = ((_d = paymentItem.type) == null ? void 0 : _d.toLowerCase()) === "eftpos" || ((_e = paymentItem.code) == null ? void 0 : _e.toUpperCase().includes("EFTPOS"));
const isCash = (0, import_utils.isCashPayment)(paymentItem);
const isCustomePayment = paymentItem.type === "custom";
this.logInfo("EFTPOS 支付检查:", {
paymentCode: paymentItem.code,
paymentType: paymentItem.type,
isEftposPayment: isEftposPayment2,
currentOrderSynced: this.store.isOrderSynced
});
if (isEftposPayment2 || isCash || isCustomePayment) {
this.logInfo("检测到 EFTPOS 支付,立即同步订单到后端...");
try {
const syncResult = await this.syncOrderToBackendWithReturn(true);
this.logInfo("EFTPOS 支付后订单同步完成 (已标记为手动同步):", {
orderId: syncResult.orderId,
isOrderSynced: this.store.isOrderSynced,
backendResponse: syncResult.response
});
} catch (error) {
this.logError("EFTPOS 支付后订单同步失败:", error);
await this.handleError(
new Error(
`EFTPOS 支付后订单同步失败: ${error instanceof Error ? error.message : String(error)}`
),
import_types.CheckoutErrorType.OrderCreationFailed
);
}
}
}
this.core.effects.emit(`${this.name}:onPaymentItemAdded`, {
orderUuid: this.store.currentOrder.uuid,
paymentMethodCode: paymentItem.code,
paymentMethodName: paymentItem.name,
amount: String(paymentItem.amount),
timestamp: Date.now()
});
} catch (error) {
this.logError("添加支付项失败:", error);
await this.handleError(error, import_types.CheckoutErrorType.PaymentFailed);
throw error;
}
}
/**
* 删除当前订单的支付项
*
* 从当前活跃订单中删除指定的支付项,支付项将被标记为已撤销状态
*
* @param paymentUuid 要删除的支付项UUID
* @throws 当前没有活跃订单时抛出错误
* @throws 支付项不存在时抛出错误
*/
async deletePaymentItemAsync(paymentUuid) {
try {
if (!this.store.currentOrder) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
"当前没有活跃订单,无法删除支付项"
);
}
this.logWarning("开始删除支付项:", {
orderUuid: this.store.currentOrder.uuid,
paymentUuid
});
const currentPayments = await this.payment.getPaymentItemsAsync(
this.store.currentOrder.uuid,
true
// 包括已撤销的支付项,因为我们需要验证是否存在
);
const paymentItem = currentPayments.find((p) => p.uuid === paymentUuid);
if (!paymentItem) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
`支付项不存在: ${paymentUuid}`
);
}
this.logInfo("找到要删除的支付项:", {
uuid: paymentItem.uuid,
code: paymentItem.code,
amount: paymentItem.amount,
status: paymentItem.status
});
await this.payment.deletePaymentAsync(
this.store.currentOrder.uuid,
paymentUuid
);
this.logInfo("Payment支付项删除完成", paymentItem);
this.clearCalculationCache();
const currentOrderId = this.store.currentOrder.order_id;
const isCurrentOrderReal = currentOrderId && !(0, import_utils.isVirtualOrderId)(currentOrderId);
const updatedOrder = await this.payment.getPaymentOrderByUuidAsync(
this.store.currentOrder.uuid
);
if (updatedOrder) {
if (isCurrentOrderReal && (0, import_utils.isVirtualOrderId)(updatedOrder.order_id)) {
this.logWarning(
"[Checkout] deletePaymentItemAsync: 检测到订单ID回退,保护真实订单ID:",
{
currentRealId: currentOrderId,
retrievedVirtualId: updatedOrder.order_id
}
);
updatedOrder.order_id = currentOrderId;
}
this.store.currentOrder = updatedOrder;
}
await this.updateStateAmountToRemaining(false);
await this.core.effects.emit(`${this.name}:onPaymentStarted`, {
orderUuid: this.store.currentOrder.uuid,
paymentMethodCode: paymentItem.code,
amount: `-${paymentItem.amount}`,
// 负数表示删除
timestamp: Date.now()
});
} catch (error) {
this.logError("删除支付项失败:", error);
await this.handleError(error, import_types.CheckoutErrorType.PaymentFailed);
throw error;
}
}
/**
* 批量更新当前订单的代金券支付项(覆盖更新)
*
* 删除所有现有的代金券支付项,然后添加新的代金券支付项。
* 这是一个覆盖式更新操作,确保代金券支付项的一致性。
*
* @param voucherPaymentItems 新的代金券支付项列表,每个都必须包含 voucher_id
* @throws 当前没有活跃订单时抛出错误
* @throws 支付项缺少 voucher_id 时抛出错误
*/
async updateVoucherPaymentItemsAsync(voucherPaymentItems) {
try {
this.logInfo("updateVoucherPaymentItemsAsync called:", {
voucherPaymentItems
});
if (!this.store.currentOrder) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
"当前没有活跃订单,无法更新代金券支付项"
);
}
const paymentItems = await this.payment.getPaymentItemsAsync(
this.store.currentOrder.uuid
);
if (!voucherPaymentItems || (voucherPaymentItems == null ? void 0 : voucherPaymentItems.length) === 0) {
const savedVoucherPaymentItems = (paymentItems == null ? void 0 : paymentItems.filter((item) => item.voucher_id && !item.isSynced)) || [];
this.logInfo("未传入 voucher 且本地订单无 voucher 记录,阻断updateVoucherPaymentItemsAsync", {
voucherPaymentItems,
savedVoucherPaymentItems
});
if ((savedVoucherPaymentItems == null ? void 0 : savedVoucherPaymentItems.length) === 0) {
this.updateStateAmountToRemaining(false);
return;
}
}
const allPaymentItemsSynced = paymentItems.every((item) => item.isSynced);
const remainingAmount = await this.calculateRemainingAmountAsync();
const remainingValue = new import_decimal.default(remainingAmount);
const isOrderSynced = this.store.isOrderSynced;
if (remainingValue.lte(0) && isOrderSynced && voucherPaymentItems.length === 0 && allPaymentItemsSynced) {
this.logInfo("订单已同步且支付完成,跳过清空代金券操作避免重复同步:", {
orderUuid: this.store.currentOrder.uuid,
orderId: this.store.currentOrder.order_id,
remainingAmount,
isOrderSynced,
voucherPaymentItemsCount: voucherPaymentItems.length,
reason: "Order synced and payment completed, skip clear vouchers to avoid duplicate sync"
});
return;
}
this.logInfo("开始批量更新代金券支付项:", {
voucherPaymentItems
});
const orderPaymentType = this.store.currentOrder.is_deposit === 1 ? "deposit" : "normal";
const voucherPaymentItemsWithType = voucherPaymentItems.map((item) => {
if (!item.voucher_id) {
throw (0, import_utils.createCheckoutError)(
import_types.CheckoutErrorType.ValidationFailed,
`代金券支付项缺少 voucher_id: ${JSON.stringify(item)}`
);
}
const metadata = {
...item.metadata,
rounding_rule: this.otherParams.order_rounding_setting,
shop_wallet_pass_id: this.otherParams.shop_wallet_pass_id
};
return {
...item,
order_payment_type: orderPaymentType,
metadata
};
});
await this.payment.updateVoucherPaymentItemsAsync(
this.store.currentOrder.uuid,
voucherPaymentItemsWithType
);
this.clearCalculationCache();
const currentOrderId = this.store.currentOrder.order_id;
const isCurrentOrderReal = currentOrderId && !(0, import_utils.isVirtualOrderId)(currentOrderId);
const updatedOrder = await this.payment.getPaymentOrderByUuidAsync(
this.store.currentOrder.uuid
);
if (updatedOrder) {
if (isCurrentOrderReal && (0, import_utils.isVirtualOrderId)(updatedOrder.order_id)) {
this.logWarning(
"updateVoucherPaymentItemsAsync: 检测到订单ID回退,保护真实订单ID:",
{
currentRealId: currentOrderId,
retrievedVirtualId: updatedOrder.order_id
}
);
updatedOrder.order_id = currentOrderId;
}
this.store.currentOrder = updatedOrder;
}
await this.updateStateAmountToRemaining(false);
await this.core.effects.emit(`${this.name}:onPaymentStarted`, {
orderUuid: this.store.currentOrder.uuid,
paymentMethodCode: "VOUCHER_BATCH",
amount: voucherPaymentItems.reduce((sum, item) => sum + parseFloat(String(item.amount)), 0).toFixed(2),
timestamp: Date.now()
});
this.logInfo("代金券支付项批量更新成功");
} catch (error) {
this.logInfo("[Checkout] 批量更新代金券支付项失败:", { error });
await this.handleError(error, import_types.CheckoutErrorType.PaymentFailed);
throw error;
}
}
/**
* 修改当前订单的定金状态
*
* 更新当前订单的 is_deposit 字段,用于标识订单是否为定金订单
*
* @param isDeposit 定金状态 (1: 定金订单, 0: 全款订单)
* @throw