WeWaiter

WeWaiter 是一个开源的自助点餐软件。

View project on GitHub

📌 WeWaiter 新后台规划

本文基于已冻结的旧微信小程序、旧 WeWaiter 服务和旧 Manager WinForms 管理端业务认知梳理,目标是把 WeWaiter 从“微信小程序 + 旧管理工具”升级为可独立交付、可部署、可维护的完整餐饮点餐与结算系统。旧后端和旧管理端源码已移除,业务事实以 docs/phase-0-legacy-business-freeze.md 为准。

📌 1. 旧项目现状冻结

📌 1.1 已有前端业务链路

旧小程序是 mpvue 项目,现已迁入 src/WeWaiter.MiniProgram,核心页面和链路如下:

  • pages/index:微信登录、授权、扫码入口。二维码里带 idseatid,分别代表商家和桌台。
  • pages/goods:根据商家和桌台获取商家信息、分类、菜品,并进入点餐。
  • components/goodscomponents/shopcart:菜品列表、购物车、数量增减。
  • pages/checkout:提交订单,调用支付。
  • pages/order:当前用户订单列表。
  • pages/order-detail:订单明细、再次支付。

现有请求大致是:

  • POST /api/WeiXinApp/Login
  • GET /api/Sellers?id={sellerId}&seatid={seatNo}
  • POST /api/Orders
  • GET /api/Orders
  • GET /api/Orders/{id}
  • GET /api/TenPayV3/JsApi/{orderId}

📌 1.2 旧后端模型

WeWaiter 后端曾是 ASP.NET Core + PostgreSQL,源码已从当前仓库移除,但冻结模型如下:

  • User:微信用户。
  • SellerSellerInfo:商家和商家扩展信息。
  • Seat:桌台。
  • Catalog:菜品分类。
  • Goods:菜品。
  • OrderBuyItem:订单和订单项。
  • Printer:打印机。

支付侧目前直接使用微信 TenPay V3,小程序通过 wx.requestPayment 拉起微信支付。订单状态主要是 NOTPAYUSERPAYINGSUCCESSREFUNDREVOKEDCLOSED

📌 1.3 旧 WinForms 管理端能力

Manager 是 .NET Framework + DevExpress + EF6 的管理端,源码已从当前仓库移除,主要功能曾是:

  • 商家管理。
  • 分类管理。
  • 菜品管理。
  • 桌台管理。
  • 打印机管理。
  • 图片上传到 OSS。

它更像数据库 CRUD 工具,没有完整权限、门店运营、结算、财务、支付通道、设备管理等后台能力。

📌 2. 新系统目标

技术栈目标:

  • 后端:ASP.NET Core 10.0、EF Core、PostgreSQL。
  • 管理端:Blazor、Element-Blazor、ASP.NET Core 10.0。
  • 客户端:微信扫码小程序、支付宝小程序、跨平台 App,后续可扩展低成本点餐平板。
  • 支付:微信小程序继续使用微信原生支付;微信小程序之外的支付场景对接 https://member.z-pay.cn/ 对应的 ZPAY 能力;后端统一封装支付通道。
  • 部署:单店/连锁均可独立部署,支持 Docker Compose 或 Windows 服务部署。

Element-Blazor 使用策略:

  • 第一版管理端以 Element-Blazor 为组件库基线。
  • 初期可优先使用 NuGet 包或源码引用快速推进。
  • 如果遇到 Element-Blazor 组件库本身应该具备的通用能力缺口,可将 Element-Blazor/Element-Blazor 作为 git 子模块拉入仓库,例如放在 external/Element-Blazor
  • 对通用组件能力、通用样式、组件 API、交互一致性、可访问性和缺陷修复,优先修改 Element-Blazor 子模块代码,而不是在 WeWaiter.AdminWeb 中写一次性绕行实现。
  • 对 WeWaiter 业务专属页面、领域交互和业务校验,保留在 WeWaiter.AdminWeb
  • Element-Blazor 使用 MIT License,修改和分发时必须保留原许可证和版权声明。

