diff --git a/.gitignore b/.gitignore index c9b9501..6103248 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,7 @@ coverage/ server/dukang-api/prisma/migrations/ debug_v3.xlsx ~$debug_v3.xlsx +server/dukang-api/assets/fonts/*.ttf +server/dukang-api/assets/fonts/*.ttc +server/dukang-api/assets/fonts/*.otf deploy/auto-release.env diff --git a/apps/admin-web/src/lib/api.ts b/apps/admin-web/src/lib/api.ts index e44d304..b1ee956 100644 --- a/apps/admin-web/src/lib/api.ts +++ b/apps/admin-web/src/lib/api.ts @@ -195,6 +195,18 @@ export type AdminOrderRow = { quantity?: number; receiverName: string; receiverPhone: string; + receiverProvince?: string; + receiverCity?: string; + receiverDistrict?: string; + receiverAddress?: string; + benefitAmount?: number; + benefitCoupon?: { + couponNo?: string; + totalAmount?: number; + usedAmount?: number; + balance?: number; + status?: string; + } | null; createdAt: string; cityId?: string; city?: { id: string; name: string; code: string }; diff --git a/apps/admin-web/src/lib/exportExcel.ts b/apps/admin-web/src/lib/exportExcel.ts index 244355e..82e02ed 100644 --- a/apps/admin-web/src/lib/exportExcel.ts +++ b/apps/admin-web/src/lib/exportExcel.ts @@ -8,3 +8,19 @@ export function downloadExcelCsv(csv: string, filename: string) { a.click(); URL.revokeObjectURL(url); } + +/** Download binary file from base64 payload returned by HQ export APIs. */ +export function downloadBase64File(contentBase64: string, filename: string, mimeType: string) { + const binary = atob(contentBase64); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i += 1) { + bytes[i] = binary.charCodeAt(i); + } + const blob = new Blob([bytes], { type: mimeType }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); +} diff --git a/apps/admin-web/src/pages/OrdersPage.tsx b/apps/admin-web/src/pages/OrdersPage.tsx index e098b06..88a8938 100644 --- a/apps/admin-web/src/pages/OrdersPage.tsx +++ b/apps/admin-web/src/pages/OrdersPage.tsx @@ -4,7 +4,9 @@ import { Alert, Button, Checkbox, + Col, Collapse, + DatePicker, Descriptions, Drawer, Form, @@ -12,6 +14,7 @@ import { InputNumber, Modal, Radio, + Row, Select, Space, Table, @@ -20,7 +23,10 @@ import { message, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; +import dayjs, { type Dayjs } from 'dayjs'; +import { ORDER_TYPE_LABELS } from '@dukang/shared-types'; import { request, type AdminOrderItem, type AdminOrderRow, type HqProfile, type Paginated } from '../lib/api'; +import { downloadBase64File } from '../lib/exportExcel'; import { ADMIN_OPTIONS_PAGE_SIZE, DELIVERY_PROVIDER_LABELS, @@ -130,6 +136,110 @@ type OrderDetail = AdminOrderRow & { type ShipMode = 'WAREHOUSE' | 'EXPRESS'; +type OrderExportScope = 'filter' | 'selected'; +type OrderExportFormat = 'xlsx' | 'pdf'; + +type OrderExportFilters = { + orderNo?: string; + status?: string; + orderType?: string; + cityId?: string; + receiverPhone?: string; + fulfillmentHold?: boolean; + excludeTest?: boolean; + deliveryType?: string; + dateRange?: [Dayjs, Dayjs]; +}; + +type OrderExportResult = { + filename: string; + mimeType: string; + contentBase64: string; + count: number; +}; + +const ORDER_TYPE_OPTIONS = Object.entries(ORDER_TYPE_LABELS).map(([value, label]) => ({ value, label })); + +const DELIVERY_TYPE_OPTIONS = [ + { value: 'LOCAL', label: '同城' }, + { value: 'CROSS_CITY', label: '跨城' }, + { value: 'ON_SITE_PICKUP', label: '现场提货' }, +]; + +function isOnSitePickupOrder(row: Pick) { + return ( + row.deliveryType === 'ON_SITE_PICKUP' || + row.receiverAddress === '现场取货' || + row.receiverAddress === '现场提货' || + (row.receiverProvince === '现场' && row.receiverCity === '现场') + ); +} + +function formatReceiverAddress(row: AdminOrderRow) { + if (isOnSitePickupOrder(row)) return '现场取货'; + const region = [row.receiverProvince, row.receiverCity, row.receiverDistrict].filter(Boolean).join(''); + const detail = row.receiverAddress || ''; + if (!region) return detail; + if (!detail || detail.startsWith(region)) return detail || region; + return `${region}${detail}`; +} + +function datePresetRange(kind: 'day' | 'week' | 'month' | 'quarter' | 'year'): [Dayjs, Dayjs] { + const now = dayjs(); + if (kind === 'day') return [now.startOf('day'), now.endOf('day')]; + if (kind === 'week') { + const monday = now.startOf('day').subtract((now.day() + 6) % 7, 'day'); + return [monday, monday.add(6, 'day').endOf('day')]; + } + if (kind === 'month') return [now.startOf('month'), now.endOf('month')]; + if (kind === 'quarter') { + const start = now.month(Math.floor(now.month() / 3) * 3).startOf('month'); + return [start, start.add(2, 'month').endOf('month')]; + } + return [now.startOf('year'), now.endOf('year')]; +} + +const DATE_PRESETS: Array<{ key: 'day' | 'week' | 'month' | 'quarter' | 'year'; label: string }> = [ + { key: 'day', label: '当日' }, + { key: 'week', label: '当周' }, + { key: 'month', label: '当月' }, + { key: 'quarter', label: '当季' }, + { key: 'year', label: '当年' }, +]; + +function formatBenefitBrief(row: AdminOrderRow) { + const coupon = row.benefitCoupon; + if (coupon) { + return `总额¥${Number(coupon.totalAmount ?? 0).toFixed(0)} / 已用¥${Number(coupon.usedAmount ?? 0).toFixed(0)} / 余¥${Number(coupon.balance ?? 0).toFixed(0)}`; + } + if (row.benefitAmount != null) return `¥${Number(row.benefitAmount).toFixed(0)}`; + return '—'; +} + +function buildExportPayload( + scope: OrderExportScope, + format: OrderExportFormat, + filters: OrderExportFilters, + selectedIds: string[], +) { + const payload: Record = { scope, format }; + if (scope === 'selected') { + payload.ids = selectedIds; + return payload; + } + if (filters.orderNo) payload.orderNo = filters.orderNo; + if (filters.status) payload.status = filters.status; + if (filters.orderType) payload.orderType = filters.orderType; + if (filters.cityId) payload.cityId = filters.cityId; + if (filters.receiverPhone) payload.receiverPhone = filters.receiverPhone; + if (filters.deliveryType) payload.deliveryType = filters.deliveryType; + if (filters.fulfillmentHold) payload.fulfillmentHold = true; + if (filters.excludeTest) payload.excludeTest = true; + if (filters.dateRange?.[0]) payload.createdFrom = filters.dateRange[0].format('YYYY-MM-DD'); + if (filters.dateRange?.[1]) payload.createdTo = filters.dateRange[1].format('YYYY-MM-DD'); + return payload; +} + function orderProductRows(detail: OrderDetail): AdminOrderItem[] { if (detail.items?.length) return detail.items; if (!detail.productName) return []; @@ -192,8 +302,11 @@ export default function OrdersPage() { const [warehouses, setWarehouses] = useState([]); const [proxyOpen, setProxyOpen] = useState(false); const [trackOpen, setTrackOpen] = useState(false); + const [exporting, setExporting] = useState(false); + const [exportFormat, setExportFormat] = useState('xlsx'); const canDeleteOrders = (profile?.permissionKeys ?? []).includes('orders_delete'); const canProxyOrder = (profile?.permissionKeys ?? []).includes('orders'); + const canExportOrders = (profile?.permissionKeys ?? []).includes('orders'); const selectedOrders = useMemo( () => (data?.items ?? []).filter((row) => selectedRowKeys.includes(row.id)), @@ -238,6 +351,7 @@ export default function OrdersPage() { try { const values = form.getFieldsValue(); const qs = new URLSearchParams({ page: String(page), pageSize: String(pageSize) }); + if (initialOrderNo) qs.set('orderNo', initialOrderNo); if (values.orderNo) qs.set('orderNo', values.orderNo); if (values.status) qs.set('status', values.status); if (values.orderType) qs.set('orderType', values.orderType); @@ -245,12 +359,15 @@ export default function OrdersPage() { if (values.receiverPhone) qs.set('receiverPhone', values.receiverPhone); if (values.fulfillmentHold) qs.set('fulfillmentHold', 'true'); if (values.excludeTest) qs.set('excludeTest', 'true'); + if (values.deliveryType) qs.set('deliveryType', values.deliveryType); + if (values.dateRange?.[0]) qs.set('createdFrom', values.dateRange[0].format('YYYY-MM-DD')); + if (values.dateRange?.[1]) qs.set('createdTo', values.dateRange[1].format('YYYY-MM-DD')); const res = await request>(`/admin/orders?${qs}`); setData(res); } finally { setLoading(false); } - }, [form, page, pageSize]); + }, [form, page, pageSize, initialOrderNo]); useEffect(() => { void load(); @@ -451,79 +568,93 @@ export default function OrdersPage() { } } + async function submitExport(scope: OrderExportScope) { + const values = form.getFieldsValue() as OrderExportFilters; + if (scope === 'selected' && !selectedRowKeys.length) { + message.warning('请先勾选要导出的订单'); + return; + } + setExporting(true); + try { + const payload = buildExportPayload(scope, exportFormat, values, selectedRowKeys); + const result = await request('/admin/orders/export', { + method: 'POST', + body: JSON.stringify(payload), + }); + downloadBase64File(result.contentBase64, result.filename, result.mimeType); + message.success(`已导出 ${result.count} 条订单`); + } catch (e) { + message.error(e instanceof Error ? e.message : '导出失败'); + } finally { + setExporting(false); + } + } + const columns: ColumnsType = [ { - title: '订单号', - dataIndex: 'orderNo', - width: 200, - render: (v, row) => ( - - {v} - {row.isTest ? 测试 : null} - + title: '商品', + width: 240, + render: (_, row) => ( +
+ + {row.productName || '—'} + {row.isTest ? 测试 : null} + {row.fulfillmentHold ? 大单 : null} + {row.orderType === 'PROXY' || row.isProxyOrder ? ( + 代下单 + ) : null} + +
+ + {[row.productSpec, row.quantity != null ? `×${row.quantity}` : null] + .filter(Boolean) + .join(' ')} + +
+
), }, { - title: '城市', - width: 90, - render: (_, row) => row.city?.name || '—', + title: '状态 / 实付', + width: 130, + render: (_, row) => ( +
+ + {ORDER_STATUS_LABELS[row.status] || row.status} + +
¥{row.payAmount}
+
+ ), }, { - title: '商品', - width: 180, - ellipsis: true, + title: '好客权益', + width: 200, + render: (_, row) => formatBenefitBrief(row), + }, + { + title: '收货信息', + width: 240, render: (_, row) => { - const name = row.productName || '—'; - const qty = row.quantity != null ? ` ×${row.quantity}` : ''; + const address = formatReceiverAddress(row); return ( - - {name}{qty} - +
+
+ {row.receiverName || '—'} {row.receiverPhone || ''} +
+ {address ? ( + + {address} + + ) : null} +
); }, }, - { - title: '状态', - dataIndex: 'status', - width: 140, - render: (s, row) => ( - - {ORDER_STATUS_LABELS[s] || s} - {row.fulfillmentHold ? 大单待确认 : null} - {row.orderType === 'PROXY' || row.isProxyOrder ? ( - - 代下单 - - ) : null} - - ), - }, - { - title: '配送', - dataIndex: 'deliveryType', - width: 90, - render: (v) => - v === 'ON_SITE_PICKUP' ? '现场提货' : v === 'LOCAL' ? '同城' : '跨城', - }, - { title: '实付', dataIndex: 'payAmount', width: 90, render: (v) => `¥${v}` }, - { title: '收货人', dataIndex: 'receiverName', width: 90 }, - { title: '手机', dataIndex: 'receiverPhone', width: 120 }, - { - title: '用户', - dataIndex: ['user', 'userNo'], - width: 110, - render: (_, row) => row.user?.userNo || '—', - }, - { - title: '快递', - width: 100, - render: (_, row) => row.delivery?.trackingNo || row.delivery?.provider || '—', - }, { title: '下单时间', dataIndex: 'createdAt', width: 170, - render: (v) => new Date(v).toLocaleString('zh-CN'), + render: fmtTime, }, { title: '操作', @@ -557,24 +688,15 @@ export default function OrdersPage() { return (
- + 订单监控 - + {canProxyOrder ? ( ) : null} - {canDeleteOrders ? ( - - ) : null} @@ -592,54 +714,115 @@ export default function OrdersPage() { /> ) : null} -
{ setPage(1); void load(); }}> - - - - - - - - - - - ({ value, label }))} + /> + + + + + + + + + + + + + + + + + 大单拦截 + + + 过滤测试 + + + + + + +
+ {canExportOrders ? ( + + setExportFormat(e.target.value)} + > + Excel + PDF + + + + + ) : } + + {canDeleteOrders ? ( + + ) : null} - +
setSelectedRowKeys(keys as string[]), - } : undefined} + }} pagination={{ current: page, pageSize, total: data?.total ?? 0, showSizeChanger: true, + showTotal: (total) => `共 ${total} 条`, onChange: (p, ps) => { setPage(p); setPageSize(ps); diff --git a/docs/杜康好客-v3-PRD.md b/docs/杜康好客-v3-PRD.md index 43caa04..9098bb1 100644 --- a/docs/杜康好客-v3-PRD.md +++ b/docs/杜康好客-v3-PRD.md @@ -23,6 +23,7 @@ | 3.4.15 | 08-07 | mini-user 门店列表卡片;HQ 门店照片替换/删除;套餐多图上限 20 | [`v3.4.15`](./杜康好客-v3.4.15-mini-user门店列表优化.md) | | 3.4.16 | 08-09 | 联系电话分离;取消自动 ST;套餐 UI;合伙人列表/入驻客服门槛;HQ 门店列表表格 | [`v3.4.16`](./杜康好客-v3.4.16-门店联系电话与体验优化.md) | | 3.5.3 | 08-20 | 门店照片/企微审核/分享;核销金额序列化与提现补建;测试流水计结算;核销浮动提示;权益金额门店图标 | [`v3.5.3`](./杜康好客-v3.5.3-开发文档.md) | +| 3.5.4 | 08-21 | 商品 SPU+SKU 规格;DK 码/独立 69 码/规格主图;下单兼容回落;发票品类;权益文案与门店详情/收银台 UI;客户端版本号 | [`v3.5.4`](./杜康好客-v3.5.4-开发文档.md) | --- @@ -68,6 +69,13 @@ `待付款 → 已付款 → 已完成`;30min 未付取消。同城自动推小飞侠(有仓+API);**≥10箱(60瓶)** 大单待 HQ 确认。跨城总部物流到付。现场提货支付即完成。 +**HQ 订单导出**(`admin-web` 订单监控): + +- 支持按筛选(下单日期、状态、类型、城市、配送、收货手机等)或勾选订单导出 +- 格式:Excel(`.xlsx`)/ PDF(`.pdf`) +- 单次上限 5000 条;按筛选且未指定日期时默认近 30 天 +- 权限:`orders`;操作审计 `ORDER_EXPORT` + ### 3.3 佣金与结算 | 类型 | 默认 | 释放 | diff --git a/docs/杜康好客-v3-现状对照.md b/docs/杜康好客-v3-现状对照.md index d7076b4..df40f55 100644 --- a/docs/杜康好客-v3-现状对照.md +++ b/docs/杜康好客-v3-现状对照.md @@ -9,7 +9,7 @@ |------|------| | 主链路 | 登录→下单支付→权益→扫码核销→payout→HQ 打款 **可跑通** | | C 端 | `mini-user` 小程序 + h5-user;v3.4.13 版本门控/门店/物流已上 | -| 近期版本 | … · v3.4.16 · **v3.5.3 门店照片/企微审核/分享 + 核销金额/提现补修 + 权益图标** | +| 近期版本 | … · v3.5.3 · **v3.5.4 商品规格 SPU+SKU + 发票品类 + 权益/门店详情 UI** | | 冒烟 | `scripts/smoke-v3.mjs` 窄路径 ≠ 全量 ACC | | REQ 明细 | PRD §4 + `.cursor/skills/dukang-v3/reference-req-index.md` | @@ -32,7 +32,7 @@ | **mini-user** | 购酒/权益/核销/门店/物流/版本门控 | 规则弹窗、发票、四类型工单、问卷 | | **h5-shop** | 扫码核销、记录、营业、iOS 扫码 OAuth | 手机号核销、提现、子账号、弱网兜底 | | **h5-partner** | 子账号、拓店、订单/账单、套餐 | 试核销100、负责人复核、代下单 | -| **admin-web** | 商品/开城/门店/订单/权益/核销/结算/推广码 metrics/开发计划/技术支持 | 完整 SOP 审核 UI、发票、热力图 | +| **admin-web** | 商品/开城/门店/订单(含 Excel/PDF 导出)/权益/核销/结算/推广码 metrics/开发计划/技术支持 | 完整 SOP 审核 UI、发票、热力图 | | **后端** | 主模块、支付、权益、核销、payout、Courier 适配 | 30min 取消 job、部分 Wave3 | ## 3. 场景 SC-01~09 @@ -57,10 +57,14 @@ | 3.4.13 | [`体验优化`](./杜康好客-v3.4.13-体验优化开发文档.md) | ✅ 生产 | | 3.4.14 | [`mini-user 门店体验 + 小程序可配置 + 测试白名单`](./杜康好客-v3.4.14-mini-user门店体验开发文档.md) | ✅ 生产 | | 3.4.15 | [`mini-user 门店列表 + HQ 照片/套餐多图`](./杜康好客-v3.4.15-mini-user门店列表优化.md) | ✅ 生产 | -| 3.5.3 | [`门店照片/企微审核/分享 + 核销金额与提现补修`](./杜康好客-v3.5.3-开发文档.md) | 🔶 开发中 | +| 3.5.3 | [`门店照片/企微审核/分享 + 核销金额与提现补修`](./杜康好客-v3.5.3-开发文档.md) | ✅ 生产 | +| 3.5.4 | [`商品规格 SPU+SKU + 发票/文案 UI`](./杜康好客-v3.5.4-开发文档.md) | ✅ 生产(weapp 版本号需单独上传) | +| 3.5.5 | [`HQ 订单导出 + 订单监控页优化`](./杜康好客-v3.5.5-开发文档.md) | 🔶 开发完成,待发版 | | 日期 | 说明 | |------|------| +| 2026-08-23 | HQ 订单监控:筛选/勾选导出 Excel(xlsx)与 PDF;`POST /admin/orders/export` | +| 2026-08-21 | v3.5.4 发生产:规格表/回填 SKU/DK 码/主图列/发票品类;H5+API 200 | | 2026-08-20 | v3.5.3 增补:Decimal 序列化/提现补建、测试流水计结算、核销浮动提示、好客权益金额图标 | | 2026-08-08 | v3.4.14 / v3.4.15 已发生产,更新状态 | | 2026-08-07 | v3.4.14 增补统一测试白名单(限测可见 / Mock 旁路;测试流水计入结算) | diff --git a/docs/杜康好客-v3.5.4-开发文档.md b/docs/杜康好客-v3.5.4-开发文档.md index 057313c..c554ba2 100644 --- a/docs/杜康好客-v3.5.4-开发文档.md +++ b/docs/杜康好客-v3.5.4-开发文档.md @@ -1,101 +1,145 @@ -# 杜康好客 · v3.5.4 商品规格(SPU + SKU) +# 杜康好客 · v3.5.4 开发文档 > **2026-08-21** · mini-user / admin-web / h5-partner / API / Prisma -> 目标:商品详情页内可选销售规格;价格、权益、69 码、履约、上下架按 SKU;箱装按瓶当量起购与物流。 +> **生产 commit**:`5fdddae`(规格主图与门店使用规则合并入 `main`)及后续客户端版本号 `3.5.4` +> **主题**:商品规格(SPU + SKU)上线 + 同批次发票 / 文案 / 门店详情体验 -## 范围 +--- -| # | 项 | 交付 | +## 1. 版本目标 + +1. 商品由单规格升级为 **SPU + 多 SKU**:价格、权益、69 码、履约、上下架、主图按规格。 +2. 箱装按 **瓶当量** 起购与物流,不改城市起购阈值口径。 +3. **旧客户端兼容**:不传 `skuId` 仍可下单;无 SKU 回落 SPU,禁止「商品暂无可售规格」误伤。 +4. 同批次补齐:发票品类、权益文案、收银台 logo、门店详情「使用规则」、C 端版本号。 + +**不做**:SKU 库存、把现有酒祖 10/15/20 合并为一个 SPU。 + +--- + +## 2. 功能清单(完整) + +### 2.1 商品规格(核心) + +| # | 模块 | 交付说明 | +|---|------|----------| +| 1 | 数据模型 | `common_product_item` 作 SPU;新增 `spec_attr` / `spec_value` / `sku` / `sku_spec`;订单快照 `sku_id` / `sale_unit` / `bottles_per_unit` | +| 2 | 存量迁移 | 每商品默认 1 个瓶装 SKU;SQL `migrate-product-sku-v354.sql`;脚本 `backfill-product-skus.ts` | +| 3 | Catalog | 列表 `specEnabled`/`saleUnit`;详情 `specAttrs`/`skus`/`defaultSkuId`;旧字段拍平保留 | +| 4 | Trade | preview/create 可选 `skuId`;有可售 SKU → 默认/唯一;**0 可售 → 回落 SPU**;订单 `sku_id` 可空 | +| 5 | Domain | 起购/大单/物流按 `quantity × bottlesPerUnit` 瓶当量 | +| 6 | Admin | `PUT .../specs`、`PUT .../skus`;抽屉加宽;「填写」弹窗;规格轴 + SKU 矩阵 | +| 7 | mini-user | 详情规格 chips;确认页带 `skuId`;箱装数量文案;选规格切主图 | +| 8 | 代下单 | HQ / 合伙人可选规格 SKU | + +### 2.2 SKU 码与 69 码 + +| # | 项 | 说明 | |---|----|------| -| 1 | 数据模型 | `common_product_item` 升级为 SPU;新增 `spec_attr` / `spec_value` / `sku` / `sku_spec`;订单快照 `sku_id` / `sale_unit` / `bottles_per_unit` | -| 2 | 存量迁移 | 每商品 1 个默认瓶装 SKU;SQL:`prisma/migrate-product-sku-v354.sql`;脚本:`prisma/backfill-product-skus.ts` | -| 3 | Catalog | 列表附加 `specEnabled`/`saleUnit`;详情附加 `specAttrs`/`skus`/`defaultSkuId`;旧字段拍平保留 | -| 4 | Trade | preview/create 可选 `skuId`;无 SKU 回落 SPU;有可售 SKU 用默认/唯一 | -| 5 | Admin | `PUT /admin/products/:id/specs`、`PUT .../skus`;商品抽屉加宽;规格弹窗填写;每规格主图 | -| 6 | mini-user | 详情规格 chips;选规格切主图;确认页带 `skuId`;箱装数量文案 | -| 7 | 代下单 | HQ / 合伙人可选规格 SKU | -| 8 | 权益文案 | 「好客权益券」→「好客权益」;首页角标「享{amount}好客权益」(去掉门店 icon) | -| 9 | SKU 码 | 系统生成 `DK` + 6 位数字,后台不可填;每规格独立 69 码 | -| 10 | 规格主图 | `common_product_sku.image_url` 可选;空则 C 端回落商品封面 | +| 9 | SKU 码 | 系统生成 `DK` + 6 位数字,后台不可手填;脚本 `rewrite-sku-codes-dk.ts` 改存量 | +| 10 | 69 码 | **每规格独立**;同商品内 + 跨商品唯一;不可与其它 SKU 冲突 | -**不做**:库存、把现有酒祖 10/15/20 合并为一个 SPU。 +### 2.3 规格主图 -## 兼容规则(线上) +| # | 项 | 说明 | +|---|----|------| +| 11 | DB | `common_product_sku.image_url`(`migrate-sku-image.sql`) | +| 12 | Admin | 弹窗可为每 SKU 上传主图 | +| 13 | C 端 | 选规格优先 SKU 主图,空则回落商品封面 | -- 不改 `/api/v1` 前缀;**只增字段,不删旧字段语义**。 +### 2.4 发票(同批次) + +| # | 项 | 说明 | +|---|----|------| +| 14 | 品类 | `user_invoice.invoice_category`:`LIQUOR` 酒水类 / `CATERING` 餐饮类 | +| 15 | C 端申请 | 票种写死「增值税普通发票」;类型选酒水/餐饮;备注写入申请 | +| 16 | 抬头 | 必填项打 `*`;强调邮箱必填 | +| 17 | Admin | 发票列表展示品类;专票仍可走后台 | + +### 2.5 文案与 UI(同批次) + +| # | 位置 | 变更 | +|---|------|------| +| 18 | 全局权益文案 | 「好客权益券」→「好客权益」 | +| 19 | 首页角标 | 「享{amount}好客权益」,去掉门店 icon | +| 20 | 商品详情 | 规格区与「好客权益」说明加大间距 | +| 21 | 待支付/收银台 | 顶部 logo 用 `logo2.png`(杜康印章) | +| 22 | 门店详情 | 「使用规则」红色,置于「门店详情」上方;详情正文限高可滚动 | +| 23 | 客户端版本号 | `package.json` + `client-version.ts` → **3.5.4**(「我的」页展示) | + +--- + +## 3. 兼容规则(线上) + +- 不改 `/api/v1`;**只增字段,不删旧字段语义**。 - 旧客户端不传 `skuId`: - - 有 ≥1 个可售 SKU → 用默认(或第一个)可售 SKU - - **没有任何可售 SKU(未回填)→ 回落 SPU 字段,照常下单**(禁止再报「商品暂无可售规格」) -- 传入 `skuId`(新客户端选规格)→ 按该 SKU 校验。 -- 旧 admin `PUT /admin/products/:id` 不传规格时:仅同步**唯一**默认 SKU(多规格时不同步,防误改);不同步覆盖已生成的 DK 码。 -- SPU 上 `sku_code` / `barcode_69` 去掉唯一约束,唯一下沉到 `common_product_sku`;SPU 列保留为默认 SKU 冗余。 -- 订单 `sku_id` 可空;无规格快照走商品 69 码 / 价格。 + - ≥1 个可售 SKU → 默认(或第一个)可售 SKU + - **0 个可售 SKU → 回落 SPU,照常下单** +- 新客户端传 `skuId` → 按该 SKU 校验。 +- 旧 admin 不传规格:仅同步**唯一**默认 SKU;不覆盖已生成的 DK 码。 +- SPU 上 `sku_code` / `barcode_69` 唯一约束下沉到 SKU;SPU 列保留冗余。 -## 发版一并执行 +--- -按顺序在**目标环境库**执行(先测试后生产)。本机 `npx ts-node …` 只改本地 `.env` 库,不会自动打线上。 +## 4. 发版与生产执行记录 -1. **规格表** `server/dukang-api/prisma/migrate-product-sku-v354.sql` - 建 spec/sku 表,订单加 `sku_id` / `sale_unit` / `bottles_per_unit`。 -2. **回填默认 SKU** - `cd server/dukang-api && npx ts-node prisma/backfill-product-skus.ts` - 尚无 SKU 的商品各建 1 条默认瓶装 SKU(码用 DK)。 -3. **SKU 主图列** `server/dukang-api/prisma/migrate-sku-image.sql` - ```sql - ALTER TABLE `common_product_sku` - ADD COLUMN `image_url` VARCHAR(512) NULL AFTER `sort_order`; - ``` -4. **SKU 码改 DK**(生产须在服务器用 `.env.production` 的 `DATABASE_URL`) - `npx dotenv -e .env.production -- npx ts-node prisma/rewrite-sku-codes-dk.ts` - 已是 `DK`+数字的不动;`JZ-10` / `QX-001` 等会改。**不改 69 码、订单、价格。** -5. **发票品类列**(同批次)`server/dukang-api/prisma/migrate-invoice-category.sql` - ```sql - ALTER TABLE `user_invoice` - ADD COLUMN `invoice_category` VARCHAR(16) NOT NULL DEFAULT 'LIQUOR' AFTER `invoice_kind`; - ``` -6. 发布 API + mini-user + admin-web。 - **此时不要给商品加第二规格**,等新小程序上线后再配「单瓶 / 整箱」。 +按序在目标库执行(先测后产)。本机脚本只打本地 `.env`。 -## 起购与物流 +| 步骤 | 动作 | 生产状态(2026-08-21) | +|------|------|------------------------| +| 1 | `migrate-product-sku-v354.sql` / Prisma sync | ✅ `db push` 已对齐 | +| 2 | `backfill-product-skus.ts` | ✅ 回填 9 个默认 SKU(扫 10 商品) | +| 3 | `migrate-sku-image.sql` / schema `image_url` | ✅ | +| 4 | `rewrite-sku-codes-dk.ts` | ✅ 13 条均已 DK,改写 0 | +| 5 | `migrate-invoice-category.sql` | ✅ schema 已含 | +| 6 | 发 API + mini-user H5 + admin | ✅ `mini-user 200` / `api 200` | +| 7 | 微信小程序 weapp 上传 | ⚠️ 需单独上传后「我的」页才见 v3.5.4 | + +运营注意:新包上线前**不要**给商品加第二规格;上线后再配「单瓶 / 整箱」。 + +--- + +## 5. 起购与物流 - `bottleQty = quantity × bottlesPerUnit` -- 城市起购阈值仍是瓶;箱装 SKU(`bottlesPerUnit=6`)数量 1 即可过同城 2 / 跨城 6。 -- 大单拦截、承运商 `goodsNum` 使用瓶当量。 +- 城市起购仍按瓶;箱装(`bottlesPerUnit=6`)数量 1 即可过同城 2 / 跨城 6 +- 大单拦截、承运商 `goodsNum` 用瓶当量 -## 关键表摘要 +--- + +## 6. 关键表 / 字段摘要 - `common_product_spec_attr` / `common_product_spec_value` -- `common_product_sku`(`sale_unit`=`BOTTLE|BOX`,`bottles_per_unit`,`image_url` 可选) +- `common_product_sku`(`sale_unit`、`bottles_per_unit`、`image_url`) - `common_product_sku_spec` - `user_order.sku_id` / `sale_unit` / `bottles_per_unit` -- `user_invoice.invoice_category`:`LIQUOR` 酒水类 / `CATERING` 餐饮类;票种 C 端固定增值税普通发票 +- `user_invoice.invoice_category` -## SKU 码与 69 码 +--- -- SKU 码:服务端生成 `DK000001` 起,后台只展示。 -- 每个规格必须填写**互不相同**的 69 码(全局不可与其它商品 SKU 冲突)。 -- 总部「规格与 SKU」点「填写」弹窗编辑 69 码 / 价 / 履约 / 主图。 +## 7. 关键路径(便于排查) -## 权益与小程序 UI(同批次) +| 域 | 路径 | +|----|------| +| Prisma / 脚本 | `server/dukang-api/prisma/migrate-product-sku-v354.sql`、`backfill-product-skus.ts`、`rewrite-sku-codes-dk.ts`、`migrate-sku-image.sql`、`migrate-invoice-category.sql` | +| 规格工具 | `server/.../catalog/product-sku.util.ts` | +| Admin 规格 | `apps/admin-web/src/components/ProductSpecsEditor.tsx` | +| C 端详情 | `apps/mini-user/src/pages/product-detail/` | +| 版本号 | `apps/mini-user/src/lib/client-version.ts` | +| 门店详情 | `apps/mini-user/src/pages/store-detail/` | +| 发票 | `apps/mini-user/src/pages/invoice-apply/`、`invoice-titles/` | -| 位置 | 变更 | -|------|------| -| 门店详情等 | 「好客权益券」→「好客权益」 | -| 首页商品角标 `CouponBadge` | 纯文案 **「享{amount}好客权益」**,无门店 icon | -| 商品详情 | 规格区与「好客权益」说明之间加大间距 | -| 待支付 / 收银台 | 顶部 logo 使用 `public/images/logo2.png`(杜康印章) | -| 门店详情 | 「使用规则」四字红色,放在「门店详情」**上面**;门店详情正文限高,超出上下滚动 | -| 发票申请 | 票种写死增值税普通发票;类型选酒水类/餐饮类;申请备注;抬头邮箱必填打 `*` | +--- -其它页 `BenefitFigure`(我的/权益/核销等)仍保留门店核销图标,仅首页角标去 icon。 +## 8. 验收清单 -## 验收 - -- [ ] 未配规格:旧小程序/H5/代下单路径与现网一致(无 SKU 也能下单) -- [ ] 多规格:详情选规格后价格/权益/履约/主图变化 -- [ ] 整箱 SKU:数量 1 过起购;订单快照 `bottles_per_unit=6` -- [ ] `GET /catalog/products/:id` 仍含 `id/name/price/spec/skuCode/...` -- [ ] 小程序无「好客权益券」字样;首页角标为「享{金额}好客权益」且无门店 icon -- [ ] 后台 SKU 码为 DK 开头且不可手填;每规格独立 69 码与主图 -- [ ] 门店详情「使用规则」红色且在门店详情上方;门店详情超长可滚动 -- [ ] 待支付页顶部为 logo2 印章 +- [ ] 未配规格:旧端下单与现网一致(无 SKU 也能下) +- [ ] 多规格:选规格后价格/权益/履约/主图变化;漏选 400 +- [ ] 整箱:数量 1 过起购;订单 `bottles_per_unit=6` +- [ ] 详情 API 仍含 `id/name/price/spec/skuCode/...` +- [ ] 后台 SKU 码 DK 开头不可手填;每规格独立 69 码与主图 +- [ ] 无「好客权益券」;首页角标「享{金额}好客权益」无门店 icon +- [ ] 门店详情「使用规则」红色且在详情上方;详情超长可滚 +- [ ] 待支付页为 logo2 印章 +- [ ] 发票:普票固定 + 酒水/餐饮品类 + 邮箱必填 +- [ ] 「我的」页版本 **杜康好客 v3.5.4**(需 weapp 新包) diff --git a/docs/杜康好客-v3.5.5-开发文档.md b/docs/杜康好客-v3.5.5-开发文档.md new file mode 100644 index 0000000..135468b --- /dev/null +++ b/docs/杜康好客-v3.5.5-开发文档.md @@ -0,0 +1,170 @@ +# 杜康好客 · v3.5.5 开发文档 + +> **2026-08-23** · admin-web / API +> **主题**:HQ 订单监控导出(Excel / PDF)+ 订单列表页布局优化 + +--- + +## 1. 版本目标 + +1. 总部 **订单监控** 支持按筛选或勾选导出 **Excel(.xlsx)** / **PDF(.pdf)**。 +2. 导出内容聚焦运营履约:**商品明细、收货信息、好客权益摘要**;次要字段从列表与导出列中精简。 +3. **订单列表页** 筛选区改为网格布局,支持日期快捷选择;导出、查询、重置、批量删除同一操作行。 +4. 修复现场取货订单地址展示为「现场现场取货现场取货」的问题。 + +**不做**:C 端 / 合伙人 / 门店端改动;无 Prisma 迁移。 + +--- + +## 2. 功能清单 + +### 2.1 HQ 订单导出(核心) + +| # | 模块 | 交付说明 | +|---|------|----------| +| 1 | API | `POST /api/v1/admin/orders/export` — 生成文件,响应 `{ filename, mimeType, contentBase64, count }` | +| 2 | 预览 | `POST /api/v1/admin/orders/export/preview` — 返回 `{ count, max, exceeds }`(上限 5000) | +| 3 | 导出范围 | `scope=filter` 按当前筛选;`scope=selected` 仅导出勾选 `ids` | +| 4 | 格式 | `format=xlsx`(exceljs)/ `format=pdf`(pdfkit,横向 A4) | +| 5 | 权限 | `HqAuthGuard` + `orders`;操作审计 `ORDER_EXPORT` | +| 6 | 筛选复用 | `buildOrderWhere()` 与列表查询共用;新增 `deliveryType` 筛选 | + +**导出列**(11 列): + +订单号、下单时间、状态、商品、规格、数量、实付、好客权益、收货人、手机、收货地址。 + +好客权益列:有权益券时 `总额¥X / 已用¥Y / 余¥Z`;否则回落订单 `benefitAmount`。 + +### 2.2 订单列表页(admin-web) + +| # | 项 | 说明 | +|---|----|------| +| 7 | 筛选布局 | 网格表单:下单日期、状态、类型、配送、城市、收货手机;大单拦截 / 过滤测试 | +| 8 | 日期快捷 | 当日、当周(周一至周日)、当月、当季、当年 | +| 9 | 列表列 | 商品(含规格/数量/标签)、状态+实付、好客权益、收货信息、下单时间、操作 | +| 10 | 勾选导出 | 列表常驻勾选;左侧 Excel/PDF +「导出已勾选」「导出全部筛选」 | +| 11 | 操作行 | 右侧同一行:查询、重置、批量删除(有权限时) | +| 12 | 现场取货 | 收货地址识别 `ON_SITE_PICKUP` 或省市区占位后,统一显示「现场取货」 | + +### 2.3 列表数据增强 + +| # | 项 | 说明 | +|---|----|------| +| 13 | 权益关联 | `GET /admin/orders` include `benefitCoupon`(券号、总额/已用/余额、状态) | +| 14 | 收货字段 | `AdminOrderRow` 增加省市区、地址、`benefitAmount`、`benefitCoupon` 类型 | + +--- + +## 3. API 契约 + +### 3.1 导出请求体 `AdminOrdersExportDto` + +```json +{ + "scope": "filter", + "format": "xlsx", + "ids": ["123"], + "status": "PENDING_SHIP", + "orderType": "NORMAL", + "cityId": "1", + "receiverPhone": "138", + "deliveryType": "LOCAL", + "fulfillmentHold": true, + "excludeTest": true, + "createdFrom": "2026-08-01", + "createdTo": "2026-08-23" +} +``` + +| 字段 | 说明 | +|------|------| +| `scope` | `filter` \| `selected`(勾选时仅传 `ids`,忽略其它筛选) | +| `format` | `xlsx` \| `pdf` | +| `createdFrom` / `createdTo` | 按筛选导出且均未填时,服务端默认近 30 天 | + +### 3.2 错误码 + +| 场景 | HTTP | message | +|------|------|---------| +| 超过 5000 条 | 400 | 超过 5000 条,请缩小日期或筛选条件 | +| 无数据 | 400 | 没有可导出的订单 | +| 勾选为空 | 400 | 请先勾选要导出的订单 | +| PDF 无字体 | 500 | 未找到可用于 PDF 的中文字体… | + +### 3.3 列表查询增量 + +`GET /admin/orders` 查询参数新增(与导出筛选一致): + +- `deliveryType`:`LOCAL` \| `CROSS_CITY` \| `ON_SITE_PICKUP` +- `createdFrom` / `createdTo`:下单日期(已有 DTO,本版接 UI) + +--- + +## 4. 依赖与部署 + +### 4.1 新增 npm 依赖(仅 API) + +- `exceljs` — xlsx 生成 +- `pdfkit` + `@types/pdfkit` — pdf 生成 + +### 4.2 PDF 中文字体 + +路径:`server/dukang-api/assets/fonts/`(见 `README.md`)。 + +解析顺序: + +1. 环境变量 `EXPORT_PDF_FONT_PATH` +2. `assets/fonts/NotoSansSC-Regular.otf` / `simhei.ttf` / `msyh.ttc` +3. Linux 系统 Noto CJK +4. Windows 系统字体(开发机) + +> 字体二进制已加入 `.gitignore`;生产部署需放置字体或安装 `fonts-noto-cjk`。 + +### 4.3 发版步骤 + +| 步骤 | 动作 | +|------|------| +| 1 | `pnpm install`(根目录,拉取 exceljs/pdfkit) | +| 2 | 确认 `server/dukang-api/assets/fonts/` 有中文字体(PDF 必需) | +| 3 | `pnpm build`(API + admin-web) | +| 4 | 发 `dukang-api` + `admin-web`;**无需** `prisma migrate` | + +--- + +## 5. 关键路径(便于排查) + +| 域 | 路径 | +|----|------| +| 导出工具 | `server/dukang-api/src/modules/ops/admin-order-export.util.ts` | +| 订单服务 | `server/dukang-api/src/modules/ops/admin-orders.service.ts` | +| 控制器 | `server/dukang-api/src/modules/ops/admin-orders.controller.ts` | +| DTO | `server/dukang-api/src/modules/ops/dto/admin-query.dto.ts` | +| 审计常量 | `server/dukang-api/src/common/hq-operation/hq-operation.constants.ts`(`ORDER_EXPORT`) | +| 前端页面 | `apps/admin-web/src/pages/OrdersPage.tsx` | +| 下载工具 | `apps/admin-web/src/lib/exportExcel.ts`(`downloadBase64File`) | +| 类型 | `apps/admin-web/src/lib/api.ts`(`AdminOrderRow`) | +| 需求 | `docs/杜康好客-v3-PRD.md` §3.2 HQ 订单导出 | +| 现状 | `docs/杜康好客-v3-现状对照.md` | + +--- + +## 6. 验收清单 + +- [ ] 订单列表:网格筛选 + 日期快捷(当日/当周/当月/当季/当年)可用 +- [ ] 列表展示:商品(规格×数量)、状态/实付、好客权益、收货人+手机+地址 +- [ ] 现场取货订单地址仅显示「现场取货」,不出现重复拼接 +- [ ] 勾选若干订单 →「导出已勾选」→ xlsx / pdf 条数与勾选一致 +- [ ] 设筛选条件 →「导出全部筛选」→ 文件含筛选范围内全部订单(≤5000) +- [ ] 超 5000 条时导出全部筛选返回明确错误 +- [ ] Excel / PDF 中文正常;PDF 表头与分页可读 +- [ ] 无 `orders` 权限账号无法调用导出接口 +- [ ] HQ 操作日志有 `ORDER_EXPORT` 记录 +- [ ] 无删除权限账号仍可勾选并导出;有删除权限时批量删除与导出/查询同一行 + +--- + +## 7. 与 v3.5.4 关系 + +- **仅 HQ 内部工具**,不影响 C 端小程序版本号。 +- 商品规格(SPU/SKU)、发票等 v3.5.4 能力保持不变。 +- PRD §3.2 已补充 HQ 订单导出业务规则;本版为实现与 UI 落地说明。 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e33fa9..99fb1fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -187,25 +187,25 @@ importers: version: link:../../packages/weixin-sdk '@tarojs/components': specifier: 4.2.0 - version: 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + version: 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) '@tarojs/helper': specifier: 4.2.0 - version: 4.2.0 + version: 4.2.0(@swc/helpers@0.5.23) '@tarojs/plugin-framework-react': specifier: 4.2.0 - version: 4.2.0(@tarojs/helper@4.2.0)(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + version: 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) '@tarojs/plugin-platform-h5': specifier: 4.2.0 - version: 4.2.0(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + version: 4.2.0(@swc/helpers@0.5.23)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) '@tarojs/plugin-platform-weapp': specifier: 4.2.0 - version: 4.2.0(@tarojs/service@4.2.0)(@tarojs/shared@4.2.0) + version: 4.2.0(@tarojs/service@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0) '@tarojs/react': specifier: 4.2.0 version: 4.2.0(react@18.3.1) '@tarojs/router': specifier: 4.2.0 - version: 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))) + version: 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))) '@tarojs/runtime': specifier: 4.2.0 version: 4.2.0 @@ -214,7 +214,7 @@ importers: version: 4.2.0 '@tarojs/taro': specifier: 4.2.0 - version: 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + version: 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) qrcode: specifier: ^1.5.4 version: 1.5.4 @@ -239,10 +239,10 @@ importers: version: 7.29.7(@babel/core@7.29.7) '@tarojs/cli': specifier: 4.2.0 - version: 4.2.0 + version: 4.2.0(@swc/helpers@0.5.23) '@tarojs/vite-runner': specifier: 4.2.0 - version: 4.2.0(@tarojs/runtime@4.2.0)(@types/babel__core@7.20.5)(postcss@8.5.15)(rollup@3.30.0)(terser@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)) + version: 4.2.0(@swc/helpers@0.5.23)(@tarojs/runtime@4.2.0)(@types/babel__core@7.20.5)(postcss@8.5.15)(rollup@3.30.0)(terser@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)) '@types/react': specifier: ^18.3.3 version: 18.3.31 @@ -251,7 +251,7 @@ importers: version: 4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)) babel-preset-taro: specifier: 4.2.0 - version: 4.2.0(@babel/core@7.29.7)(@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7))(@babel/preset-react@7.29.7(@babel/core@7.29.7)) + version: 4.2.0(@babel/core@7.29.7)(@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7))(@babel/preset-react@7.29.7(@babel/core@7.29.7))(@swc/helpers@0.5.23) typescript: specifier: ^5.4.5 version: 5.9.3 @@ -361,6 +361,9 @@ importers: dotenv: specifier: ^17.4.2 version: 17.4.2 + exceljs: + specifier: ^4.4.0 + version: 4.4.0 express: specifier: ^4.21.0 version: 4.22.1 @@ -370,6 +373,9 @@ importers: ip2region: specifier: ^2.3.0 version: 2.3.0(@types/node@20.19.43) + pdfkit: + specifier: ^0.19.1 + version: 0.19.1 qrcode: specifier: ^1.5.4 version: 1.5.4 @@ -382,7 +388,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.0 - version: 10.4.9(@swc/core@1.3.96) + version: 10.4.9 '@nestjs/schematics': specifier: ^10.1.0 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -398,6 +404,9 @@ importers: '@types/node': specifier: ^20.14.0 version: 20.19.43 + '@types/pdfkit': + specifier: ^0.17.6 + version: 0.17.6 '@types/qrcode': specifier: ^1.5.6 version: 1.5.6 @@ -406,7 +415,7 @@ importers: version: 5.22.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.3.96)(@types/node@20.19.43)(typescript@5.9.3) + version: 10.9.2(@types/node@20.19.43)(typescript@5.9.3) typescript: specifier: ^5.4.5 version: 5.9.3 @@ -1371,6 +1380,12 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@fast-csv/format@4.3.5': + resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} + + '@fast-csv/parse@4.3.6': + resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -1576,6 +1591,14 @@ packages: peerDependencies: typescript: '>=4.8.2' + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2156,6 +2179,9 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + '@swc/register@0.1.10': resolution: {integrity: sha512-6STwH/q4dc3pitXLVkV7sP0Hiy+zBsU2wOF1aXpXR95pnH3RYHKIsDC+gvesfyB7jxNT9OOZgcqOp9RPxVTx9A==} deprecated: Use @swc-node/register instead @@ -2599,12 +2625,18 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@14.18.63': + resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + '@types/node@20.19.43': resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} '@types/node@22.20.0': resolution: {integrity: sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==} + '@types/pdfkit@0.17.6': + resolution: {integrity: sha512-tIwzxk2uWKp0Cq9JIluQXJid77lYhF52EsIOwhsMF4iWLA6YneoBR1xVKYYdAysHuepUB0OX4tdwMiUDdGKmig==} + '@types/postcss-url@10.0.4': resolution: {integrity: sha512-5QIO9NgbWmAkle65haRqkdgYPCOXheNsaFdbTJJQjT302yK3H49ql4t9a4y0NfpuPtU/UBo15VcV64WCSIMJKg==} @@ -2907,6 +2939,18 @@ packages: resolution: {integrity: sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==} engines: {node: '>=4'} + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + archy@1.0.0: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} @@ -2937,6 +2981,9 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -3029,6 +3076,10 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64-js@0.0.8: + resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} + engines: {node: '>= 0.4'} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3037,16 +3088,26 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + bl@1.2.3: resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==} bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + body-parser@1.20.4: resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -3068,6 +3129,12 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + browserslist@4.28.4: resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3091,9 +3158,17 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + builtin-status-codes@3.0.0: resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} @@ -3168,6 +3243,9 @@ packages: resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} engines: {node: '>=4'} + chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} @@ -3265,6 +3343,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + cluster-key-slot@1.1.1: resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==} engines: {node: '>=0.10.0'} @@ -3298,6 +3380,10 @@ packages: resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} engines: {node: '>= 6'} + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + compute-scroll-into-view@3.1.1: resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} @@ -3382,6 +3468,15 @@ packages: typescript: optional: true + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -3526,6 +3621,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3582,6 +3680,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} @@ -3717,6 +3818,7 @@ packages: eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -3774,6 +3876,10 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + exceljs@4.4.0: + resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} + engines: {node: '>=8.3.0'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -3801,6 +3907,10 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} + fast-csv@4.3.6: + resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} + engines: {node: '>=10.0.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3939,6 +4049,9 @@ packages: debug: optional: true + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -4001,6 +4114,11 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + deprecated: This package is no longer supported. + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -4260,6 +4378,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immutable@5.1.9: resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} @@ -4449,6 +4570,9 @@ packages: js-base64@2.6.4: resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} + js-md5@0.8.3: + resolution: {integrity: sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4512,6 +4636,9 @@ packages: jstoxml@2.2.9: resolution: {integrity: sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + jwa@1.4.2: resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} @@ -4544,6 +4671,10 @@ packages: resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} engines: {node: '>=8'} + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -4551,13 +4682,22 @@ packages: libphonenumber-js@1.13.7: resolution: {integrity: sha512-rvr3HIMdOgzhz1RFGjftji+wjoAFlzhqCNqJOU/MKTZQ8d9NZxAR/tI+0weDicyoucqVR0U1GCniqHJ0f8aM2A==} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} + linebreak@1.1.0: + resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + loader-runner@4.3.2: resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==} engines: {node: '>=6.11.5'} @@ -4593,6 +4733,18 @@ packages: lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} @@ -4602,9 +4754,19 @@ packages: lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + lodash.isnil@4.0.0: + resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} + lodash.isnumber@3.0.3: resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} @@ -4614,6 +4776,9 @@ packages: lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.isundefined@3.0.1: + resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -4623,6 +4788,12 @@ packages: lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -4770,6 +4941,10 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} @@ -5014,6 +5189,12 @@ packages: resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==} engines: {node: '>=8'} + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + param-case@2.1.1: resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} @@ -5095,6 +5276,9 @@ packages: pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pdfkit@0.19.1: + resolution: {integrity: sha512-6Gzk+wDwTs4VSxsR5rCMTnIl5nlmkye1oWB0l2hDB1EX6ZNSIBroKQEv+2+fPPn+stVjyqzmsqRJVDfB9fo5DA==} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -5143,6 +5327,9 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + png-js@1.1.0: + resolution: {integrity: sha512-PM/uYGzGdNSzqeOgly68+6wKQDL1SY0a/N+OEa/+br6LnHWOAJB0Npiamnodfq3jd2LS/i2fMeOKSAILjA+m5Q==} + pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} @@ -5604,6 +5791,9 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -5704,10 +5894,18 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -5764,6 +5962,10 @@ packages: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -5836,6 +6038,9 @@ packages: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -6072,6 +6277,10 @@ packages: resolution: {integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==} engines: {node: '>= 0.8.0'} + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + terser-webpack-plugin@5.6.1: resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==} engines: {node: '>= 10.13.0'} @@ -6144,6 +6353,9 @@ packages: tiny-case@1.0.3: resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -6163,6 +6375,10 @@ packages: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} + engines: {node: '>=14.14'} + to-arraybuffer@1.0.1: resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} @@ -6195,6 +6411,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -6329,10 +6548,16 @@ packages: resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} engines: {node: '>=4'} + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + unicode-property-aliases-ecmascript@2.2.0: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + universal-router@9.2.1: resolution: {integrity: sha512-i3XDhyfg0znwCu2Ue1zwWIWgfed+XYDqlUYXgriGnS58tzO3RgKHe4KWtq9HLQOl/Tknnh0C6jEH0+FLTrrBiQ==} @@ -6348,6 +6573,9 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -6394,6 +6622,11 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -6588,6 +6821,9 @@ packages: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -6645,6 +6881,10 @@ packages: yup@1.7.1: resolution: {integrity: sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==} + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + zrender@6.1.0: resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==} @@ -7873,6 +8113,25 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@fast-csv/format@4.3.5': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.isboolean: 3.0.3 + lodash.isequal: 4.5.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + + '@fast-csv/parse@4.3.6': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + '@hapi/hoek@9.3.0': {} '@hapi/topo@5.1.0': @@ -7999,7 +8258,7 @@ snapshots: bullmq: 5.79.2 tslib: 2.8.1 - '@nestjs/cli@10.4.9(@swc/core@1.3.96)': + '@nestjs/cli@10.4.9': dependencies: '@angular-devkit/core': 17.3.11(chokidar@3.6.0) '@angular-devkit/schematics': 17.3.11(chokidar@3.6.0) @@ -8009,7 +8268,7 @@ snapshots: chokidar: 3.6.0 cli-table3: 0.6.5 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.3.96)) + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1) glob: 10.4.5 inquirer: 8.2.6 node-emoji: 1.11.0 @@ -8018,10 +8277,8 @@ snapshots: tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 typescript: 5.7.2 - webpack: 5.97.1(@swc/core@1.3.96) + webpack: 5.97.1 webpack-node-externals: 3.0.0 - optionalDependencies: - '@swc/core': 1.3.96 transitivePeerDependencies: - '@minify-html/node' - '@swc/css' @@ -8120,6 +8377,10 @@ snapshots: transitivePeerDependencies: - chokidar + '@noble/ciphers@1.3.0': {} + + '@noble/hashes@1.8.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8564,7 +8825,7 @@ snapshots: '@swc/core-win32-x64-msvc@1.3.96': optional: true - '@swc/core@1.3.96': + '@swc/core@1.3.96(@swc/helpers@0.5.23)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.27 @@ -8579,12 +8840,17 @@ snapshots: '@swc/core-win32-arm64-msvc': 1.3.96 '@swc/core-win32-ia32-msvc': 1.3.96 '@swc/core-win32-x64-msvc': 1.3.96 + '@swc/helpers': 0.5.23 '@swc/counter@0.1.3': {} - '@swc/register@0.1.10(@swc/core@1.3.96)': + '@swc/helpers@0.5.23': dependencies: - '@swc/core': 1.3.96 + tslib: 2.8.1 + + '@swc/register@0.1.10(@swc/core@1.3.96(@swc/helpers@0.5.23))': + dependencies: + '@swc/core': 1.3.96(@swc/helpers@0.5.23) lodash.clonedeep: 4.5.0 pirates: 4.0.7 source-map-support: 0.5.21 @@ -8627,12 +8893,12 @@ snapshots: '@tarojs/binding-linux-x64-gnu': 4.2.0 '@tarojs/binding-win32-x64-msvc': 4.2.0 - '@tarojs/cli@4.2.0': + '@tarojs/cli@4.2.0(@swc/helpers@0.5.23)': dependencies: '@tarojs/binding': 4.2.0 - '@tarojs/helper': 4.2.0 + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) '@tarojs/plugin-doctor': 0.0.13 - '@tarojs/service': 4.2.0 + '@tarojs/service': 4.2.0(@swc/helpers@0.5.23) '@tarojs/shared': 4.2.0 adm-zip: 0.5.18 axios: 1.18.1 @@ -8650,12 +8916,12 @@ snapshots: - debug - supports-color - '@tarojs/components-react@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))': + '@tarojs/components-react@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))': dependencies: '@babel/runtime': 7.29.7 - '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) '@tarojs/shared': 4.2.0 - '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) classnames: 2.5.1 identity-obj-proxy: 3.0.0 react: 18.3.1 @@ -8673,12 +8939,12 @@ snapshots: - webpack-chain - webpack-dev-server - '@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))': + '@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))': dependencies: '@stencil/core': 2.22.3 '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 - '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) classnames: 2.5.1 hammerjs: 2.0.8 hls.js: 1.6.16 @@ -8696,15 +8962,15 @@ snapshots: - webpack-chain - webpack-dev-server - '@tarojs/helper@4.2.0': + '@tarojs/helper@4.2.0(@swc/helpers@0.5.23)': dependencies: '@babel/core': 7.29.7 '@babel/generator': 7.29.7 '@babel/parser': 7.29.7 '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 - '@swc/core': 1.3.96 - '@swc/register': 0.1.10(@swc/core@1.3.96) + '@swc/core': 1.3.96(@swc/helpers@0.5.23) + '@swc/register': 0.1.10(@swc/core@1.3.96(@swc/helpers@0.5.23)) ansi-escapes: 4.3.2 chalk: 4.1.2 chokidar: 3.6.0 @@ -8818,9 +9084,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@tarojs/plugin-framework-react@4.2.0(@tarojs/helper@4.2.0)(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))': + '@tarojs/plugin-framework-react@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))': dependencies: - '@tarojs/helper': 4.2.0 + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 acorn: 8.17.0 @@ -8831,18 +9097,18 @@ snapshots: '@vitejs/plugin-react': 4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)) react: 18.3.1 vite: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0) - webpack: 5.97.1(@swc/core@1.3.96)(postcss@8.5.15) + webpack: 5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15) - '@tarojs/plugin-platform-h5@4.2.0(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))': + '@tarojs/plugin-platform-h5@4.2.0(@swc/helpers@0.5.23)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))': dependencies: '@babel/core': 7.29.7 - '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) - '@tarojs/components-react': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) - '@tarojs/helper': 4.2.0 + '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) + '@tarojs/components-react': 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) '@tarojs/runtime': 4.2.0 - '@tarojs/service': 4.2.0 + '@tarojs/service': 4.2.0(@swc/helpers@0.5.23) '@tarojs/shared': 4.2.0 - '@tarojs/taro-h5': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))) + '@tarojs/taro-h5': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))) babel-plugin-transform-taroapi: 4.2.0(@babel/core@7.29.7) change-case: 4.1.2 lodash-es: 4.17.21 @@ -8862,9 +9128,9 @@ snapshots: - webpack-chain - webpack-dev-server - '@tarojs/plugin-platform-weapp@4.2.0(@tarojs/service@4.2.0)(@tarojs/shared@4.2.0)': + '@tarojs/plugin-platform-weapp@4.2.0(@tarojs/service@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)': dependencies: - '@tarojs/service': 4.2.0 + '@tarojs/service': 4.2.0(@swc/helpers@0.5.23) '@tarojs/shared': 4.2.0 '@tarojs/react@4.2.0(react@18.3.1)': @@ -8874,11 +9140,11 @@ snapshots: react: 18.3.1 react-reconciler: 0.29.0(react@18.3.1) - '@tarojs/router@4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))': + '@tarojs/router@4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))': dependencies: '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 - '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) dingtalk-jsapi: 2.15.6 history: 5.3.0 mobile-detect: 1.4.5 @@ -8886,9 +9152,9 @@ snapshots: tslib: 2.8.1 universal-router: 9.2.1 - '@tarojs/runner-utils@4.2.0': + '@tarojs/runner-utils@4.2.0(@swc/helpers@0.5.23)': dependencies: - '@tarojs/helper': 4.2.0 + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) rollup: 3.30.0 scss-bundle: 3.1.2 transitivePeerDependencies: @@ -8900,10 +9166,10 @@ snapshots: '@tarojs/shared': 4.2.0 tslib: 2.8.1 - '@tarojs/service@4.2.0': + '@tarojs/service@4.2.0(@swc/helpers@0.5.23)': dependencies: - '@tarojs/helper': 4.2.0 - '@tarojs/runner-utils': 4.2.0 + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) + '@tarojs/runner-utils': 4.2.0(@swc/helpers@0.5.23) '@tarojs/shared': 4.2.0 joi: 17.13.4 lodash: 4.18.1 @@ -8917,11 +9183,11 @@ snapshots: '@tarojs/shared@4.2.0': {} - '@tarojs/taro-h5@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))': + '@tarojs/taro-h5@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))': dependencies: '@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0) - '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) - '@tarojs/router': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))) + '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) + '@tarojs/router': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))) '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 abortcontroller-polyfill: 1.7.8 @@ -8937,11 +9203,11 @@ snapshots: transitivePeerDependencies: - '@tarojs/taro' - '@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))': + '@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15))': dependencies: '@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0) - '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) - '@tarojs/helper': 4.2.0 + '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 '@types/postcss-url': 10.0.4 @@ -8949,9 +9215,9 @@ snapshots: optionalDependencies: '@types/react': 18.3.31 rollup: 3.30.0 - webpack: 5.97.1(@swc/core@1.3.96)(postcss@8.5.15) + webpack: 5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15) - '@tarojs/vite-runner@4.2.0(@tarojs/runtime@4.2.0)(@types/babel__core@7.20.5)(postcss@8.5.15)(rollup@3.30.0)(terser@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))': + '@tarojs/vite-runner@4.2.0(@swc/helpers@0.5.23)(@tarojs/runtime@4.2.0)(@types/babel__core@7.20.5)(postcss@8.5.15)(rollup@3.30.0)(terser@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))': dependencies: '@ampproject/remapping': 2.3.0 '@babel/core': 7.29.7 @@ -8959,9 +9225,9 @@ snapshots: '@rollup/plugin-inject': 5.0.5(rollup@3.30.0) '@rollup/plugin-terser': 0.4.4(rollup@3.30.0) '@rollup/pluginutils': 5.4.0(rollup@3.30.0) - '@tarojs/helper': 4.2.0 + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) '@tarojs/parse-css-to-stylesheet': 0.0.69 - '@tarojs/runner-utils': 4.2.0 + '@tarojs/runner-utils': 4.2.0(@swc/helpers@0.5.23) '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 '@vitejs/plugin-legacy': 4.1.1(terser@5.48.0)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)) @@ -9124,6 +9390,8 @@ snapshots: '@types/node@12.20.55': {} + '@types/node@14.18.63': {} + '@types/node@20.19.43': dependencies: undici-types: 6.21.0 @@ -9132,6 +9400,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/pdfkit@0.17.6': + dependencies: + '@types/node': 20.19.43 + '@types/postcss-url@10.0.4': dependencies: '@types/node': 20.19.43 @@ -9611,6 +9883,42 @@ snapshots: dependencies: file-type: 4.4.0 + archiver-utils@2.1.0: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + + archiver-utils@3.0.4: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@5.3.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + archy@1.0.0: {} arg@4.1.3: {} @@ -9629,6 +9937,8 @@ snapshots: astring@1.9.0: {} + async@3.2.6: {} + asynckit@0.4.0: {} autoprefixer@10.5.2(postcss@8.5.15): @@ -9717,7 +10027,7 @@ snapshots: '@babel/core': 7.29.7 lodash: 4.18.1 - babel-preset-taro@4.2.0(@babel/core@7.29.7)(@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7))(@babel/preset-react@7.29.7(@babel/core@7.29.7)): + babel-preset-taro@4.2.0(@babel/core@7.29.7)(@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7))(@babel/preset-react@7.29.7(@babel/core@7.29.7))(@swc/helpers@0.5.23): dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 @@ -9729,7 +10039,7 @@ snapshots: '@babel/runtime': 7.29.7 '@babel/runtime-corejs3': 7.29.7 '@rnx-kit/babel-preset-metro-react-native': 1.1.8(@babel/core@7.29.7)(@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7) - '@tarojs/helper': 4.2.0 + '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) babel-plugin-dynamic-import-node: 2.3.3 babel-plugin-transform-imports-api: 1.0.0 babel-plugin-transform-solid-jsx: 4.2.0(@babel/core@7.29.7) @@ -9749,12 +10059,21 @@ snapshots: balanced-match@4.0.4: {} + base64-js@0.0.8: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.10.40: {} + big-integer@1.6.52: {} + binary-extensions@2.3.0: {} + binary@0.3.0: + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + bl@1.2.3: dependencies: readable-stream: 2.3.8 @@ -9766,6 +10085,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + bluebird@3.4.7: {} + body-parser@1.20.4: dependencies: bytes: 3.1.2 @@ -9802,6 +10123,14 @@ snapshots: dependencies: fill-range: 7.1.1 + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + browserslist@4.28.4: dependencies: baseline-browser-mapping: 2.10.40 @@ -9825,11 +10154,15 @@ snapshots: buffer-from@1.1.2: {} + buffer-indexof-polyfill@1.0.2: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + buffers@0.1.1: {} + builtin-status-codes@3.0.0: {} bullmq@5.79.2: @@ -9935,6 +10268,10 @@ snapshots: pathval: 1.1.1 type-detect: 4.1.0 + chainsaw@0.1.0: + dependencies: + traverse: 0.3.9 + chalk@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -10057,6 +10394,8 @@ snapshots: clone@1.0.4: {} + clone@2.1.2: {} + cluster-key-slot@1.1.1: {} cluster-key-slot@1.1.2: {} @@ -10085,6 +10424,13 @@ snapshots: has-own-prop: 2.0.0 repeat-string: 1.6.1 + compress-commons@4.1.2: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + compute-scroll-into-view@3.1.1: {} concat-map@0.0.1: {} @@ -10164,6 +10510,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 + crc-32@1.2.2: {} + + crc32-stream@4.0.3: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + create-require@1.1.1: {} cron-parser@4.9.0: @@ -10295,6 +10648,8 @@ snapshots: detect-libc@2.1.2: optional: true + dfa@1.2.0: {} + diff-sequences@29.6.3: {} diff@4.0.4: {} @@ -10357,6 +10712,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + duplexer3@0.1.5: {} eastasianwidth@0.2.0: {} @@ -10607,6 +10966,18 @@ snapshots: events@3.3.0: {} + exceljs@4.4.0: + dependencies: + archiver: 5.3.2 + dayjs: 1.11.21 + fast-csv: 4.3.6 + jszip: 3.10.1 + readable-stream: 3.6.2 + saxes: 5.0.1 + tmp: 0.2.7 + unzipper: 0.10.14 + uuid: 8.3.2 + execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -10676,6 +11047,11 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 + fast-csv@4.3.6: + dependencies: + '@fast-csv/format': 4.3.5 + '@fast-csv/parse': 4.3.6 + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -10808,6 +11184,18 @@ snapshots: follow-redirects@1.16.0: {} + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.23 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -10817,7 +11205,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.3.96)): + fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1): dependencies: '@babel/code-frame': 7.29.7 chalk: 4.1.2 @@ -10832,7 +11220,7 @@ snapshots: semver: 7.8.5 tapable: 2.3.3 typescript: 5.7.2 - webpack: 5.97.1(@swc/core@1.3.96) + webpack: 5.97.1 form-data@4.0.6: dependencies: @@ -10887,6 +11275,13 @@ snapshots: fsevents@2.3.3: optional: true + fstream@1.0.12: + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + function-bind@1.1.2: {} generic-names@4.0.0: @@ -11180,6 +11575,8 @@ snapshots: ignore@7.0.5: {} + immediate@3.0.6: {} + immutable@5.1.9: {} import-fresh@3.3.1: @@ -11386,6 +11783,8 @@ snapshots: js-base64@2.6.4: {} + js-md5@0.8.3: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -11447,6 +11846,13 @@ snapshots: jstoxml@2.2.9: {} + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + jwa@1.4.2: dependencies: buffer-equal-constant-time: 1.0.1 @@ -11486,6 +11892,10 @@ snapshots: dependencies: package-json: 6.5.0 + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -11493,10 +11903,21 @@ snapshots: libphonenumber-js@1.13.7: {} + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lilconfig@3.1.3: {} + linebreak@1.1.0: + dependencies: + base64-js: 0.0.8 + unicode-trie: 2.0.0 + lines-and-columns@1.2.4: {} + listenercount@1.0.1: {} + loader-runner@4.3.2: {} loader-utils@3.3.1: {} @@ -11524,26 +11945,46 @@ snapshots: lodash.defaults@4.2.0: {} + lodash.difference@4.5.0: {} + + lodash.escaperegexp@4.1.2: {} + + lodash.flatten@4.4.0: {} + + lodash.groupby@4.6.0: {} + lodash.includes@4.3.0: {} lodash.isarguments@3.1.0: {} lodash.isboolean@3.0.3: {} + lodash.isequal@4.5.0: {} + + lodash.isfunction@3.0.9: {} + lodash.isinteger@4.0.4: {} + lodash.isnil@4.0.0: {} + lodash.isnumber@3.0.3: {} lodash.isplainobject@4.0.6: {} lodash.isstring@4.0.1: {} + lodash.isundefined@3.0.1: {} + lodash.merge@4.6.2: {} lodash.once@4.1.1: {} lodash.truncate@4.4.2: {} + lodash.union@4.6.0: {} + + lodash.uniq@4.5.0: {} + lodash@4.17.21: {} lodash@4.18.1: {} @@ -11652,6 +12093,10 @@ snapshots: dependencies: brace-expansion: 1.1.15 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.1 + minimatch@9.0.9: dependencies: brace-expansion: 2.1.1 @@ -11893,6 +12338,10 @@ snapshots: registry-url: 5.1.0 semver: 6.3.1 + pako@0.2.9: {} + + pako@1.0.11: {} + param-case@2.1.1: dependencies: no-case: 2.3.2 @@ -11966,6 +12415,15 @@ snapshots: dependencies: through: 2.3.8 + pdfkit@0.19.1: + dependencies: + '@noble/ciphers': 1.3.0 + '@noble/hashes': 1.8.0 + fontkit: 2.0.4 + js-md5: 0.8.3 + linebreak: 1.1.0 + png-js: 1.1.0 + pend@1.2.0: {} picocolors@1.1.1: {} @@ -11998,6 +12456,10 @@ snapshots: pluralize@8.0.0: {} + png-js@1.1.0: + dependencies: + browserify-zlib: 0.2.0 + pngjs@5.0.0: {} possible-typed-array-names@1.1.0: {} @@ -12548,6 +13010,10 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + readdirp@3.6.0: dependencies: picomatch: 2.3.2 @@ -12636,8 +13102,14 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + restructure@3.0.2: {} + reusify@1.1.0: {} + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -12713,6 +13185,10 @@ snapshots: sax@1.6.0: {} + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -12825,6 +13301,8 @@ snapshots: gopd: 1.2.0 has-property-descriptors: 1.0.2 + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} shallow-clone@3.0.1: @@ -13091,27 +13569,33 @@ snapshots: to-buffer: 1.2.2 xtend: 4.0.2 - terser-webpack-plugin@5.6.1(@swc/core@1.3.96)(postcss@8.5.15)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)): + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + terser-webpack-plugin@5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.48.0 - webpack: 5.97.1(@swc/core@1.3.96)(postcss@8.5.15) + webpack: 5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15) optionalDependencies: - '@swc/core': 1.3.96 + '@swc/core': 1.3.96(@swc/helpers@0.5.23) postcss: 8.5.15 optional: true - terser-webpack-plugin@5.6.1(@swc/core@1.3.96)(webpack@5.97.1(@swc/core@1.3.96)): + terser-webpack-plugin@5.6.1(webpack@5.97.1): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.48.0 - webpack: 5.97.1(@swc/core@1.3.96) - optionalDependencies: - '@swc/core': 1.3.96 + webpack: 5.97.1 terser@5.48.0: dependencies: @@ -13138,6 +13622,8 @@ snapshots: tiny-case@1.0.3: {} + tiny-inflate@1.0.3: {} + tinybench@2.9.0: {} tinyglobby@0.2.17: @@ -13153,6 +13639,8 @@ snapshots: dependencies: os-tmpdir: 1.0.2 + tmp@0.2.7: {} + to-arraybuffer@1.0.1: {} to-buffer@1.2.2: @@ -13181,6 +13669,8 @@ snapshots: tr46@0.0.3: {} + traverse@0.3.9: {} + tree-kill@1.2.2: {} trim-repeated@1.0.0: @@ -13191,7 +13681,7 @@ snapshots: dependencies: typescript: 5.9.3 - ts-node@10.9.2(@swc/core@1.3.96)(@types/node@20.19.43)(typescript@5.9.3): + ts-node@10.9.2(@types/node@20.19.43)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 @@ -13208,8 +13698,6 @@ snapshots: typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.3.96 tsconfig-paths-webpack-plugin@4.2.0: dependencies: @@ -13304,8 +13792,18 @@ snapshots: unicode-match-property-value-ecmascript@2.2.1: {} + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + unicode-property-aliases-ecmascript@2.2.0: {} + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + universal-router@9.2.1: dependencies: path-to-regexp: 6.3.0 @@ -13316,6 +13814,19 @@ snapshots: unpipe@1.0.0: {} + unzipper@0.10.14: + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + update-browserslist-db@1.2.3(browserslist@4.28.4): dependencies: browserslist: 4.28.4 @@ -13369,6 +13880,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@8.3.2: {} + v8-compile-cache-lib@3.0.1: {} validate-html-nesting@1.2.4: {} @@ -13470,7 +13983,7 @@ snapshots: webpack-sources@3.5.0: {} - webpack@5.97.1(@swc/core@1.3.96): + webpack@5.97.1: dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.9 @@ -13492,7 +14005,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.3.3 - terser-webpack-plugin: 5.6.1(@swc/core@1.3.96)(webpack@5.97.1(@swc/core@1.3.96)) + terser-webpack-plugin: 5.6.1(webpack@5.97.1) watchpack: 2.5.2 webpack-sources: 3.5.0 transitivePeerDependencies: @@ -13509,7 +14022,7 @@ snapshots: - postcss - uglify-js - webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15): + webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.9 @@ -13531,7 +14044,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.3.3 - terser-webpack-plugin: 5.6.1(@swc/core@1.3.96)(postcss@8.5.15)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)) + terser-webpack-plugin: 5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)(webpack@5.97.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(postcss@8.5.15)) watchpack: 2.5.2 webpack-sources: 3.5.0 transitivePeerDependencies: @@ -13623,6 +14136,8 @@ snapshots: xmlbuilder@11.0.1: {} + xmlchars@2.2.0: {} + xtend@4.0.2: {} y18n@4.0.3: {} @@ -13684,6 +14199,12 @@ snapshots: toposort: 2.0.2 type-fest: 2.19.0 + zip-stream@4.1.1: + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + zrender@6.1.0: dependencies: tslib: 2.3.0 diff --git a/server/dukang-api/assets/fonts/README.md b/server/dukang-api/assets/fonts/README.md new file mode 100644 index 0000000..dca90d3 --- /dev/null +++ b/server/dukang-api/assets/fonts/README.md @@ -0,0 +1,12 @@ +# PDF 导出字体 + +订单 PDF 导出需要支持中文的 TTF/OTF/TTC 字体。按优先级尝试: + +1. 环境变量 `EXPORT_PDF_FONT_PATH` +2. `assets/fonts/NotoSansSC-Regular.otf`(推荐,SIL OFL) +3. `assets/fonts/simhei.ttf` +4. `assets/fonts/msyh.ttc` +5. Linux 系统字体(如 `fonts-noto-cjk`) +6. Windows 系统字体(开发机) + +本地开发可将 `C:\Windows\Fonts\simhei.ttf` 复制到本目录。 diff --git a/server/dukang-api/nest-cli.json b/server/dukang-api/nest-cli.json index f9aa683..3ec5390 100644 --- a/server/dukang-api/nest-cli.json +++ b/server/dukang-api/nest-cli.json @@ -3,6 +3,7 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { - "deleteOutDir": true + "deleteOutDir": true, + "assets": [{ "include": "../assets/**/*", "outDir": "dist", "watchAssets": true }] } } diff --git a/server/dukang-api/package.json b/server/dukang-api/package.json index 149cbc2..fd7674d 100644 --- a/server/dukang-api/package.json +++ b/server/dukang-api/package.json @@ -21,7 +21,8 @@ "prisma:seed-wecom-push": "ts-node --transpile-only prisma/seed-wecom-message-push.ts", "prisma:migrate-wecom-push": "ts-node --transpile-only prisma/migrate-wecom-message-push.ts", "prisma:upsert-super-admin": "ts-node --transpile-only scripts/upsert-super-admin.ts", - "prisma:sync-benefit": "ts-node --transpile-only prisma/sync-benefit-to-price.ts" + "prisma:sync-benefit": "ts-node --transpile-only prisma/sync-benefit-to-price.ts", + "prisma:merge-spu-dk000007-008": "ts-node --transpile-only prisma/merge-spu-dk000007-008.ts" }, "dependencies": { "@alicloud/dysmsapi20170525": "^4.6.0", @@ -43,9 +44,11 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^17.4.2", + "exceljs": "^4.4.0", "express": "^4.21.0", "ioredis": "^5.4.1", "ip2region": "^2.3.0", + "pdfkit": "^0.19.1", "qrcode": "^1.5.4", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" @@ -57,6 +60,7 @@ "@types/express": "^4.17.21", "@types/multer": "^2.1.0", "@types/node": "^20.14.0", + "@types/pdfkit": "^0.17.6", "@types/qrcode": "^1.5.6", "prisma": "^5.18.0", "ts-node": "^10.9.2", diff --git a/server/dukang-api/prisma/merge-spu-dk000007-008.ts b/server/dukang-api/prisma/merge-spu-dk000007-008.ts new file mode 100644 index 0000000..c79b7ff --- /dev/null +++ b/server/dukang-api/prisma/merge-spu-dk000007-008.ts @@ -0,0 +1,352 @@ +/** + * 方案 A:把 DK000007 / DK000008 合并为同一 SPU,保留原 SKU id 与 DK 码,订单改绑到对应 SKU。 + * + * 用法:cd server/dukang-api && npx ts-node --transpile-only prisma/merge-spu-dk000007-008.ts + * 预览:npx ts-node --transpile-only prisma/merge-spu-dk000007-008.ts --dry-run + * + * 幸存 SPU = DK000007 所在商品;默认规格 = 国标特级20。 + */ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +const CODES = ['DK000007', 'DK000008'] as const; +const SURVIVOR_CODE = 'DK000007'; +const SPEC_ATTR_NAME = '规格'; +const SPU_NAME = '酒祖杜康(国标特级)'; +const SPEC_LABEL: Record<(typeof CODES)[number], string> = { + DK000007: '国标特级20', + DK000008: '国标特级30', +}; + +const dryRun = process.argv.includes('--dry-run'); + +function buildSpecKey(valueIds: bigint[]): string { + if (!valueIds.length) return ''; + return [...valueIds] + .map((id) => id.toString()) + .sort((a, b) => (BigInt(a) < BigInt(b) ? -1 : BigInt(a) > BigInt(b) ? 1 : 0)) + .join('_'); +} + +async function ensureSkuImageColumn() { + const rows = await prisma.$queryRaw>` + SELECT COUNT(*) AS cnt + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'common_product_sku' + AND COLUMN_NAME = 'image_url' + `; + const cnt = Number(rows[0]?.cnt ?? 0); + if (cnt > 0) return; + await prisma.$executeRawUnsafe( + 'ALTER TABLE `common_product_sku` ADD COLUMN `image_url` VARCHAR(512) NULL AFTER `sort_order`', + ); + console.log('added column common_product_sku.image_url'); +} + +async function resolveProductByCode(skuCode: string) { + const sku = await prisma.commonProductSku.findUnique({ + where: { skuCode }, + include: { product: true }, + }); + if (sku) return { product: sku.product, sku }; + const product = await prisma.commonProductItem.findFirst({ + where: { skuCode }, + }); + if (!product) { + throw new Error(`找不到商品/SKU:${skuCode}`); + } + return { product, sku: null as null }; +} + +async function coverUrlOf(productId: bigint, coverResourceId: bigint | null) { + if (coverResourceId) { + const res = await prisma.commonResource.findUnique({ + where: { id: coverResourceId }, + select: { url: true }, + }); + if (res?.url) return res.url; + } + const fallback = await prisma.commonResource.findFirst({ + where: { ownerType: 'PRODUCT', ownerId: productId, bizType: 'COVER', status: 'ACTIVE' }, + orderBy: { id: 'asc' }, + select: { url: true }, + }); + return fallback?.url ?? null; +} + +async function ensureSkuFromProduct( + tx: PrismaClient, + product: { + id: bigint; + skuCode: string; + barcode69: string; + spec: string; + price: unknown; + benefitAmount: unknown; + status: 'DRAFT' | 'ON_SALE' | 'OFF_SALE'; + allowOnSitePickup: boolean; + allowOnlinePurchase: boolean; + allowCrossCityDelivery: boolean; + }, +) { + const existing = + (await tx.commonProductSku.findUnique({ where: { skuCode: product.skuCode } })) ?? + (await tx.commonProductSku.findFirst({ + where: { productId: product.id, isDefault: true }, + })); + if (existing) return existing; + return tx.commonProductSku.create({ + data: { + productId: product.id, + skuCode: product.skuCode, + barcode69: product.barcode69, + specKey: '', + specText: product.spec, + price: product.price as never, + benefitAmount: product.benefitAmount as never, + status: product.status, + allowOnSitePickup: product.allowOnSitePickup, + allowOnlinePurchase: product.allowOnlinePurchase, + allowCrossCityDelivery: product.allowCrossCityDelivery, + saleUnit: 'BOTTLE', + bottlesPerUnit: 1, + isDefault: true, + sortOrder: 0, + }, + }); +} + +async function main() { + await ensureSkuImageColumn(); + + const rows = []; + for (const code of CODES) { + const resolved = await resolveProductByCode(code); + rows.push({ code, ...resolved }); + } + + const survivorRow = rows.find((r) => r.code === SURVIVOR_CODE); + if (!survivorRow) throw new Error(`缺少幸存码 ${SURVIVOR_CODE}`); + const discardRows = rows.filter((r) => r.product.id !== survivorRow.product.id); + const survivorId = survivorRow.product.id; + + console.log( + JSON.stringify( + { + dryRun, + survivor: { + productId: survivorId.toString(), + name: survivorRow.product.name, + skuCode: survivorRow.product.skuCode, + }, + items: await Promise.all( + rows.map(async (r) => { + const orderCount = await prisma.order.count({ where: { productId: r.product.id } }); + const nullSku = await prisma.order.count({ + where: { productId: r.product.id, skuId: null }, + }); + return { + code: r.code, + productId: r.product.id.toString(), + name: r.product.name, + skuId: r.sku?.id.toString() ?? null, + orders: orderCount, + nullSkuOrders: nullSku, + }; + }), + ), + }, + null, + 2, + ), + ); + + if (discardRows.length === 0 && survivorRow.sku) { + const sibling = await prisma.commonProductSku.findMany({ + where: { productId: survivorId, skuCode: { in: [...CODES] } }, + }); + if (sibling.length === CODES.length) { + console.log('SKU 已在同一 SPU 上,继续核对订单 product_id / sku_id'); + } + } + + if (dryRun) { + console.log('[dry-run] skip writes'); + return; + } + + await prisma.$transaction( + async (tx) => { + const db = tx as unknown as PrismaClient; + const skuByCode = new Map(); + + for (const row of rows) { + const sku = await ensureSkuFromProduct(db, row.product); + skuByCode.set(row.code, sku); + console.log(`ensured sku ${row.code} id=${sku.id} fromProduct=${row.product.id}`); + } + + let attr = await db.commonProductSpecAttr.findFirst({ + where: { productId: survivorId, name: SPEC_ATTR_NAME }, + }); + if (!attr) { + attr = await db.commonProductSpecAttr.create({ + data: { productId: survivorId, name: SPEC_ATTR_NAME, sortOrder: 0 }, + }); + } + + const valueIdByCode = new Map(); + for (let i = 0; i < CODES.length; i++) { + const code = CODES[i]; + const label = SPEC_LABEL[code]; + let value = await db.commonProductSpecValue.findFirst({ + where: { attrId: attr.id, name: label }, + }); + if (!value) { + value = await db.commonProductSpecValue.create({ + data: { attrId: attr.id, name: label, sortOrder: i }, + }); + } + valueIdByCode.set(code, value.id); + } + + for (let i = 0; i < CODES.length; i++) { + const code = CODES[i]; + const sku = skuByCode.get(code)!; + const valueId = valueIdByCode.get(code)!; + const specKey = buildSpecKey([valueId]); + const specText = SPEC_LABEL[code]; + const sourceProduct = rows.find((r) => r.code === code)!.product; + const imageUrl = await coverUrlOf(sourceProduct.id, sourceProduct.coverResourceId); + const isDefault = code === SURVIVOR_CODE; + + await db.commonProductSkuSpec.deleteMany({ where: { skuId: sku.id } }); + await db.commonProductSku.update({ + where: { id: sku.id }, + data: { + product: { connect: { id: survivorId } }, + specKey, + specText, + isDefault, + sortOrder: i, + imageUrl, + status: sourceProduct.status, + }, + }); + await db.commonProductSkuSpec.create({ + data: { skuId: sku.id, valueId }, + }); + + const orderRes = await db.order.updateMany({ + where: { + OR: [{ skuId: sku.id }, { productId: sourceProduct.id, skuId: null }], + }, + data: { productId: survivorId, skuId: sku.id }, + }); + console.log( + `bound ${code} → product ${survivorId} sku ${sku.id} spec=${specText} orders=${orderRes.count}`, + ); + } + + const leftover = await db.order.findMany({ + where: { productId: { in: discardRows.map((r) => r.product.id) } }, + select: { id: true, orderNo: true, productId: true, skuId: true }, + }); + if (leftover.length) { + throw new Error( + `仍有订单绑在被合并 SPU 上:${leftover.map((o) => o.orderNo).join(', ')}`, + ); + } + + for (const discard of discardRows) { + const phones = await db.commonProductVisibilityPhone.findMany({ + where: { productId: discard.product.id }, + }); + for (const p of phones) { + await db.commonProductVisibilityPhone.upsert({ + where: { productId_phone: { productId: survivorId, phone: p.phone } }, + create: { productId: survivorId, phone: p.phone }, + update: {}, + }); + } + await db.commonProductVisibilityPhone.deleteMany({ + where: { productId: discard.product.id }, + }); + await db.commonProductSkuSpec.deleteMany({ + where: { sku: { productId: discard.product.id } }, + }); + await db.commonProductSku.deleteMany({ where: { productId: discard.product.id } }); + await db.commonProductSpecAttr.deleteMany({ where: { productId: discard.product.id } }); + await db.commonResource.deleteMany({ + where: { ownerType: 'PRODUCT', ownerId: discard.product.id }, + }); + await db.commonProductItem.update({ + where: { id: discard.product.id }, + data: { status: 'OFF_SALE', coverResourceId: null }, + }); + await db.commonProductItem.delete({ where: { id: discard.product.id } }); + console.log(`deleted discard SPU ${discard.product.id} ${discard.code}`); + } + + const defaultSku = await db.commonProductSku.findUniqueOrThrow({ + where: { skuCode: SURVIVOR_CODE }, + }); + await db.commonProductItem.update({ + where: { id: survivorId }, + data: { + name: SPU_NAME, + skuCode: defaultSku.skuCode, + barcode69: defaultSku.barcode69, + spec: defaultSku.specText, + price: defaultSku.price, + benefitAmount: defaultSku.benefitAmount, + allowOnSitePickup: defaultSku.allowOnSitePickup, + allowOnlinePurchase: defaultSku.allowOnlinePurchase, + allowCrossCityDelivery: defaultSku.allowCrossCityDelivery, + status: 'ON_SALE', + }, + }); + }, + { timeout: 30000 }, + ); + + const skus = await prisma.commonProductSku.findMany({ + where: { skuCode: { in: [...CODES] } }, + orderBy: { sortOrder: 'asc' }, + }); + const orders = await prisma.order.groupBy({ + by: ['productId', 'skuId'], + where: { sku: { skuCode: { in: [...CODES] } } }, + _count: true, + }); + console.log( + JSON.stringify( + { + done: true, + skus: skus.map((s) => ({ + id: s.id.toString(), + productId: s.productId.toString(), + skuCode: s.skuCode, + specText: s.specText, + isDefault: s.isDefault, + })), + orders: orders.map((o) => ({ + productId: o.productId.toString(), + skuId: o.skuId?.toString() ?? null, + count: o._count, + })), + }, + null, + 2, + ), + ); +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(() => prisma.$disconnect()); diff --git a/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts b/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts index 018c7b2..5004258 100644 --- a/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts +++ b/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts @@ -24,6 +24,7 @@ export const HqOperationAction = { ORDER_BATCH_DELETE: 'ORDER_BATCH_DELETE', ORDER_DELETE: 'ORDER_DELETE', ORDER_PROXY_CREATE: 'ORDER_PROXY_CREATE', + ORDER_EXPORT: 'ORDER_EXPORT', STORE_CREATE: 'STORE_CREATE', STORE_UPDATE: 'STORE_UPDATE', STORE_STATUS: 'STORE_STATUS', @@ -147,6 +148,7 @@ export const HQ_OPERATION_ACTION_LABELS: Record = { [HqOperationAction.ORDER_BATCH_DELETE]: '批量删除订单', [HqOperationAction.ORDER_DELETE]: '删除订单', [HqOperationAction.ORDER_PROXY_CREATE]: '总部代下单', + [HqOperationAction.ORDER_EXPORT]: '导出订单', [HqOperationAction.STORE_CREATE]: '新增门店', [HqOperationAction.STORE_UPDATE]: '编辑门店', [HqOperationAction.STORE_STATUS]: '变更门店状态', diff --git a/server/dukang-api/src/modules/ops/admin-order-export.util.ts b/server/dukang-api/src/modules/ops/admin-order-export.util.ts new file mode 100644 index 0000000..3c06664 --- /dev/null +++ b/server/dukang-api/src/modules/ops/admin-order-export.util.ts @@ -0,0 +1,211 @@ +import { ORDER_STATUS_LABELS } from '@dukang/shared-types'; +import ExcelJS from 'exceljs'; +import * as fs from 'fs'; +import * as path from 'path'; +import PDFDocument from 'pdfkit'; + +const EXPORT_HEADERS = [ + '订单号', + '下单时间', + '状态', + '商品', + '规格', + '数量', + '实付', + '好客权益', + '收货人', + '手机', + '收货地址', +] as const; + +export type OrderExportRow = { + orderNo: string; + createdAt: string; + status: string; + productName: string; + productSpec: string; + quantity: number; + payAmount: number; + benefitBrief: string; + receiverName: string; + receiverPhone: string; + address: string; +}; + +function formatBenefitBrief(order: { + benefitAmount?: { toString(): string } | number | null; + benefitCoupon?: { + totalAmount?: { toString(): string } | number; + usedAmount?: { toString(): string } | number; + balance?: { toString(): string } | number; + } | null; +}): string { + const coupon = order.benefitCoupon; + if (coupon) { + return `总额¥${Number(coupon.totalAmount).toFixed(2)} / 已用¥${Number(coupon.usedAmount).toFixed(2)} / 余¥${Number(coupon.balance).toFixed(2)}`; + } + if (order.benefitAmount != null) return `¥${Number(order.benefitAmount).toFixed(2)}`; + return ''; +} + +export function formatExportDateTime(value?: Date | null): string { + if (!value) return ''; + return value.toISOString().slice(0, 19).replace('T', ' '); +} + +export function mapOrderToExportRow(order: { + orderNo: string; + createdAt: Date; + status: string; + productName: string; + productSpec: string; + quantity: number; + payAmount: { toString(): string } | number; + benefitAmount?: { toString(): string } | number | null; + receiverName: string; + receiverPhone: string; + receiverProvince: string; + receiverCity: string; + receiverDistrict: string; + receiverAddress: string; + benefitCoupon?: { + totalAmount?: { toString(): string } | number; + usedAmount?: { toString(): string } | number; + balance?: { toString(): string } | number; + } | null; + deliveryType?: string; +}): OrderExportRow { + const isOnSite = + order.deliveryType === 'ON_SITE_PICKUP' || + order.receiverAddress === '现场取货' || + order.receiverAddress === '现场提货' || + (order.receiverProvince === '现场' && order.receiverCity === '现场'); + const address = isOnSite + ? '现场取货' + : `${order.receiverProvince}${order.receiverCity}${order.receiverDistrict}${order.receiverAddress}`; + return { + orderNo: order.orderNo, + createdAt: formatExportDateTime(order.createdAt), + status: ORDER_STATUS_LABELS[order.status] || order.status, + productName: order.productName, + productSpec: order.productSpec, + quantity: order.quantity, + payAmount: Number(order.payAmount), + benefitBrief: formatBenefitBrief(order), + receiverName: order.receiverName, + receiverPhone: order.receiverPhone, + address, + }; +} + +export function rowToCells(row: OrderExportRow): string[] { + return [ + row.orderNo, + row.createdAt, + row.status, + row.productName, + row.productSpec, + String(row.quantity), + row.payAmount.toFixed(2), + row.benefitBrief, + row.receiverName, + row.receiverPhone, + row.address, + ]; +} + +function resolvePdfFontPath(): string { + const candidates = [ + process.env.EXPORT_PDF_FONT_PATH, + path.join(process.cwd(), 'assets', 'fonts', 'NotoSansSC-Regular.otf'), + path.join(process.cwd(), 'assets', 'fonts', 'simhei.ttf'), + path.join(process.cwd(), 'assets', 'fonts', 'msyh.ttc'), + path.join(process.cwd(), 'dist', 'assets', 'fonts', 'simhei.ttf'), + '/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc', + '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', + 'C:\\Windows\\Fonts\\simhei.ttf', + 'C:\\Windows\\Fonts\\msyh.ttc', + ].filter(Boolean) as string[]; + + for (const candidate of candidates) { + if (fs.existsSync(candidate)) return candidate; + } + throw new Error('未找到可用于 PDF 的中文字体,请将字体文件放到 server/dukang-api/assets/fonts/'); +} + +export async function buildOrdersXlsx(rows: OrderExportRow[]): Promise { + const workbook = new ExcelJS.Workbook(); + const sheet = workbook.addWorksheet('订单'); + sheet.addRow([...EXPORT_HEADERS]); + for (const row of rows) { + sheet.addRow(rowToCells(row)); + } + sheet.columns.forEach((col) => { + col.width = 16; + }); + sheet.getColumn(4).width = 22; + sheet.getColumn(8).width = 36; + sheet.getColumn(11).width = 36; + const buffer = await workbook.xlsx.writeBuffer(); + return Buffer.from(buffer); +} + +export async function buildOrdersPdf(rows: OrderExportRow[]): Promise { + const fontPath = resolvePdfFontPath(); + const chunks: Buffer[] = []; + + return new Promise((resolve, reject) => { + const doc = new PDFDocument({ + size: 'A4', + layout: 'landscape', + margin: 24, + bufferPages: true, + }); + doc.on('data', (chunk) => chunks.push(chunk as Buffer)); + doc.on('end', () => resolve(Buffer.concat(chunks))); + doc.on('error', reject); + + doc.registerFont('zh', fontPath); + doc.font('zh'); + + const pageWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right; + const colWidths = [72, 78, 48, 88, 64, 32, 48, 110, 48, 72, 120]; + const scale = pageWidth / colWidths.reduce((sum, w) => sum + w, 0); + const widths = colWidths.map((w) => w * scale); + const rowHeight = 28; + const fontSize = 7; + let y = doc.page.margins.top; + + const drawRow = (cells: string[], isHeader = false) => { + let x = doc.page.margins.left; + const height = isHeader ? 24 : rowHeight; + if (y + height > doc.page.height - doc.page.margins.bottom) { + doc.addPage({ size: 'A4', layout: 'landscape', margin: 24 }); + y = doc.page.margins.top; + } + doc.fontSize(isHeader ? 8 : fontSize); + cells.forEach((cell, index) => { + doc.rect(x, y, widths[index], height).stroke('#dddddd'); + doc.text(cell || '', x + 2, y + 4, { + width: widths[index] - 4, + height: height - 6, + lineBreak: true, + }); + x += widths[index]; + }); + y += height; + }; + + drawRow([...EXPORT_HEADERS], true); + for (const row of rows) { + drawRow(rowToCells(row)); + } + + doc.end(); + }); +} + +export function buildExportFilename(format: 'xlsx' | 'pdf', count: number): string { + const stamp = new Date().toISOString().slice(0, 10); + return `订单导出_${stamp}_${count}条.${format}`; +} diff --git a/server/dukang-api/src/modules/ops/admin-orders.controller.ts b/server/dukang-api/src/modules/ops/admin-orders.controller.ts index e2bd1aa..8ac659c 100644 --- a/server/dukang-api/src/modules/ops/admin-orders.controller.ts +++ b/server/dukang-api/src/modules/ops/admin-orders.controller.ts @@ -8,7 +8,7 @@ import { HqOperation } from '../../common/hq-operation/hq-operation.decorator'; import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants'; import { AdminOrdersService } from './admin-orders.service'; import { AdminShipOrderDto, BatchDeleteOrdersDto, HqLogisticsShipDto, UpdateOrderStatusDto } from './dto/admin-mutate.dto'; -import { AdminOrdersQueryDto } from './dto/admin-query.dto'; +import { AdminOrdersExportDto, AdminOrdersQueryDto } from './dto/admin-query.dto'; @Controller('admin/orders') @UseGuards(HqAuthGuard) @@ -26,6 +26,25 @@ export class AdminOrdersController { return this.ordersService.listBigScreen(limit); } + @Post('export/preview') + @UseGuards(HqPermissionGuard) + @RequireHqPermissions('orders') + previewExport(@Body() dto: AdminOrdersExportDto) { + return this.ordersService.previewExport(dto); + } + + @Post('export') + @UseGuards(HqPermissionGuard) + @RequireHqPermissions('orders') + @HqOperation({ + action: HqOperationAction.ORDER_EXPORT, + refType: 'ORDER', + includeBody: true, + }) + exportOrders(@Body() dto: AdminOrdersExportDto) { + return this.ordersService.exportOrders(dto); + } + @Post('batch-delete') @UseGuards(HqPermissionGuard) @RequireHqPermissions('orders_delete') diff --git a/server/dukang-api/src/modules/ops/admin-orders.service.ts b/server/dukang-api/src/modules/ops/admin-orders.service.ts index d440b94..ea1b62f 100644 --- a/server/dukang-api/src/modules/ops/admin-orders.service.ts +++ b/server/dukang-api/src/modules/ops/admin-orders.service.ts @@ -7,12 +7,35 @@ import { orderStatusLogWhere } from '../../common/event/event.helpers'; import { mapOrderCompat, mapStatusLogCompat } from '../../common/compat/v31-compat'; import { TradeService } from '../trade/trade.service'; import { AdminXiaofeixiaService } from './admin-xiaofeixia.service'; -import type { AdminOrdersQueryDto } from './dto/admin-query.dto'; +import type { AdminOrdersExportDto, AdminOrdersQueryDto } from './dto/admin-query.dto'; import type { AdminShipOrderDto, HqLogisticsShipDto } from './dto/admin-mutate.dto'; import type { XiaofeixiaCreateShipmentDto } from './dto/admin-courier.dto'; import { FulfillmentService } from '../fulfillment/fulfillment.service'; import { FulfillmentProviderService } from '../fulfillment/fulfillment-provider.service'; import { AdminRedeemService } from './admin-redeem.service'; +import { + buildExportFilename, + buildOrdersPdf, + buildOrdersXlsx, + mapOrderToExportRow, +} from './admin-order-export.util'; + +const ORDER_EXPORT_MAX = 5000; + +type OrderFilterInput = Pick< + AdminOrdersQueryDto, + | 'orderNo' + | 'status' + | 'orderType' + | 'userId' + | 'cityId' + | 'receiverPhone' + | 'fulfillmentHold' + | 'createdFrom' + | 'createdTo' + | 'excludeTest' + | 'deliveryType' +>; @Injectable() export class AdminOrdersService { @@ -60,23 +83,7 @@ export class AdminOrdersService { async list(query: AdminOrdersQueryDto) { const page = query.page ?? 1; const pageSize = query.pageSize ?? 20; - const where: Prisma.OrderWhereInput = {}; - - if (query.orderNo) where.orderNo = { contains: query.orderNo }; - if (query.status) where.status = query.status as Prisma.EnumOrderStatusFilter['equals']; - if (query.orderType) where.orderType = query.orderType as Prisma.EnumOrderTypeFilter['equals']; - if (query.userId) where.userId = BigInt(query.userId); - if (query.cityId) where.cityId = BigInt(query.cityId); - if (query.receiverPhone) where.receiverPhone = { contains: query.receiverPhone }; - if (query.fulfillmentHold === true || query.fulfillmentHold === 'true') { - where.fulfillmentHold = true; - } - if (query.createdFrom || query.createdTo) { - where.createdAt = {}; - if (query.createdFrom) where.createdAt.gte = new Date(query.createdFrom); - if (query.createdTo) where.createdAt.lte = new Date(query.createdTo); - } - if (query.excludeTest) where.isTest = false; + const where = this.buildOrderWhere(query); const [items, total] = await Promise.all([ this.prisma.order.findMany({ @@ -89,6 +96,9 @@ export class AdminOrdersService { delivery: { select: { provider: true, trackingNo: true, providerOrderNo: true } }, city: { select: { id: true, name: true, code: true } }, fulfillmentWarehouse: { select: { id: true, name: true } }, + benefitCoupon: { + select: { couponNo: true, totalAmount: true, usedAmount: true, balance: true, status: true }, + }, }, }), this.prisma.order.count({ where }), @@ -97,6 +107,114 @@ export class AdminOrdersService { return serializeBigInt({ items, total, page, pageSize }); } + async previewExport(dto: AdminOrdersExportDto) { + const count = await this.countExportOrders(dto); + return { count, max: ORDER_EXPORT_MAX, exceeds: count > ORDER_EXPORT_MAX }; + } + + async exportOrders(dto: AdminOrdersExportDto) { + const count = await this.countExportOrders(dto); + if (count > ORDER_EXPORT_MAX) { + throw new BadRequestException(`超过 ${ORDER_EXPORT_MAX} 条,请缩小日期或筛选条件`); + } + if (count === 0) { + throw new BadRequestException('没有可导出的订单'); + } + + const orders = await this.prisma.order.findMany({ + where: this.buildExportWhere(dto), + orderBy: { createdAt: 'desc' }, + take: ORDER_EXPORT_MAX, + include: { + city: { select: { name: true } }, + delivery: { select: { trackingNo: true } }, + benefitCoupon: { + select: { totalAmount: true, usedAmount: true, balance: true }, + }, + }, + }); + + const rows = orders.map((order) => mapOrderToExportRow(order)); + const buffer = + dto.format === 'pdf' ? await buildOrdersPdf(rows) : await buildOrdersXlsx(rows); + const filename = buildExportFilename(dto.format, rows.length); + const mimeType = + dto.format === 'pdf' + ? 'application/pdf' + : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; + + return { + filename, + mimeType, + contentBase64: buffer.toString('base64'), + count: rows.length, + }; + } + + private buildOrderWhere(query: OrderFilterInput): Prisma.OrderWhereInput { + const where: Prisma.OrderWhereInput = {}; + + if (query.orderNo) where.orderNo = { contains: query.orderNo }; + if (query.status) where.status = query.status as Prisma.EnumOrderStatusFilter['equals']; + if (query.orderType) where.orderType = query.orderType as Prisma.EnumOrderTypeFilter['equals']; + if (query.userId) where.userId = BigInt(query.userId); + if (query.cityId) where.cityId = BigInt(query.cityId); + if (query.receiverPhone) where.receiverPhone = { contains: query.receiverPhone }; + if (query.deliveryType) { + where.deliveryType = query.deliveryType as Prisma.EnumDeliveryTypeFilter['equals']; + } + if (query.fulfillmentHold === true || query.fulfillmentHold === 'true') { + where.fulfillmentHold = true; + } + if (query.createdFrom || query.createdTo) { + where.createdAt = {}; + if (query.createdFrom) where.createdAt.gte = new Date(query.createdFrom); + if (query.createdTo) where.createdAt.lte = this.endOfDay(query.createdTo); + } + if (query.excludeTest) where.isTest = false; + + return where; + } + + private buildExportWhere(dto: AdminOrdersExportDto): Prisma.OrderWhereInput { + if (dto.scope === 'selected') { + const ids = (dto.ids ?? []).map((id) => BigInt(id)); + if (!ids.length) { + throw new BadRequestException('请先勾选要导出的订单'); + } + return { id: { in: ids } }; + } + + const normalized = this.normalizeExportFilter(dto); + return this.buildOrderWhere(normalized); + } + + private async countExportOrders(dto: AdminOrdersExportDto): Promise { + return this.prisma.order.count({ where: this.buildExportWhere(dto) }); + } + + private normalizeExportFilter(dto: AdminOrdersExportDto): OrderFilterInput { + const createdFrom = dto.createdFrom; + const createdTo = dto.createdTo; + if (!createdFrom && !createdTo) { + const from = new Date(); + from.setDate(from.getDate() - 30); + from.setHours(0, 0, 0, 0); + return { + ...dto, + createdFrom: from.toISOString().slice(0, 10), + createdTo: new Date().toISOString().slice(0, 10), + }; + } + return { ...dto, createdFrom, createdTo }; + } + + private endOfDay(dateStr: string): Date { + const end = new Date(dateStr); + end.setHours(23, 59, 59, 999); + return end; + } + async getOrderTrack(id: bigint) { const order = await this.prisma.order.findUnique({ where: { id }, diff --git a/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts b/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts index 98e19f4..fcfb005 100644 --- a/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts +++ b/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts @@ -1,5 +1,5 @@ import { Type, Transform } from 'class-transformer'; -import { IsBoolean, IsIn, IsInt, IsOptional, IsString, Max, Min } from 'class-validator'; +import { IsArray, IsBoolean, IsIn, IsInt, IsOptional, IsString, Max, Min } from 'class-validator'; function toOptionalBoolean(value: unknown): boolean | undefined { if (value === undefined || value === null || value === '') return undefined; @@ -93,6 +93,66 @@ export class AdminOrdersQueryDto extends PaginationQueryDto { @Transform(({ value }) => toOptionalBoolean(value)) @IsBoolean() excludeTest?: boolean; + + @IsOptional() + @IsIn(['LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP']) + deliveryType?: string; +} + +/** HQ 订单导出(筛选 + 勾选范围) */ +export class AdminOrdersExportDto { + @IsIn(['filter', 'selected']) + scope: 'filter' | 'selected'; + + @IsIn(['xlsx', 'pdf']) + format: 'xlsx' | 'pdf'; + + @IsOptional() + @IsArray() + @IsString({ each: true }) + ids?: string[]; + + @IsOptional() + @IsString() + orderNo?: string; + + @IsOptional() + @IsString() + status?: string; + + @IsOptional() + @IsIn(['NORMAL', 'PROXY', 'RESHIPMENT']) + orderType?: string; + + @IsOptional() + @IsString() + cityId?: string; + + @IsOptional() + @IsString() + receiverPhone?: string; + + @IsOptional() + @Transform(({ value }) => toOptionalBoolean(value)) + @IsBoolean() + fulfillmentHold?: boolean; + + @IsOptional() + @IsString() + createdFrom?: string; + + @IsOptional() + @IsString() + createdTo?: string; + + @IsOptional() + @Transform(({ value }) => toOptionalBoolean(value)) + @IsBoolean() + excludeTest?: boolean; + + @IsOptional() + @IsIn(['LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP']) + deliveryType?: string; } /** 概览页用户/订单 ECharts 聚合筛选 */