UNPKG

@pisell/pisellos

Version:

一个可扩展的前端模块化SDK框架,支持插件系统

1,310 lines (1,309 loc) 130 kB
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