业务目标:

  • 顾客扫码选桌点餐、下单、支付。
  • 商户后台管理门店、桌台、菜品、套餐、规格、库存、打印、订单和退款。
  • 平台/商户可查账、对账、结算、导出报表。
  • 支付通道可配置,微信小程序默认走微信原生 JSAPI/小程序支付,支付宝小程序、跨平台 App、H5 等微信小程序之外的场景默认走 ZPAY,后续可扩展支付宝直连、现金或店内收银。
  • 预留厨房屏、取餐屏、平板点餐、服务员 App、POS 收银端接口。

📌 3. 产品模块规划

📌 3.1 顾客点餐端

  • 登录与身份识别:微信 openid、支付宝 userId、App 手机号/游客。
  • 扫码入座:二维码包含 tenantId/storeId/tableId/channel 或短码。
  • 菜单浏览:分类、菜品、规格、做法、加料、口味、沽清、推荐。
  • 购物车:同桌加菜、数量修改、规格合并、备注。
  • 下单:堂食、外带、预约、人数、餐具、备注。
  • 支付:创建支付单、跳转支付、支付回调、支付状态轮询。
  • 订单状态:待支付、已支付、已接单、制作中、待取餐、已完成、已取消、退款中、已退款。
  • 历史订单:再次点单、开发票预留、售后预留。

📌 3.2 商户管理端

  • 总览看板:今日营业额、订单数、客单价、退款、支付成功率、热销菜品。
  • 门店管理:门店资料、营业时间、服务电话、公告、堂食/外带开关。
  • 桌台管理:区域、桌号、人数、二维码生成。
  • 菜品管理:分类、菜品、图片、价格、规格、套餐、口味、库存、上下架。
  • 订单中心:实时订单、筛选、接单、取消、退款、改价、补打小票。
  • 厨房与打印:打印机、出品档口、打印模板、失败重打。
  • 支付配置:微信支付和 ZPAY 商户参数、通道开关、回调地址校验。
  • 财务结算:支付流水、退款流水、平台服务费、商户结算单、导出。
  • 员工与权限:老板、店长、收银员、后厨、服务员、财务。
  • 系统配置:图片存储、短信/通知、数据备份、操作日志。

📌 3.3 平台管理端

如果 WeWaiter 后续要作为 SaaS 或代理商系统,需要平台层:

  • 租户/商户开通。
  • 套餐和授权期限。
  • 平台服务费规则。
  • 支付通道总配置。
  • 结算审核。
  • 平台操作审计。
  • 版本和远程升级。

如果只做单商户私有部署,平台层可以保留表结构,后台先隐藏。

📌 3.4 设备端

可分三类设备:

  • 厨房屏/KDS:Web PWA 或轻量 App 优先,接收新订单、改状态、催单提醒。
  • 点餐平板:低成本设备可以评估 LVGLSharp 或 MewUI,但第一版建议先用 WebView/PWA 降低维护成本。
  • 收银/打印网关:独立本地 Agent,负责局域网打印、钱箱、扫码枪、断网缓冲。

低成本平板不建议第一阶段直接押注 LVGLSharp/MewUI。原因是餐饮点餐强依赖图片、滚动、动效、输入法、联网缓存和扫码支付跳转,Web/PWA 更快验证业务。LVGLSharp/MewUI 可以作为“极低成本、固定菜单、弱图片、只内网下单”的第二路径。

📌 4. 推荐架构

📌 4.1 后端项目拆分

建议新建 src 结构:

  • WeWaiter.Api:HTTP API、认证、OpenAPI。
  • WeWaiter.Application:用例服务、DTO、权限、事务边界。
  • WeWaiter.Domain:领域实体、枚举、规则。
  • WeWaiter.Infrastructure:EF Core、支付、文件存储、打印、消息。
  • WeWaiter.Worker:超时关单、结算生成、支付补偿、打印重试。
  • WeWaiter.AdminWeb:Blazor + Element-Blazor 管理端。
  • WeWaiter.MiniProgram:迁入 src 的 mpvue 微信小程序。
  • external/Element-Blazor:可选 git 子模块;当 Element-Blazor 需要源码级修改时引入。
  • WeWaiter.Migrations 或直接使用 API 项目承载 migrations。

