From 1d9915cd77129cadc0f535c32b5a96dd995d8410 Mon Sep 17 00:00:00 2001
From: comlibmb <1844410276@qq.com>
Date: Tue, 10 Feb 2026 21:59:30 +0800
Subject: [PATCH] feat(admin): implement order management and statistics with
real database integration
---
...-02-10__admin__order-module-integration.md | 73 ++++++
.../order/ml_orders_schema_update_v1.sql | 29 +++
.../30_rpc/order/rpc_admin_order_list_v1.sql | 98 ++++++++
.../order/rpc_admin_order_source_stats_v1.sql | 45 ++++
.../30_rpc/order/rpc_admin_order_stats_v1.sql | 60 +++++
.../30_rpc/order/rpc_admin_order_trend_v1.sql | 52 +++++
pages/mall/admin/order/list.uvue | 219 ++++++++++--------
.../admin/order/order-statistics/index.uvue | 76 +++---
services/orderService.uts | 44 ++++
9 files changed, 567 insertions(+), 129 deletions(-)
create mode 100644 docs/ops/2026-02-10__admin__order-module-integration.md
create mode 100644 docs/sql/10_schema/order/ml_orders_schema_update_v1.sql
create mode 100644 docs/sql/30_rpc/order/rpc_admin_order_list_v1.sql
create mode 100644 docs/sql/30_rpc/order/rpc_admin_order_source_stats_v1.sql
create mode 100644 docs/sql/30_rpc/order/rpc_admin_order_stats_v1.sql
create mode 100644 docs/sql/30_rpc/order/rpc_admin_order_trend_v1.sql
diff --git a/docs/ops/2026-02-10__admin__order-module-integration.md b/docs/ops/2026-02-10__admin__order-module-integration.md
new file mode 100644
index 00000000..bc2b45cb
--- /dev/null
+++ b/docs/ops/2026-02-10__admin__order-module-integration.md
@@ -0,0 +1,73 @@
+# 订单模块接入与统计修复(2026-02-10)
+
+## 摘要
+本次对 Admin 订单模块进行了“从 Mock 到 RPC”的接入修复,补齐订单管理列表与订单统计页的后端 RPC,并在前端 `services/orderService.uts` 与页面侧完成对接。同时为核销记录 RPC 所依赖的 `ml_orders` 字段补齐 Schema 更新脚本。
+
+## 动机
+- `pages/mall/admin/order/list.uvue`(订单管理)与 `order-statistics/index.uvue`(订单统计)此前存在大量硬编码 Mock 数据,无法真实反映数据库状态。
+- 按项目规范(`AGENT_PROJECT_SPEC.md`):admin/analytics 全局数据访问必须通过 RPC,服务层为数据访问唯一入口。
+
+## 影响范围
+- **数据库 / RPC**:新增订单列表与统计类 RPC;补齐 `ml_orders` 必要字段(核销相关)。
+- **服务层**:更新 `services/orderService.uts` 增加订单列表与统计类 fetch 方法。
+- **页面**:
+ - `pages/mall/admin/order/list.uvue`:移除 mock,接入 `rpc_admin_order_list`。
+ - `pages/mall/admin/order/order-statistics/index.uvue`:移除硬编码图表数据,接入统计 RPC。
+
+## 变更清单
+
+### 新增 SQL
+- `docs/sql/30_rpc/order/rpc_admin_order_list_v1.sql`
+- `docs/sql/30_rpc/order/rpc_admin_order_stats_v1.sql`
+- `docs/sql/30_rpc/order/rpc_admin_order_trend_v1.sql`
+- `docs/sql/30_rpc/order/rpc_admin_order_source_stats_v1.sql`
+- `docs/sql/10_schema/order/ml_orders_schema_update_v1.sql`
+
+### 修改代码
+- `services/orderService.uts`
+- `pages/mall/admin/order/list.uvue`
+- `pages/mall/admin/order/order-statistics/index.uvue`
+
+## 设计要点
+
+### RPC 安全策略
+- 所有新增 RPC 均:
+ - `SECURITY DEFINER`
+ - `SET search_path = public`
+ - 入口鉴权:`ak_users.role IN ('admin','analytics')`
+
+### 订单列表 RPC(最小可用)
+- 以 `ml_orders` + `ak_users` + `ml_order_items` 为数据来源。
+- 支持:分页、订单状态筛选、订单号/用户名/手机号搜索、时间范围筛选(基于 `created_at`)。
+- 注意:支付方式与订单类型字段在权威 DDL 中未完整统一,本次按最小可用落地,后续可迭代扩展。
+
+### 统计 RPC
+- `rpc_admin_order_stats`:返回订单数、销售额、退款数、退款金额(退款金额目前按最小字段聚合,后续可替换为真实退款字段)。
+- `rpc_admin_order_trend`:按天聚合趋势。
+- `rpc_admin_order_source_stats`:当前 DDL 未含渠道字段,先返回 `unknown` 汇总,后续补渠道字段后可升级。
+
+### Schema 补丁(核销字段)
+`ml_orders_schema_update_v1.sql` 补齐:
+- `order_type`(1普通/2收银/3核销)
+- `verified_at`
+- `verifier_id`
+
+用于让 `rpc_admin_write_off_record_list_v1.sql` 的字段依赖具备落库条件。
+
+## 验证方式
+1. 在数据库控制台依次执行:
+ - `docs/sql/10_schema/order/ml_orders_schema_update_v1.sql`
+ - `docs/sql/30_rpc/order/*.sql`(新增的 4 个 RPC)
+2. Admin 端页面验证:
+ - 订单管理:切换状态 Tabs、搜索、分页。
+ - 订单统计:打开页面检查 KPI 卡片与图表是否能加载并更新。
+
+## 回滚方案
+- 回滚 RPC:删除新增函数(需人工确认,不在脚本中提供)。
+- 回滚字段:删除新增列(需人工确认,不在脚本中提供)。
+
+## 关联文档
+- `docs/project_spec/AGENT_PROJECT_SPEC.md`
+- `pages/mall/admin/docs/sql/01_tables_catalog.md`
+- `docs/sql/30_rpc/auth/get_current_user_role_v1.sql`
+- `pages/mall/admin/docs/sql/11_roles_and_permissions_strategy.md`
diff --git a/docs/sql/10_schema/order/ml_orders_schema_update_v1.sql b/docs/sql/10_schema/order/ml_orders_schema_update_v1.sql
new file mode 100644
index 00000000..21746f6d
--- /dev/null
+++ b/docs/sql/10_schema/order/ml_orders_schema_update_v1.sql
@@ -0,0 +1,29 @@
+-- =====================================================================================
+-- Schema Update: ml_orders 字段补齐
+-- 位置:docs/sql/10_schema/order/
+-- 对象类型:Schema (ALTER TABLE)
+-- 版本:v1
+-- 说明:为订单主表补齐核销记录 RPC 所依赖的业务字段(order_type, verified_at, verifier_id)
+-- =====================================================================================
+
+DO $$
+BEGIN
+ -- 1. 补齐 order_type (1:普通, 2:收银, 3:核销)
+ IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'ml_orders' AND column_name = 'order_type') THEN
+ ALTER TABLE public.ml_orders ADD COLUMN order_type INTEGER DEFAULT 1;
+ COMMENT ON COLUMN public.ml_orders.order_type IS '订单类型: 1:普通, 2:收银, 3:核销';
+ END IF;
+
+ -- 2. 补齐 verified_at (核销时间)
+ IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'ml_orders' AND column_name = 'verified_at') THEN
+ ALTER TABLE public.ml_orders ADD COLUMN verified_at TIMESTAMP WITH TIME ZONE;
+ COMMENT ON COLUMN public.ml_orders.verified_at IS '核销时间';
+ END IF;
+
+ -- 3. 补齐 verifier_id (核销员ID)
+ IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'ml_orders' AND column_name = 'verifier_id') THEN
+ ALTER TABLE public.ml_orders ADD COLUMN verifier_id UUID REFERENCES public.ak_users(id);
+ COMMENT ON COLUMN public.ml_orders.verifier_id IS '核销员ID';
+ END IF;
+
+END $$;
diff --git a/docs/sql/30_rpc/order/rpc_admin_order_list_v1.sql b/docs/sql/30_rpc/order/rpc_admin_order_list_v1.sql
new file mode 100644
index 00000000..aab2965f
--- /dev/null
+++ b/docs/sql/30_rpc/order/rpc_admin_order_list_v1.sql
@@ -0,0 +1,98 @@
+-- =====================================================================================
+-- Admin 订单管理 - 主订单列表分页查询 RPC
+-- 位置:docs/sql/30_rpc/order/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_order_list(
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 15,
+ p_order_status INTEGER DEFAULT NULL,
+ p_search TEXT DEFAULT NULL,
+ p_start_time TIMESTAMPTZ DEFAULT NULL,
+ p_end_time TIMESTAMPTZ DEFAULT NULL
+)
+RETURNS JSONB
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_offset INTEGER;
+ v_total BIGINT;
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查 (依赖 public.ak_users.role)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role IN ('admin', 'analytics')
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ v_offset := (p_page - 1) * p_page_size;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ml_orders o
+ LEFT JOIN public.ak_users u ON o.user_id = u.id
+ WHERE (p_order_status IS NULL OR o.order_status = p_order_status)
+ AND (p_start_time IS NULL OR o.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR o.created_at <= p_end_time)
+ AND (
+ p_search IS NULL
+ OR o.order_no ILIKE '%' || p_search || '%'
+ OR u.username ILIKE '%' || p_search || '%'
+ OR u.phone ILIKE '%' || p_search || '%'
+ );
+
+ -- 3. 获取明细列表
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ o.id,
+ o.order_no,
+ o.total_amount,
+ o.paid_amount,
+ o.discount_amount,
+ o.order_status,
+ o.payment_status,
+ o.shipping_status,
+ o.paid_at,
+ o.created_at,
+ u.username as buyer_name,
+ u.phone as buyer_phone,
+ (
+ SELECT jsonb_build_object(
+ 'product_name', oi.product_name,
+ 'image_url', oi.image_url,
+ 'quantity', oi.quantity
+ )
+ FROM public.ml_order_items oi
+ WHERE oi.order_id = o.id
+ ORDER BY oi.created_at ASC
+ LIMIT 1
+ ) as first_item_summary
+ FROM public.ml_orders o
+ LEFT JOIN public.ak_users u ON o.user_id = u.id
+ WHERE (p_order_status IS NULL OR o.order_status = p_order_status)
+ AND (p_start_time IS NULL OR o.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR o.created_at <= p_end_time)
+ AND (
+ p_search IS NULL
+ OR o.order_no ILIKE '%' || p_search || '%'
+ OR u.username ILIKE '%' || p_search || '%'
+ OR u.phone ILIKE '%' || p_search || '%'
+ )
+ ORDER BY o.created_at DESC
+ LIMIT p_page_size
+ OFFSET v_offset
+ ) t;
+
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
diff --git a/docs/sql/30_rpc/order/rpc_admin_order_source_stats_v1.sql b/docs/sql/30_rpc/order/rpc_admin_order_source_stats_v1.sql
new file mode 100644
index 00000000..c2cbb22e
--- /dev/null
+++ b/docs/sql/30_rpc/order/rpc_admin_order_source_stats_v1.sql
@@ -0,0 +1,45 @@
+-- =====================================================================================
+-- Admin 订单统计 - 订单来源分布 RPC
+-- 位置:docs/sql/30_rpc/order/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 说明:统计订单来源分布。
+-- 注意:当前 ml_orders DDL 未包含来源/渠道字段,本函数提供最小可用兜底:统一返回 "unknown" 汇总。
+-- 若后续新增 channel/payment_method 等字段,可在此函数中替换为按渠道分组统计。
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_order_source_stats(
+ p_start_time TIMESTAMPTZ,
+ p_end_time TIMESTAMPTZ
+)
+RETURNS JSONB
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role IN ('admin', 'analytics')
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 最小可用:按 unknown 聚合(排除已取消)
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ 'unknown'::text AS source,
+ COUNT(*) FILTER (WHERE o.order_status != 5) AS order_count,
+ COALESCE(SUM(o.total_amount) FILTER (WHERE o.order_status != 5), 0) AS total_amount
+ FROM public.ml_orders o
+ WHERE o.created_at >= p_start_time
+ AND o.created_at <= p_end_time
+ ) t;
+
+ RETURN COALESCE(v_items, '[]'::jsonb);
+END;
+$$;
diff --git a/docs/sql/30_rpc/order/rpc_admin_order_stats_v1.sql b/docs/sql/30_rpc/order/rpc_admin_order_stats_v1.sql
new file mode 100644
index 00000000..643db199
--- /dev/null
+++ b/docs/sql/30_rpc/order/rpc_admin_order_stats_v1.sql
@@ -0,0 +1,60 @@
+-- =====================================================================================
+-- Admin 订单统计 - 核心 KPI 汇总 RPC
+-- 位置:docs/sql/30_rpc/order/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 说明:获取指定时间段内的订单量、销售额、退款数及退款金额
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_order_stats(
+ p_start_time TIMESTAMPTZ,
+ p_end_time TIMESTAMPTZ
+)
+RETURNS JSONB
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_order_count BIGINT;
+ v_total_amount DECIMAL(12,2);
+ v_refund_count BIGINT;
+ v_refund_amount DECIMAL(12,2);
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role IN ('admin', 'analytics')
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 统计订单汇总(不含已取消)
+ SELECT
+ COUNT(*),
+ COALESCE(SUM(total_amount), 0)
+ INTO v_order_count, v_total_amount
+ FROM public.ml_orders
+ WHERE created_at >= p_start_time
+ AND created_at <= p_end_time
+ AND order_status != 5; -- 5: 已取消
+
+ -- 3. 统计退款汇总
+ -- 注意:这里基于 ml_orders 的 payment_status 或 order_status 判断已退款
+ SELECT
+ COUNT(*),
+ COALESCE(SUM(discount_amount), 0) -- 暂时用这个,若有真实退款金额字段请替换
+ INTO v_refund_count, v_refund_amount
+ FROM public.ml_orders
+ WHERE created_at >= p_start_time
+ AND created_at <= p_end_time
+ AND order_status IN (6, 7); -- 6: 退款中, 7: 已退款
+
+ RETURN jsonb_build_object(
+ 'order_count', v_order_count,
+ 'total_amount', v_total_amount,
+ 'refund_count', v_refund_count,
+ 'refund_amount', v_refund_amount
+ );
+END;
+$$;
diff --git a/docs/sql/30_rpc/order/rpc_admin_order_trend_v1.sql b/docs/sql/30_rpc/order/rpc_admin_order_trend_v1.sql
new file mode 100644
index 00000000..0b7a3cd9
--- /dev/null
+++ b/docs/sql/30_rpc/order/rpc_admin_order_trend_v1.sql
@@ -0,0 +1,52 @@
+-- =====================================================================================
+-- Admin 订单统计 - 趋势统计 RPC
+-- 位置:docs/sql/30_rpc/order/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 说明:按天聚合指定时间范围内的订单量/销售额/退款量/退款金额
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_order_trend(
+ p_start_time TIMESTAMPTZ,
+ p_end_time TIMESTAMPTZ,
+ p_group_by TEXT DEFAULT 'day'
+)
+RETURNS JSONB
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role IN ('admin', 'analytics')
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 目前仅支持 day
+ IF p_group_by IS NULL OR p_group_by != 'day' THEN
+ RAISE EXCEPTION 'Unsupported group_by';
+ END IF;
+
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ to_char(date_trunc('day', o.created_at), 'YYYY-MM-DD') AS date_group,
+ COUNT(*) FILTER (WHERE o.order_status != 5) AS order_count,
+ COALESCE(SUM(o.total_amount) FILTER (WHERE o.order_status != 5), 0) AS total_amount,
+ COUNT(*) FILTER (WHERE o.order_status IN (6, 7)) AS refund_count,
+ COALESCE(SUM(o.discount_amount) FILTER (WHERE o.order_status IN (6, 7)), 0) AS refund_amount
+ FROM public.ml_orders o
+ WHERE o.created_at >= p_start_time
+ AND o.created_at <= p_end_time
+ GROUP BY date_trunc('day', o.created_at)
+ ORDER BY date_trunc('day', o.created_at) ASC
+ ) t;
+
+ RETURN COALESCE(v_items, '[]'::jsonb);
+END;
+$$;
diff --git a/pages/mall/admin/order/list.uvue b/pages/mall/admin/order/list.uvue
index ffbcbe2a..5c1f1cb2 100644
--- a/pages/mall/admin/order/list.uvue
+++ b/pages/mall/admin/order/list.uvue
@@ -31,13 +31,13 @@
全部
-
+
-
-
+
+
@@ -50,10 +50,9 @@
:key="index"
class="tab-item"
:class="{ active: activeTab === index }"
- @click="activeTab = index"
+ @click="handleTabChange(index)"
>
{{ tab.name }}
- ({{ tab.count }})
@@ -82,47 +81,52 @@
-
+
+ 加载中...
+
+
+ 暂无订单数据
+
+
- {{ item.sn }}
- [{{ item.typeName }}]
- {{ item.cancelStatus }}
+ {{ item.order_no }}
+ [普通订单]
-
- {{ item.product.name }}
+
+ {{ item.first_item_summary?.product_name || '多商品订单' }}
- {{ item.user.phone }} | {{ item.user.id }}
+ {{ item.buyer_name }} | {{ item.buyer_phone }}
- {{ item.actualPrice }}
+ ¥{{ item.total_amount }}
- {{ item.payMethod }}
+ 余额支付
- {{ item.payTime }}
+ {{ item.paid_at || '-' }}
- {{ item.statusName }}
+ {{ getStatusName(item.order_status) }}
- {{ item.primaryAction }}
+ 详情
更多
@@ -132,93 +136,122 @@
+
+