📌 4.2 核心原则

  • 订单和支付分离:订单是业务单,支付单是资金请求,退款单是逆向资金请求。
  • 菜品价格快照:订单项必须保存下单时名称、规格、单价、图片、税费/服务费等快照,不能依赖菜品后续修改。
  • 多租户隔离:所有经营数据带 tenant_id,门店数据带 store_id
  • 钱用 decimal(18,2) 或以分为单位的 long。支付接口建议用分,库内可用 numeric(18,2),但要统一转换边界。
  • 状态机显式化:订单、支付、退款、结算各自有状态,不混用一个字段。
  • 支付回调幂等:以支付平台交易号和本地支付单号做唯一约束,重复回调不重复入账。
  • 对账优先:每一笔资金动作都要有流水和原始回调报文。

📌 5. 数据库规划

以下是第一版 PostgreSQL 逻辑表。字段命名建议使用 snake_case,主键统一 uuid,时间统一 timestamptz

📌 5.1 租户与组织

tenants

  • id
  • name
  • code
  • status
  • contact_name
  • contact_phone
  • created_at
  • updated_at

stores

  • id
  • tenant_id
  • name
  • logo_url
  • address
  • phone
  • business_hours
  • announcement
  • status
  • timezone
  • created_at
  • updated_at

store_areas

  • id
  • tenant_id
  • store_id
  • name
  • sort_order

dining_tables

  • id
  • tenant_id
  • store_id
  • area_id
  • table_no
  • display_name
  • capacity
  • qr_code
  • qr_payload
  • status
  • created_at
  • updated_at

📌 5.2 账号与权限

staff_users

  • id
  • tenant_id
  • store_id
  • username
  • phone
  • password_hash
  • display_name
  • status
  • last_login_at
  • created_at

roles

  • id
  • tenant_id
  • name
  • scope
  • permissions jsonb

staff_user_roles

  • staff_user_id
  • role_id

customers

  • id
  • tenant_id
  • phone
  • nickname
  • avatar_url
  • created_at
  • last_active_at

customer_identities

  • id
  • customer_id
  • provider
  • openid
  • unionid
  • session_key_encrypted
  • created_at

provider 可取 wechat_miniappalipay_miniappapp_phoneguest

📌 5.3 菜单与商品

menu_categories

  • id
  • tenant_id
  • store_id
  • name
  • sort_order
  • is_enabled
  • created_at

products

  • id
  • tenant_id
  • store_id
  • category_id
  • sku
  • barcode
  • name
  • description
  • image_url
  • thumbnail_url
  • base_price
  • member_price
  • cost_price
  • stock_mode
  • stock_quantity
  • is_sold_out
  • is_enabled
  • sort_order
  • created_at
  • updated_at

product_option_groups

  • id
  • tenant_id
  • store_id
  • product_id
  • name
  • min_select
  • max_select
  • is_required
  • sort_order

product_options

  • id
  • group_id
  • name
  • price_delta
  • sort_order
  • is_enabled

combo_groupscombo_items

  • 用于套餐和必选组合,第二阶段实现。

📌 5.4 订单

orders

  • id
  • tenant_id
  • store_id
  • table_id
  • customer_id
  • order_no
  • daily_no
  • order_type
  • source_channel
  • people_count
  • status
  • pay_status
  • total_amount
  • discount_amount
  • service_fee_amount
  • payable_amount
  • paid_amount
  • remark
  • created_at
  • submitted_at
  • paid_at
  • completed_at
  • cancelled_at

order_typedine_intakeawaypickup

source_channelwechat_miniappalipay_miniappapptabletpos

statusdraftpending_paymentpaidacceptedpreparingreadycompletedcancelledrefundingrefunded

order_items

  • id
  • tenant_id
  • store_id
  • order_id
  • product_id
  • product_name
  • product_image_url
  • unit_price
  • quantity
  • option_amount
  • total_amount
  • remark
  • kitchen_status
  • snapshot jsonb
  • created_at

order_item_options

  • id
  • order_item_id
  • option_group_name
  • option_name
  • price_delta

order_status_logs

  • id
  • order_id
  • from_status
  • to_status
  • operator_type
  • operator_id
  • reason
  • created_at

📌 5.5 支付与退款

payment_channels

  • id
  • tenant_id
  • store_id
  • provider
  • name
  • config_encrypted
  • is_enabled
  • created_at
  • updated_at

provider 第一阶段支持 wechat_payzpay,后续支持 alipay_directcashpos

payment_orders

  • id
  • tenant_id
  • store_id
  • order_id
  • payment_channel_id
  • payment_no
  • provider
  • provider_trade_no
  • amount
  • currency
  • status
  • client_type
  • cashier_url
  • miniapp_payload jsonb
  • request_payload jsonb
  • notify_payload jsonb
  • return_payload jsonb
  • expired_at
  • paid_at
  • created_at
  • updated_at

statuscreatedpendingpaidfailedclosedexpired

refund_orders

  • id
  • tenant_id
  • store_id
  • order_id
  • payment_order_id
  • refund_no
  • provider_refund_no
  • amount
  • reason
  • status
  • request_payload jsonb
  • notify_payload jsonb
  • created_at
  • succeeded_at

payment_events

  • id
  • payment_order_id
  • event_type
  • provider
  • provider_trade_no
  • raw_payload jsonb
  • signature_valid
  • created_at

📌 5.6 结算与财务

ledger_entries

  • id
  • tenant_id
  • store_id
  • biz_type
  • biz_id
  • direction
  • amount
  • fee_amount
  • net_amount
  • occurred_at
  • created_at

settlement_batches

  • id
  • tenant_id
  • store_id
  • settlement_no
  • period_start
  • period_end
  • gross_amount
  • refund_amount
  • fee_amount
  • net_amount
  • status
  • created_at
  • confirmed_at

settlement_items

  • id
  • settlement_batch_id
  • ledger_entry_id
  • amount

📌 5.7 打印与设备

printers

  • id
  • tenant_id
  • store_id
  • name
  • printer_type
  • connection_type
  • config jsonb
  • status
  • created_at

print_jobs

  • id
  • tenant_id
  • store_id
  • printer_id
  • order_id
  • job_type
  • content
  • status
  • retry_count
  • last_error
  • created_at
  • printed_at

devices

  • id
  • tenant_id
  • store_id
  • device_type
  • name
  • device_key
  • status
  • last_seen_at
  • config jsonb

📌 5.8 系统与审计

files

  • id
  • tenant_id
  • store_id
  • storage_provider
  • bucket
  • object_key
  • url
  • content_type
  • size
  • created_at

operation_logs

  • id
  • tenant_id
  • store_id
  • operator_type
  • operator_id
  • action
  • target_type
  • target_id
  • diff jsonb
  • ip
  • user_agent
  • created_at

outbox_messages

  • id
  • topic
  • payload jsonb
  • status
  • retry_count
  • next_retry_at
  • created_at

📌 6. 支付平台对接规划

支付不要写死在订单控制器里,建议定义统一接口:

public interface IPaymentProvider
{
    string Provider { get; }
    Task<CreatePaymentResult> CreatePaymentAsync(CreatePaymentRequest request, CancellationToken cancellationToken);
    Task<PaymentNotifyResult> HandleNotifyAsync(HttpRequest request, CancellationToken cancellationToken);
    Task<QueryPaymentResult> QueryAsync(QueryPaymentRequest request, CancellationToken cancellationToken);
    Task<ClosePaymentResult> CloseAsync(ClosePaymentRequest request, CancellationToken cancellationToken);
    Task<CreateRefundResult> RefundAsync(CreateRefundRequest request, CancellationToken cancellationToken);
}

第一阶段实现两个支付 provider:

  • WechatMiniProgramPaymentProvider:微信小程序原生支付,延续当前 wx.requestPayment 体验。
  • ZPayPaymentProvider:微信小程序之外的支付场景,例如支付宝小程序、跨平台 App、H5 或后续聚合支付入口。

支付通道路由规则:

  • source_channel = wechat_miniapp:默认选择 wechat_pay
  • source_channel = alipay_miniapp:默认选择 zpay
  • source_channel = app:默认选择 zpay,也可按 App 内部环境扩展微信/支付宝直连。
  • source_channel = tabletpos:默认选择店内收银、现金或扫码聚合支付,按门店配置决定。

统一支付流程建议如下:

  1. 顾客提交订单,后端创建 ordersorder_items,订单状态为 pending_payment
  2. 前端调用 POST /api/client/orders/{id}/payments
  3. 后端根据 source_channel 和门店支付配置选择 provider,创建 payment_orders
  4. 微信小程序 provider 调用微信统一下单,返回 wx.requestPayment 参数。
  5. ZPAY provider 调用 ZPAY 创建支付,返回收银台跳转信息、二维码、URL 或小程序跳转参数。
  6. 支付平台回调后端 notify_url
  7. 后端验签、幂等保存 payment_events,更新 payment_orderspaid,再更新 orders.pay_status/status
  8. Worker 定时查询未确认支付单,补偿回调丢失。

微信小程序原生支付流程:

  1. 前端创建订单后调用 POST /api/client/orders/{id}/payments
  2. 后端选择 WechatMiniProgramPaymentProvider
  3. 后端使用顾客 openid、订单号、金额、商品摘要调用微信支付。
  4. 返回 timeStampnonceStrpackagesignTypepaySign
  5. 小程序调用 wx.requestPayment
  6. 微信支付回调后端,后端验签并更新支付单和订单。

ZPAY 支付流程:

  1. 非微信小程序客户端创建订单后调用 POST /api/client/orders/{id}/payments
  2. 后端选择 ZPayPaymentProvider
  3. 后端调用 ZPAY 创建支付单。
  4. 前端按 ZPAY 返回结果跳转收银台、展示二维码或拉起对应小程序。
  5. ZPAY 回调后端,后端验签并更新支付单和订单。

重要风险:

  • 微信小程序场景不走 ZPAY,避免第三方收银台跳转带来的审核和体验风险。
  • 支付宝小程序、App、H5 使用 ZPAY 时,仍需以 ZPAY 当前文档和各平台跳转规则为准。
  • 支付金额、订单号、回调签名、重复通知、超时关闭必须在后端兜住。

📌 7. API 边界建议

📌 7.1 顾客端 API

  • POST /api/client/auth/wechat/login
  • POST /api/client/auth/alipay/login
  • GET /api/client/scan-tickets/{code}
  • GET /api/client/stores/{storeId}/menu?tableId=...
  • POST /api/client/orders
  • GET /api/client/orders
  • GET /api/client/orders/{id}
  • POST /api/client/orders/{id}/payments
  • GET /api/client/orders/{id}/payment-status
  • POST /api/client/orders/{id}/cancel

📌 7.2 管理端 API

  • POST /api/admin/auth/login
  • GET /api/admin/dashboard/summary
  • GET/POST/PUT/DELETE /api/admin/stores
  • GET/POST/PUT/DELETE /api/admin/tables
  • GET/POST/PUT/DELETE /api/admin/menu-categories
  • GET/POST/PUT/DELETE /api/admin/products
  • GET /api/admin/orders
  • GET /api/admin/orders/{id}
  • POST /api/admin/orders/{id}/accept
  • POST /api/admin/orders/{id}/complete
  • POST /api/admin/orders/{id}/refund
  • GET/PUT /api/admin/payment-channels
  • GET /api/admin/payment-orders
  • GET /api/admin/settlements
  • POST /api/admin/settlements/{id}/confirm
  • GET/POST/PUT/DELETE /api/admin/printers
  • GET /api/admin/operation-logs

📌 7.3 设备 API

  • POST /api/device/auth
  • GET /api/device/orders/pending
  • POST /api/device/orders/{id}/kitchen-status
  • GET /api/device/print-jobs
  • POST /api/device/print-jobs/{id}/ack
  • POST /api/device/heartbeat

📌 8. 管理端页面规划

Blazor + Element-Blazor 管理端建议第一版页面:

  • 登录页。
  • 工作台:营业概览、待处理订单、支付状态。
  • 订单中心:实时订单列表、订单详情抽屉、状态操作、退款。
  • 菜品中心:分类、菜品表格、菜品编辑、图片上传、规格/口味。
  • 桌台中心:区域、桌台、二维码生成和下载。
  • 门店设置:基础信息、营业时间、公告。
  • 支付设置:微信支付、ZPAY 参数、回调地址、测试支付。
  • 打印设置:打印机、模板、打印记录。
  • 财务中心:流水、退款、结算单、导出。
  • 员工权限:员工、角色。
  • 系统日志:操作日志、异常事件。

UI 风格建议偏运营工具:紧凑、可扫描、少装饰、多表格筛选和批量操作。

📌 9. 部署方案

📌 9.1 单机私有部署

  • wewaiter-api:ASP.NET Core API。
  • wewaiter-admin:Blazor 管理端,可与 API 同进程托管或独立部署。
  • postgres:业务数据库。
  • redis:缓存、分布式锁、队列可选。
  • wewaiter-worker:定时任务和异步任务。
  • wewaiter-print-agent:可选,本地打印网关。

📌 9.2 Docker Compose

适合 Linux 小主机、云服务器、NAS:

  • API、Worker、PostgreSQL、Redis、Nginx 一键启动。
  • volumes 保存数据库和上传文件。
  • .env 配置微信支付、ZPAY、JWT、数据库密码、对象存储。

📌 9.3 Windows 服务

适合现有商户电脑:

  • API/Worker 作为 Windows Service。
  • PostgreSQL 本机安装或内置部署脚本。
  • 打印 Agent 作为 Windows 托盘或服务。

📌 10. 迁移路线

📌 阶段 0:冻结旧业务认知

  • 梳理旧表字段和小程序接口。
  • 固化新枚举:订单状态、支付状态、渠道、设备类型。
  • 确认微信原生支付和 ZPAY 当前接口文档。

📌 阶段 1:新后端骨架

  • 建立 ASP.NET Core 10.0 项目结构。
  • 接入 EF Core + PostgreSQL + migrations。
  • 建立租户、门店、桌台、分类、菜品、订单基础表。
  • 建立 JWT 登录和 OpenAPI。
  • 写入种子数据,复刻现有小程序点餐链路。

📌 阶段 2:支付闭环

  • 实现 IPaymentProviderWechatMiniProgramPaymentProviderZPayPaymentProvider
  • 建立支付单、支付事件、回调幂等。
  • 完成微信小程序原生支付、支付宝小程序/App 的 ZPAY 支付返回形态。
  • 增加超时关单和支付查询补偿。

📌 阶段 3:管理端 MVP

  • 登录、门店、桌台、菜品、分类。
  • 订单列表、订单详情、状态流转。
  • 微信支付和 ZPAY 配置与支付流水查询。
  • 图片上传。
  • 二维码生成。

📌 阶段 4:结算与报表

  • 支付流水、退款流水。
  • 日结/周期结算。
  • 导出 Excel/CSV。
  • 财务权限和操作审计。

📌 阶段 5:设备与出品

  • 打印任务和打印模板。
  • 本地打印 Agent。
  • 厨房屏/KDS。
  • 平板点餐 PWA。
  • 再评估 LVGLSharp/MewUI 的专用设备版本。

📌 11. 第一批实现建议

建议不要直接在旧 WeWaiter 项目上大改,而是新建 src 新系统,旧系统作为业务参考。第一批代码目标:

  1. 新建 solution/project 结构。
  2. 建立 PostgreSQL schema 和 migrations。
  3. 实现管理端登录、门店、桌台、分类、菜品 API。
  4. 实现顾客端扫码菜单 API 和创建订单 API。
  5. 对接真实支付前先做 FakePaymentProvider,把订单/支付状态机跑通。
  6. 再替换为微信小程序原生支付和真实 ZPayPaymentProvider

这样可以在没有支付联调账号时继续推进核心业务,避免支付平台细节阻塞整个后台重建。