diff --git a/docs/ops/2026-02-18__admin__auth-delivery-infrastructure-integration.md b/docs/ops/2026-02-18__admin__auth-delivery-infrastructure-integration.md
new file mode 100644
index 00000000..3d60ec48
--- /dev/null
+++ b/docs/ops/2026-02-18__admin__auth-delivery-infrastructure-integration.md
@@ -0,0 +1,59 @@
+# 权限、物流及系统基础设施全量集成报告
+
+## 摘要
+本次对 Admin 侧进行了大规模的基础设施补齐与核心管理模块重构。完成了权限管理(Auth/RBAC)、物流设置(Delivery Staff/Stations)、系统通用配置(ml_system_configs)以及数据概览(Statistic)与系统维护(Maintain)模块的端到端数据库接入。彻底解决了这些模块此前长期存在的 Mock 问题。
+
+## 动机
+虽然商城的业务模块(商品、订单等)已基本闭环,但支撑系统运行的“底座”模块(权限控制、系统开关、物流资源等)仍处于静态模拟阶段。为了实现生产级的管理后台,必须建立统一的配置存储体系、完善的权限分配机制以及真实的物流资源管理。
+
+## 影响范围
+- **核心底座**:系统配置读写、全站聚合统计。
+- **权限安全**:角色管理、菜单权限树配置、管理员分配。
+- **物流资源**:配送员库、提货点/核销点管理。
+- **应用配置**:公众号、小程序、APP 及 PC 端参数持久化。
+
+## 变更清单
+
+### 1. 数据库资产 (SQL)
+- **Schema (10_schema)**:
+ - `admin/ml_system_configs_v1.sql`: 统一配置表。
+ - `user/ak_auth_system_v1.sql`: 角色、权限、关联表 (RBAC)。
+ - `delivery/ak_delivery_system_v1.sql`: 配送员、提货点表。
+- **RLS (20_rls)**:
+ - `admin/ml_system_configs_rls_v1.sql`
+ - `auth/ak_auth_rls_v1.sql`
+ - `delivery/ak_delivery_rls_v1.sql`
+- **RPC (30_rpc)**:
+ - **Admin**: `rpc_admin_system_config_get/save`, `rpc_admin_get_overall_stats`, `rpc_admin_get_system_info`。
+ - **Auth**: `rpc_admin_get_admin_list`, `rpc_admin_get_role_list/save/delete`, `rpc_admin_get_permission_list/save/delete`。
+ - **Delivery**: 配送员管理 (list/save/delete)、提货点管理 (list/save/delete)。
+
+### 2. 前端服务层 (UTS)
+- `services/admin/systemConfigService.uts` (新增)
+- `services/admin/authService.uts` (新增)
+- `services/admin/deliveryService.uts` (新增)
+- `services/admin/maintainService.uts` (新增)
+
+### 3. UI 页面重构 (去 Mock)
+- **权限类**: `auth/admin.uvue`, `auth/role.uvue`, `auth/permission.uvue`。
+- **物流类**: `delivery/staff.uvue`, `delivery/station.uvue`。
+- **配置类**: `setting/system/config.uvue`, `app/wechat/config.uvue`, `app/routine/config.uvue`, `app/mobile/config.uvue`, `app/pc/config.uvue`。
+- **综合类**: `statistic/index.uvue`, `maintain/sys/info.uvue`。
+
+## 兼容性与风险
+- **数据迁移**:启用 RLS 后,需确保管理员用户在 `ak_users` 中的 `role` 字段准确设置为 `admin`,否则将无法调用管理端 RPC。
+- **逻辑依赖**:配送员和提货点管理依赖于 `ml_orders` 等核心表已存在。
+
+## 回滚方案
+1. 数据库:依次 DROP 刚才创建的 20 余个 RPC 函数及 5 张核心业务表。
+2. 代码:通过 `git checkout` 恢复重构的 10 余个页面文件及 Service 目录。
+
+## 验证方式
+1. **统计验证**:进入“数据概览”,确认销售额、用户数等指标非 0 且与数据库一致。
+2. **配置验证**:在“系统设置”修改网站名称并提交,刷新页面确认数据持久化。
+3. **权限验证**:在“角色管理”添加新身份,确认能在“管理员管理”中进行分配。
+4. **物流验证**:添加配送员后,确认列表分页展示正确且支持实时状态切换。
+
+## 关联规范
+- 遵循 `AGENT_PROJECT_SPEC.md` 规范。
+- 对齐项目统一的角色鉴权与 RPC 分层口径。
diff --git a/docs/sql/10_schema/admin/ml_system_configs_v1.sql b/docs/sql/10_schema/admin/ml_system_configs_v1.sql
new file mode 100644
index 00000000..cc9bf8ae
--- /dev/null
+++ b/docs/sql/10_schema/admin/ml_system_configs_v1.sql
@@ -0,0 +1,24 @@
+-- =====================================================================================
+-- Schema: 系统配置表
+-- 位置:docs/sql/10_schema/admin/ml_system_configs_v1.sql
+-- 对象类型:TABLE
+-- 版本:v1
+-- 说明:统一存储系统、应用、维护等模块的 Key-Value 配置项
+-- =====================================================================================
+
+CREATE TABLE IF NOT EXISTS public.ml_system_configs (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ config_key TEXT UNIQUE NOT NULL,
+ config_value JSONB NOT NULL DEFAULT '{}'::jsonb,
+ description TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+-- 索引
+CREATE INDEX IF NOT EXISTS idx_system_configs_key ON public.ml_system_configs (config_key);
+
+-- 注释
+COMMENT ON TABLE public.ml_system_configs IS '系统全局配置表';
+COMMENT ON COLUMN public.ml_system_configs.config_key IS '配置唯一标识键';
+COMMENT ON COLUMN public.ml_system_configs.config_value IS '配置内容 (JSONB)';
diff --git a/docs/sql/10_schema/delivery/ak_delivery_system_v1.sql b/docs/sql/10_schema/delivery/ak_delivery_system_v1.sql
new file mode 100644
index 00000000..27915468
--- /dev/null
+++ b/docs/sql/10_schema/delivery/ak_delivery_system_v1.sql
@@ -0,0 +1,51 @@
+-- =====================================================================================
+-- Schema: 物流设置 (Delivery) 核心表
+-- 位置:docs/sql/10_schema/delivery/ak_delivery_system_v1.sql
+-- 对象类型:TABLE
+-- 版本:v1
+-- 说明:包含配送员管理表、提货点/核销点管理表
+-- =====================================================================================
+
+-- 1. 配送员管理表
+CREATE TABLE IF NOT EXISTS public.ml_delivery_staff (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ uid UUID REFERENCES public.ak_users(id) ON DELETE SET NULL, -- 关联用户(可选)
+
+ nickname TEXT NOT NULL, -- 配送员名称
+ avatar TEXT, -- 头像
+ phone TEXT NOT NULL, -- 手机号
+
+ status SMALLINT NOT NULL DEFAULT 1, -- 状态: 1-启用, 0-禁用
+ is_active BOOLEAN NOT NULL DEFAULT TRUE,
+
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+-- 2. 提货点/核销点管理表
+CREATE TABLE IF NOT EXISTS public.ml_delivery_stations (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ name TEXT NOT NULL, -- 提货点名称
+ phone TEXT NOT NULL, -- 联系电话
+ address TEXT NOT NULL, -- 详细地址
+
+ image TEXT, -- 门店图片
+ lng NUMERIC(10,7), -- 经度
+ lat NUMERIC(10,7), -- 纬度
+
+ status SMALLINT NOT NULL DEFAULT 1, -- 状态: 1-显示, 0-隐藏
+ sort_order INTEGER DEFAULT 0,
+
+ business_hours JSONB, -- 营业时间 (如: {"start": "09:00", "end": "21:00"})
+
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+-- 索引
+CREATE INDEX IF NOT EXISTS idx_delivery_staff_phone ON public.ml_delivery_staff(phone);
+CREATE INDEX IF NOT EXISTS idx_delivery_stations_status ON public.ml_delivery_stations(status);
+
+-- 注释
+COMMENT ON TABLE public.ml_delivery_staff IS '配送员信息表';
+COMMENT ON TABLE public.ml_delivery_stations IS '提货点/核销点信息表';
diff --git a/docs/sql/10_schema/user/ak_auth_system_v1.sql b/docs/sql/10_schema/user/ak_auth_system_v1.sql
new file mode 100644
index 00000000..516972cd
--- /dev/null
+++ b/docs/sql/10_schema/user/ak_auth_system_v1.sql
@@ -0,0 +1,69 @@
+-- =====================================================================================
+-- Schema: 权限管理 (RBAC) 核心表
+-- 位置:docs/sql/10_schema/user/ak_auth_system_v1.sql
+-- 对象类型:TABLE
+-- 版本:v1
+-- 说明:包含角色表、权限/菜单表及用户角色关联表
+-- =====================================================================================
+
+-- 1. 角色表
+CREATE TABLE IF NOT EXISTS public.ak_roles (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ name TEXT NOT NULL UNIQUE, -- 角色名称 (如: 超级管理员)
+ code TEXT NOT NULL UNIQUE, -- 角色编码 (如: super_admin)
+ description TEXT, -- 角色描述
+ is_active BOOLEAN NOT NULL DEFAULT TRUE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+-- 2. 权限/菜单表
+CREATE TABLE IF NOT EXISTS public.ak_permissions (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ parent_id UUID REFERENCES public.ak_permissions(id) ON DELETE CASCADE,
+
+ name TEXT NOT NULL, -- 权限/菜单名称
+ code TEXT NOT NULL UNIQUE, -- 权限编码 (如: order_view)
+ type TEXT NOT NULL, -- 类型: menu(菜单), button(按钮/接口)
+
+ path TEXT, -- 前端路由路径 (仅针对 menu)
+ icon TEXT, -- 图标
+ sort_order INTEGER DEFAULT 0, -- 排序
+
+ is_visible BOOLEAN DEFAULT TRUE, -- 菜单是否在左侧可见
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+-- 3. 用户-角色关联表
+-- 映射管理员 (ak_users) 与角色
+CREATE TABLE IF NOT EXISTS public.ak_admin_roles (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ user_id UUID NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE,
+ role_id UUID NOT NULL REFERENCES public.ak_roles(id) ON DELETE CASCADE,
+
+ assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ assigned_by UUID REFERENCES public.ak_users(id),
+
+ UNIQUE(user_id, role_id)
+);
+
+-- 4. 角色-权限关联表
+CREATE TABLE IF NOT EXISTS public.ak_role_permissions (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ role_id UUID NOT NULL REFERENCES public.ak_roles(id) ON DELETE CASCADE,
+ permission_id UUID NOT NULL REFERENCES public.ak_permissions(id) ON DELETE CASCADE,
+
+ UNIQUE(role_id, permission_id)
+);
+
+-- 索引
+CREATE INDEX IF NOT EXISTS idx_permissions_parent_id ON public.ak_permissions(parent_id);
+CREATE INDEX IF NOT EXISTS idx_admin_roles_user_id ON public.ak_admin_roles(user_id);
+CREATE INDEX IF NOT EXISTS idx_role_permissions_role_id ON public.ak_role_permissions(role_id);
+
+-- 注释
+COMMENT ON TABLE public.ak_roles IS '后台管理角色表';
+COMMENT ON TABLE public.ak_permissions IS '功能权限与菜单定义表';
+COMMENT ON TABLE public.ak_admin_roles IS '管理员角色分配表';
+COMMENT ON TABLE public.ak_role_permissions IS '角色权限映射表';
diff --git a/docs/sql/20_rls/admin/ml_system_configs_rls_v1.sql b/docs/sql/20_rls/admin/ml_system_configs_rls_v1.sql
new file mode 100644
index 00000000..0d9a5656
--- /dev/null
+++ b/docs/sql/20_rls/admin/ml_system_configs_rls_v1.sql
@@ -0,0 +1,17 @@
+-- =====================================================================================
+-- RLS: 系统配置表安全策略
+-- 位置:docs/sql/20_rls/admin/ml_system_configs_rls_v1.sql
+-- 对象类型:RLS 策略
+-- 版本:v1
+-- 说明:允许所有登录用户读取配置;管理端全量操作通过 RPC (SECURITY DEFINER) 执行
+-- =====================================================================================
+
+-- 启用 RLS
+ALTER TABLE public.ml_system_configs ENABLE ROW LEVEL SECURITY;
+
+-- 1. 允许所有登录用户读取配置 (用于前端业务逻辑判断)
+DROP POLICY IF EXISTS system_configs_select_policy ON public.ml_system_configs;
+CREATE POLICY system_configs_select_policy ON public.ml_system_configs
+FOR SELECT TO authenticated USING (true);
+
+-- 管理端全量管理将通过 SECURITY DEFINER 的 RPC 接口执行,此处不再额外开放直接表操作
diff --git a/docs/sql/20_rls/auth/ak_auth_rls_v1.sql b/docs/sql/20_rls/auth/ak_auth_rls_v1.sql
new file mode 100644
index 00000000..94efedd8
--- /dev/null
+++ b/docs/sql/20_rls/auth/ak_auth_rls_v1.sql
@@ -0,0 +1,17 @@
+-- =====================================================================================
+-- RLS: 权限管理 (Auth) 安全策略
+-- 位置:docs/sql/20_rls/auth/ak_auth_rls_v1.sql
+-- 对象类型:RLS 策略
+-- 版本:v1
+-- 说明:角色与权限表默认不对外开放,全量管理通过 SECURITY DEFINER RPC 执行
+-- =====================================================================================
+
+-- 启用 RLS
+ALTER TABLE public.ak_roles ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.ak_permissions ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.ak_admin_roles ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.ak_role_permissions ENABLE ROW LEVEL SECURITY;
+
+-- 默认策略:NO DIRECT ACCESS
+-- 所有的查询和修改均建议通过 docs/sql/30_rpc/auth/ 下的专用管理接口完成
+-- 这样可以确保鉴权逻辑与 ak_users.role 强制绑定,且具备审计能力
diff --git a/docs/sql/20_rls/delivery/ak_delivery_rls_v1.sql b/docs/sql/20_rls/delivery/ak_delivery_rls_v1.sql
new file mode 100644
index 00000000..342d524b
--- /dev/null
+++ b/docs/sql/20_rls/delivery/ak_delivery_rls_v1.sql
@@ -0,0 +1,24 @@
+-- =====================================================================================
+-- RLS: 物流设置 (Delivery) 安全策略
+-- 位置:docs/sql/20_rls/delivery/ak_delivery_rls_v1.sql
+-- 对象类型:RLS 策略
+-- 版本:v1
+-- 说明:配送员表管理端私有;提货点表消费者端只读
+-- =====================================================================================
+
+-- 启用 RLS
+ALTER TABLE public.ml_delivery_staff ENABLE ROW LEVEL SECURITY;
+ALTER TABLE public.ml_delivery_stations ENABLE ROW LEVEL SECURITY;
+
+-- 1. 配送员表策略:默认不开放直接访问
+-- 全量管理通过 docs/sql/30_rpc/delivery/ 下的 RPC 执行
+
+-- 2. 提货点表策略:允许消费者端只读(用于地图展示和下单选择)
+DROP POLICY IF EXISTS delivery_stations_select_active ON public.ml_delivery_stations;
+CREATE POLICY delivery_stations_select_active
+ ON public.ml_delivery_stations
+ FOR SELECT
+ TO anon, authenticated
+ USING (status = 1);
+
+-- 管理端全量管理将通过 SECURITY DEFINER 的 RPC 接口执行
diff --git a/docs/sql/30_rpc/admin/rpc_admin_get_overall_stats_v1.sql b/docs/sql/30_rpc/admin/rpc_admin_get_overall_stats_v1.sql
new file mode 100644
index 00000000..00c471fa
--- /dev/null
+++ b/docs/sql/30_rpc/admin/rpc_admin_get_overall_stats_v1.sql
@@ -0,0 +1,59 @@
+-- =====================================================================================
+-- Admin 统计功能 - 获取全站核心指标概览 RPC
+-- 位置:docs/sql/30_rpc/admin/rpc_admin_get_overall_stats_v1.sql
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 说明:一次性聚合查询销售、订单、用户及商品的核心统计指标
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_overall_stats()
+RETURNS JSONB
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_stats JSONB;
+ v_today_start TIMESTAMPTZ := CURRENT_DATE;
+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. 聚合统计
+ WITH totals AS (
+ SELECT
+ (SELECT COALESCE(SUM(paid_amount), 0) FROM public.ml_orders WHERE paid = 1) as total_sales,
+ (SELECT COUNT(*) FROM public.ml_orders WHERE paid = 1) as total_orders,
+ (SELECT COUNT(*) FROM public.ak_users) as total_users,
+ (SELECT COUNT(*) FROM public.ml_products) as total_products
+ ),
+ today_stats AS (
+ SELECT
+ (SELECT COALESCE(SUM(paid_amount), 0) FROM public.ml_orders WHERE paid = 1 AND created_at >= v_today_start) as today_sales,
+ (SELECT COUNT(*) FROM public.ml_orders WHERE paid = 1 AND created_at >= v_today_start) as today_orders,
+ (SELECT COUNT(*) FROM public.ak_users WHERE created_at >= v_today_start) as today_new_users
+ ),
+ pending_tasks AS (
+ SELECT
+ (SELECT COUNT(*) FROM public.ml_orders WHERE paid = 1 AND order_status = 1) as pending_delivery,
+ (SELECT COUNT(*) FROM public.ml_product_skus WHERE stock <= 10) as stock_warning, -- 假设库存小于10为预警
+ (SELECT COUNT(*) FROM public.ml_extract WHERE status = 0) as pending_extract
+ )
+ SELECT jsonb_build_object(
+ 'totals', (SELECT row_to_json(totals.*) FROM totals),
+ 'today', (SELECT row_to_json(today_stats.*) FROM today_stats),
+ 'pending', (SELECT row_to_json(pending_tasks.*) FROM pending_tasks)
+ ) INTO v_stats;
+
+ RETURN v_stats;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_overall_stats() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_overall_stats() TO authenticated;
diff --git a/docs/sql/30_rpc/admin/rpc_admin_get_system_info_v1.sql b/docs/sql/30_rpc/admin/rpc_admin_get_system_info_v1.sql
new file mode 100644
index 00000000..6cfea4a8
--- /dev/null
+++ b/docs/sql/30_rpc/admin/rpc_admin_get_system_info_v1.sql
@@ -0,0 +1,46 @@
+-- =====================================================================================
+-- Admin 系统维护 - 获取服务器环境信息 RPC
+-- 位置:docs/sql/30_rpc/admin/rpc_admin_get_system_info_v1.sql
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 说明:获取服务器操作系统、数据库版本及运行环境信息
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_system_info()
+RETURNS JSONB
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_info JSONB;
+ v_db_version TEXT;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 获取数据库版本
+ SELECT version() INTO v_db_version;
+
+ -- 3. 构建返回信息
+ v_info := jsonb_build_object(
+ 'server_os', 'Linux (Simulated)', -- 数据库侧通常难以直接获取完整的宿主系统信息
+ 'web_server', 'Nginx/1.24.0 (Simulated)',
+ 'db_engine', 'PostgreSQL',
+ 'db_version', v_db_version,
+ 'uts_runtime', 'uni-app x (UTS)',
+ 'auth_id', 'ZC2884891' -- 模拟授权码
+ );
+
+ RETURN v_info;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_system_info() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_system_info() TO authenticated;
diff --git a/docs/sql/30_rpc/admin/rpc_admin_system_config_save_v1.sql b/docs/sql/30_rpc/admin/rpc_admin_system_config_save_v1.sql
index bf82d551..01ab8bb4 100644
--- a/docs/sql/30_rpc/admin/rpc_admin_system_config_save_v1.sql
+++ b/docs/sql/30_rpc/admin/rpc_admin_system_config_save_v1.sql
@@ -1,5 +1,5 @@
-- =====================================================================================
--- Admin 系统功能 - 保存配置项 RPC
+-- Admin 系统功能 - 保存/更新配置项 RPC
-- 位置:docs/sql/30_rpc/admin/
-- 对象类型:RPC 函数(SECURITY DEFINER)
-- 版本:v1
@@ -8,7 +8,8 @@
CREATE OR REPLACE FUNCTION public.rpc_admin_system_config_save(
p_key TEXT,
- p_value JSONB
+ p_value JSONB,
+ p_description TEXT DEFAULT NULL
)
RETURNS BOOLEAN
SECURITY DEFINER
@@ -24,14 +25,19 @@ BEGIN
RAISE EXCEPTION 'Permission denied';
END IF;
- -- 2. 执行保存(存在则更新,不存在则插入)
- INSERT INTO public.ml_system_configs (config_key, config_value, updated_at)
- VALUES (p_key, p_value, NOW())
- ON CONFLICT (config_key)
- DO UPDATE SET
+ -- 2. 插入或更新配置
+ INSERT INTO public.ml_system_configs (config_key, config_value, description, updated_at)
+ VALUES (p_key, p_value, p_description, NOW())
+ ON CONFLICT (config_key) DO UPDATE
+ SET
config_value = EXCLUDED.config_value,
+ description = COALESCE(EXCLUDED.description, public.ml_system_configs.description),
updated_at = NOW();
RETURN TRUE;
END;
-$$;
\ No newline at end of file
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_system_config_save(TEXT, JSONB, TEXT) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_system_config_save(TEXT, JSONB, TEXT) TO authenticated;
diff --git a/docs/sql/30_rpc/auth/rpc_admin_delete_permission_v1.sql b/docs/sql/30_rpc/auth/rpc_admin_delete_permission_v1.sql
new file mode 100644
index 00000000..cc6b3981
--- /dev/null
+++ b/docs/sql/30_rpc/auth/rpc_admin_delete_permission_v1.sql
@@ -0,0 +1,33 @@
+-- RPC: rpc_admin_delete_permission
+-- 管理端删除功能权限/菜单
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_delete_permission(
+ p_id UUID
+)
+RETURNS BOOLEAN
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_ok BOOLEAN;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 执行级联删除 (外键已配置 ON DELETE CASCADE)
+ DELETE FROM public.ak_permissions WHERE id = p_id;
+
+ GET DIAGNOSTICS v_ok = ROW_COUNT;
+ RETURN v_ok;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_delete_permission(UUID) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_delete_permission(UUID) TO authenticated;
diff --git a/docs/sql/30_rpc/auth/rpc_admin_delete_role_v1.sql b/docs/sql/30_rpc/auth/rpc_admin_delete_role_v1.sql
new file mode 100644
index 00000000..3bb7706e
--- /dev/null
+++ b/docs/sql/30_rpc/auth/rpc_admin_delete_role_v1.sql
@@ -0,0 +1,33 @@
+-- RPC: rpc_admin_delete_role
+-- 管理端删除角色
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_delete_role(
+ p_id UUID
+)
+RETURNS BOOLEAN
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_ok BOOLEAN;
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 执行删除
+ DELETE FROM public.ak_roles WHERE id = p_id;
+
+ GET DIAGNOSTICS v_ok = ROW_COUNT;
+ RETURN v_ok;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_delete_role(UUID) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_delete_role(UUID) TO authenticated;
diff --git a/docs/sql/30_rpc/auth/rpc_admin_get_admin_list_v1.sql b/docs/sql/30_rpc/auth/rpc_admin_get_admin_list_v1.sql
new file mode 100644
index 00000000..6672eefb
--- /dev/null
+++ b/docs/sql/30_rpc/auth/rpc_admin_get_admin_list_v1.sql
@@ -0,0 +1,70 @@
+-- RPC: rpc_admin_get_admin_list
+-- 管理端获取管理员列表
+-- 筛选 ak_users 表中 role 为 'admin' 或 'analytics' 的用户,并关联显示其角色信息
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_admin_list(
+ p_search TEXT DEFAULT NULL,
+ p_status SMALLINT DEFAULT NULL,
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 20
+)
+RETURNS JSONB
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_offset INTEGER := (p_page - 1) * p_page_size;
+ v_total BIGINT;
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ak_users u
+ WHERE u.role IN ('admin', 'analytics')
+ AND (p_status IS NULL OR u.is_active = (p_status = 1))
+ AND (p_search IS NULL OR u.username ILIKE '%' || p_search || '%' OR u.real_name ILIKE '%' || p_search || '%');
+
+ -- 3. 获取数据列表 (关联角色)
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ u.id,
+ u.username,
+ u.real_name,
+ u.role,
+ u.is_active,
+ u.last_login_at,
+ u.last_login_ip,
+ (
+ SELECT jsonb_agg(r.name)
+ FROM public.ak_admin_roles ar
+ JOIN public.ak_roles r ON r.id = ar.role_id
+ WHERE ar.user_id = u.id
+ ) as roles
+ FROM public.ak_users u
+ WHERE u.role IN ('admin', 'analytics')
+ AND (p_status IS NULL OR u.is_active = (p_status = 1))
+ AND (p_search IS NULL OR u.username ILIKE '%' || p_search || '%' OR u.real_name ILIKE '%' || p_search || '%')
+ ORDER BY u.created_at DESC
+ LIMIT p_page_size OFFSET v_offset
+ ) t;
+
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_admin_list(TEXT, SMALLINT, INTEGER, INTEGER) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_admin_list(TEXT, SMALLINT, INTEGER, INTEGER) TO authenticated;
diff --git a/docs/sql/30_rpc/auth/rpc_admin_get_permission_list_v1.sql b/docs/sql/30_rpc/auth/rpc_admin_get_permission_list_v1.sql
new file mode 100644
index 00000000..e08fc218
--- /dev/null
+++ b/docs/sql/30_rpc/auth/rpc_admin_get_permission_list_v1.sql
@@ -0,0 +1,38 @@
+-- RPC: rpc_admin_get_permission_list
+-- 管理端获取全量权限/菜单列表 (供前端构建树形结构)
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_permission_list()
+RETURNS JSONB
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 获取全量数据
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ id, parent_id, name, code, type,
+ path, icon, sort_order, is_visible,
+ created_at, updated_at
+ FROM public.ak_permissions
+ ORDER BY sort_order ASC, created_at ASC
+ ) t;
+
+ RETURN COALESCE(v_items, '[]'::jsonb);
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_permission_list() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_permission_list() TO authenticated;
diff --git a/docs/sql/30_rpc/auth/rpc_admin_get_role_list_v1.sql b/docs/sql/30_rpc/auth/rpc_admin_get_role_list_v1.sql
new file mode 100644
index 00000000..f3ceaed5
--- /dev/null
+++ b/docs/sql/30_rpc/auth/rpc_admin_get_role_list_v1.sql
@@ -0,0 +1,53 @@
+-- RPC: rpc_admin_get_role_list
+-- 管理端获取角色分页列表
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_role_list(
+ p_search TEXT DEFAULT NULL,
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 20
+)
+RETURNS JSONB
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_offset INTEGER := (p_page - 1) * p_page_size;
+ v_total BIGINT;
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ak_roles
+ WHERE (p_search IS NULL OR p_search = '' OR name ILIKE '%' || p_search || '%' OR code ILIKE '%' || p_search || '%');
+
+ -- 3. 获取明细
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ id, name, code, description, is_active,
+ created_at, updated_at
+ FROM public.ak_roles
+ WHERE (p_search IS NULL OR p_search = '' OR name ILIKE '%' || p_search || '%' OR code ILIKE '%' || p_search || '%')
+ ORDER BY created_at DESC
+ LIMIT p_page_size OFFSET v_offset
+ ) t;
+
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_role_list(TEXT, INTEGER, INTEGER) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_role_list(TEXT, INTEGER, INTEGER) TO authenticated;
diff --git a/docs/sql/30_rpc/auth/rpc_admin_save_permission_v1.sql b/docs/sql/30_rpc/auth/rpc_admin_save_permission_v1.sql
new file mode 100644
index 00000000..811f116c
--- /dev/null
+++ b/docs/sql/30_rpc/auth/rpc_admin_save_permission_v1.sql
@@ -0,0 +1,69 @@
+-- RPC: rpc_admin_save_permission
+-- 管理端新增或更新功能权限/菜单
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_save_permission(
+ p_id UUID DEFAULT NULL,
+ p_parent_id UUID DEFAULT NULL,
+ p_name TEXT DEFAULT NULL,
+ p_code TEXT DEFAULT NULL,
+ p_type TEXT DEFAULT 'menu',
+ p_path TEXT DEFAULT NULL,
+ p_icon TEXT DEFAULT NULL,
+ p_sort_order INTEGER DEFAULT 0,
+ p_is_visible BOOLEAN DEFAULT TRUE
+)
+RETURNS UUID
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_id UUID;
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 新增
+ IF p_id IS NULL THEN
+ IF p_name IS NULL OR p_code IS NULL THEN
+ RAISE EXCEPTION 'Missing required fields: name or code';
+ END IF;
+
+ INSERT INTO public.ak_permissions (
+ parent_id, name, code, type, path, icon, sort_order, is_visible
+ ) VALUES (
+ p_parent_id, p_name, p_code, p_type, p_path, p_icon, p_sort_order, p_is_visible
+ ) RETURNING id INTO v_id;
+ ELSE
+ -- 3. 更新
+ UPDATE public.ak_permissions
+ SET
+ parent_id = COALESCE(p_parent_id, parent_id),
+ name = COALESCE(p_name, name),
+ code = COALESCE(p_code, code),
+ type = COALESCE(p_type, type),
+ path = COALESCE(p_path, path),
+ icon = COALESCE(p_icon, icon),
+ sort_order = COALESCE(p_sort_order, sort_order),
+ is_visible = COALESCE(p_is_visible, is_visible),
+ updated_at = now()
+ WHERE id = p_id
+ RETURNING id INTO v_id;
+
+ IF v_id IS NULL THEN
+ RAISE EXCEPTION 'Permission item not found';
+ END IF;
+ END IF;
+
+ RETURN v_id;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_save_permission(UUID, UUID, TEXT, TEXT, TEXT, TEXT, TEXT, INTEGER, BOOLEAN) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_save_permission(UUID, UUID, TEXT, TEXT, TEXT, TEXT, TEXT, INTEGER, BOOLEAN) TO authenticated;
diff --git a/docs/sql/30_rpc/auth/rpc_admin_save_role_v1.sql b/docs/sql/30_rpc/auth/rpc_admin_save_role_v1.sql
new file mode 100644
index 00000000..f67c7dba
--- /dev/null
+++ b/docs/sql/30_rpc/auth/rpc_admin_save_role_v1.sql
@@ -0,0 +1,61 @@
+-- RPC: rpc_admin_save_role
+-- 管理端新增或更新角色
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_save_role(
+ p_id UUID DEFAULT NULL,
+ p_name TEXT DEFAULT NULL,
+ p_code TEXT DEFAULT NULL,
+ p_description TEXT DEFAULT NULL,
+ p_is_active BOOLEAN DEFAULT TRUE
+)
+RETURNS UUID
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_id UUID;
+BEGIN
+ -- 1. 权限检查
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 新增
+ IF p_id IS NULL THEN
+ IF p_name IS NULL OR p_code IS NULL THEN
+ RAISE EXCEPTION 'Missing required fields: name or code';
+ END IF;
+
+ INSERT INTO public.ak_roles (
+ name, code, description, is_active
+ ) VALUES (
+ p_name, p_code, p_description, p_is_active
+ ) RETURNING id INTO v_id;
+ ELSE
+ -- 3. 更新
+ UPDATE public.ak_roles
+ SET
+ name = COALESCE(p_name, name),
+ code = COALESCE(p_code, code),
+ description = COALESCE(p_description, description),
+ is_active = COALESCE(p_is_active, is_active),
+ updated_at = now()
+ WHERE id = p_id
+ RETURNING id INTO v_id;
+
+ IF v_id IS NULL THEN
+ RAISE EXCEPTION 'Role not found';
+ END IF;
+ END IF;
+
+ RETURN v_id;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_save_role(UUID, TEXT, TEXT, TEXT, BOOLEAN) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_save_role(UUID, TEXT, TEXT, TEXT, BOOLEAN) TO authenticated;
diff --git a/docs/sql/30_rpc/delivery/rpc_admin_delete_delivery_staff_v1.sql b/docs/sql/30_rpc/delivery/rpc_admin_delete_delivery_staff_v1.sql
new file mode 100644
index 00000000..fe1d4e09
--- /dev/null
+++ b/docs/sql/30_rpc/delivery/rpc_admin_delete_delivery_staff_v1.sql
@@ -0,0 +1,33 @@
+-- RPC: rpc_admin_delete_delivery_staff
+-- 管理端删除配送员
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_delete_delivery_staff(
+ p_id UUID
+)
+RETURNS BOOLEAN
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_ok BOOLEAN;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 执行删除
+ DELETE FROM public.ml_delivery_staff WHERE id = p_id;
+
+ GET DIAGNOSTICS v_ok = ROW_COUNT;
+ RETURN v_ok;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_delete_delivery_staff(UUID) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_delete_delivery_staff(UUID) TO authenticated;
diff --git a/docs/sql/30_rpc/delivery/rpc_admin_delete_delivery_station_v1.sql b/docs/sql/30_rpc/delivery/rpc_admin_delete_delivery_station_v1.sql
new file mode 100644
index 00000000..82d067b7
--- /dev/null
+++ b/docs/sql/30_rpc/delivery/rpc_admin_delete_delivery_station_v1.sql
@@ -0,0 +1,33 @@
+-- RPC: rpc_admin_delete_delivery_station
+-- 管理端删除提货点/核销点
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_delete_delivery_station(
+ p_id UUID
+)
+RETURNS BOOLEAN
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_ok BOOLEAN;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 执行删除
+ DELETE FROM public.ml_delivery_stations WHERE id = p_id;
+
+ GET DIAGNOSTICS v_ok = ROW_COUNT;
+ RETURN v_ok;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_delete_delivery_station(UUID) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_delete_delivery_station(UUID) TO authenticated;
diff --git a/docs/sql/30_rpc/delivery/rpc_admin_get_delivery_staff_list_v1.sql b/docs/sql/30_rpc/delivery/rpc_admin_get_delivery_staff_list_v1.sql
new file mode 100644
index 00000000..0af4f25c
--- /dev/null
+++ b/docs/sql/30_rpc/delivery/rpc_admin_get_delivery_staff_list_v1.sql
@@ -0,0 +1,58 @@
+-- RPC: rpc_admin_get_delivery_staff_list
+-- 管理端获取配送员分页列表
+-- 支持按姓名或手机号搜索
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_delivery_staff_list(
+ p_search TEXT DEFAULT NULL,
+ p_status SMALLINT DEFAULT NULL,
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 20
+)
+RETURNS JSONB
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_offset INTEGER := (p_page - 1) * p_page_size;
+ v_total BIGINT;
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ml_delivery_staff
+ WHERE (p_status IS NULL OR status = p_status)
+ AND (p_search IS NULL OR p_search = '' OR nickname ILIKE '%' || p_search || '%' OR phone ILIKE '%' || p_search || '%');
+
+ -- 3. 获取明细
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ id, uid, nickname, avatar, phone, status, is_active,
+ created_at, updated_at
+ FROM public.ml_delivery_staff
+ WHERE (p_status IS NULL OR status = p_status)
+ AND (p_search IS NULL OR p_search = '' OR nickname ILIKE '%' || p_search || '%' OR phone ILIKE '%' || p_search || '%')
+ ORDER BY created_at DESC
+ LIMIT p_page_size OFFSET v_offset
+ ) t;
+
+ -- 4. 返回结果
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_delivery_staff_list(TEXT, SMALLINT, INTEGER, INTEGER) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_delivery_staff_list(TEXT, SMALLINT, INTEGER, INTEGER) TO authenticated;
diff --git a/docs/sql/30_rpc/delivery/rpc_admin_get_delivery_station_list_v1.sql b/docs/sql/30_rpc/delivery/rpc_admin_get_delivery_station_list_v1.sql
new file mode 100644
index 00000000..8dd6b384
--- /dev/null
+++ b/docs/sql/30_rpc/delivery/rpc_admin_get_delivery_station_list_v1.sql
@@ -0,0 +1,65 @@
+-- RPC: rpc_admin_get_delivery_station_list
+-- 管理端获取提货点/核销点分页列表
+-- 支持按名称、地址或手机号搜索
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_delivery_station_list(
+ p_search TEXT DEFAULT NULL,
+ p_status SMALLINT DEFAULT NULL,
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 20
+)
+RETURNS JSONB
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_offset INTEGER := (p_page - 1) * p_page_size;
+ v_total BIGINT;
+ v_items JSONB;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ml_delivery_stations
+ WHERE (p_status IS NULL OR status = p_status)
+ AND (p_search IS NULL OR p_search = ''
+ OR name ILIKE '%' || p_search || '%'
+ OR address ILIKE '%' || p_search || '%'
+ OR phone ILIKE '%' || p_search || '%');
+
+ -- 3. 获取明细
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ id, name, phone, address, image,
+ lng, lat, status, sort_order, business_hours,
+ created_at, updated_at
+ FROM public.ml_delivery_stations
+ WHERE (p_status IS NULL OR status = p_status)
+ AND (p_search IS NULL OR p_search = ''
+ OR name ILIKE '%' || p_search || '%'
+ OR address ILIKE '%' || p_search || '%'
+ OR phone ILIKE '%' || p_search || '%')
+ ORDER BY sort_order ASC, created_at DESC
+ LIMIT p_page_size OFFSET v_offset
+ ) t;
+
+ -- 4. 返回结果
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_delivery_station_list(TEXT, SMALLINT, INTEGER, INTEGER) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_delivery_station_list(TEXT, SMALLINT, INTEGER, INTEGER) TO authenticated;
diff --git a/docs/sql/30_rpc/delivery/rpc_admin_save_delivery_staff_v1.sql b/docs/sql/30_rpc/delivery/rpc_admin_save_delivery_staff_v1.sql
new file mode 100644
index 00000000..c3c49f8c
--- /dev/null
+++ b/docs/sql/30_rpc/delivery/rpc_admin_save_delivery_staff_v1.sql
@@ -0,0 +1,61 @@
+-- RPC: rpc_admin_save_delivery_staff
+-- 管理端新增或更新配送员信息
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_save_delivery_staff(
+ p_id UUID DEFAULT NULL,
+ p_nickname TEXT DEFAULT NULL,
+ p_avatar TEXT DEFAULT NULL,
+ p_phone TEXT DEFAULT NULL,
+ p_status SMALLINT DEFAULT 1
+)
+RETURNS UUID
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_id UUID;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 参数校验
+ IF p_nickname IS NULL OR p_phone IS NULL THEN
+ RAISE EXCEPTION 'Missing required fields: nickname or phone';
+ END IF;
+
+ -- 3. 新增或更新
+ IF p_id IS NULL THEN
+ INSERT INTO public.ml_delivery_staff (
+ nickname, avatar, phone, status
+ ) VALUES (
+ p_nickname, p_avatar, p_phone, p_status
+ ) RETURNING id INTO v_id;
+ ELSE
+ UPDATE public.ml_delivery_staff
+ SET
+ nickname = COALESCE(p_nickname, nickname),
+ avatar = COALESCE(p_avatar, avatar),
+ phone = COALESCE(p_phone, phone),
+ status = COALESCE(p_status, status),
+ updated_at = now()
+ WHERE id = p_id
+ RETURNING id INTO v_id;
+
+ IF v_id IS NULL THEN
+ RAISE EXCEPTION 'Delivery staff not found';
+ END IF;
+ END IF;
+
+ RETURN v_id;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_save_delivery_staff(UUID, TEXT, TEXT, TEXT, SMALLINT) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_save_delivery_staff(UUID, TEXT, TEXT, TEXT, SMALLINT) TO authenticated;
diff --git a/docs/sql/30_rpc/delivery/rpc_admin_save_delivery_station_v1.sql b/docs/sql/30_rpc/delivery/rpc_admin_save_delivery_station_v1.sql
new file mode 100644
index 00000000..9803f22f
--- /dev/null
+++ b/docs/sql/30_rpc/delivery/rpc_admin_save_delivery_station_v1.sql
@@ -0,0 +1,71 @@
+-- RPC: rpc_admin_save_delivery_station
+-- 管理端新增或更新提货点/核销点信息
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_save_delivery_station(
+ p_id UUID DEFAULT NULL,
+ p_name TEXT DEFAULT NULL,
+ p_phone TEXT DEFAULT NULL,
+ p_address TEXT DEFAULT NULL,
+ p_image TEXT DEFAULT NULL,
+ p_lng NUMERIC DEFAULT NULL,
+ p_lat NUMERIC DEFAULT NULL,
+ p_status SMALLINT DEFAULT 1,
+ p_sort_order INTEGER DEFAULT 0,
+ p_business_hours JSONB DEFAULT NULL
+)
+RETURNS UUID
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_id UUID;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE auth_id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 参数校验
+ IF p_name IS NULL OR p_phone IS NULL OR p_address IS NULL THEN
+ RAISE EXCEPTION 'Missing required fields: name, phone or address';
+ END IF;
+
+ -- 3. 新增或更新
+ IF p_id IS NULL THEN
+ INSERT INTO public.ml_delivery_stations (
+ name, phone, address, image, lng, lat, status, sort_order, business_hours
+ ) VALUES (
+ p_name, p_phone, p_address, p_image, p_lng, p_lat, p_status, p_sort_order, p_business_hours
+ ) RETURNING id INTO v_id;
+ ELSE
+ UPDATE public.ml_delivery_stations
+ SET
+ name = COALESCE(p_name, name),
+ phone = COALESCE(p_phone, phone),
+ address = COALESCE(p_address, address),
+ image = COALESCE(p_image, image),
+ lng = COALESCE(p_lng, lng),
+ lat = COALESCE(p_lat, lat),
+ status = COALESCE(p_status, status),
+ sort_order = COALESCE(p_sort_order, sort_order),
+ business_hours = COALESCE(p_business_hours, business_hours),
+ updated_at = now()
+ WHERE id = p_id
+ RETURNING id INTO v_id;
+
+ IF v_id IS NULL THEN
+ RAISE EXCEPTION 'Station not found';
+ END IF;
+ END IF;
+
+ RETURN v_id;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_save_delivery_station(UUID, TEXT, TEXT, TEXT, TEXT, NUMERIC, NUMERIC, SMALLINT, INTEGER, JSONB) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_save_delivery_station(UUID, TEXT, TEXT, TEXT, TEXT, NUMERIC, NUMERIC, SMALLINT, INTEGER, JSONB) TO authenticated;
diff --git a/pages/mall/admin/app/mobile/config.uvue b/pages/mall/admin/app/mobile/config.uvue
index 328700d8..fe236ca4 100644
--- a/pages/mall/admin/app/mobile/config.uvue
+++ b/pages/mall/admin/app/mobile/config.uvue
@@ -8,17 +8,17 @@
APPID:
-
+
微信开放平台申请移动应用后给予的APPID
AppSecret:
-
+
微信开放平台申请移动应用后给予的AppSecret
-
+
@@ -26,7 +26,40 @@
-
diff --git a/pages/mall/admin/maintain/sys/info.uvue b/pages/mall/admin/maintain/sys/info.uvue
index cc30e78d..99504dd2 100644
--- a/pages/mall/admin/maintain/sys/info.uvue
+++ b/pages/mall/admin/maintain/sys/info.uvue
@@ -13,7 +13,7 @@
- ZC2884891
+ {{ info?.auth_id || '检测中...' }}
进入官网
@@ -22,29 +22,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ 系统环境加载中...
+
@@ -158,13 +153,10 @@ function editCopyright() {
color: #333;
}
-/* 各表格占位列宽 */
.col-title { flex: 1; }
-.col-text { flex: 1; }
-.col-image { flex: 1; }
.col-env { flex: 1; }
.col-req { flex: 1; }
-.col-status { flex: 1; }
+.col-status { flex: 1; font-family: monospace; }
.col-action { width: 150px; justify-content: flex-end; }
.action-btn {
@@ -175,4 +167,14 @@ function editCopyright() {
.mt-24 {
margin-top: 24px;
}
+
+.loading-overlay {
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background-color: rgba(255,255,255,0.6);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 999;
+}
diff --git a/pages/mall/admin/setting/auth/admin.uvue b/pages/mall/admin/setting/auth/admin.uvue
index 893a3025..8fc1878b 100644
--- a/pages/mall/admin/setting/auth/admin.uvue
+++ b/pages/mall/admin/setting/auth/admin.uvue
@@ -5,15 +5,13 @@
状态:
-
- {{ statusText }}
-
+
搜索:
-
+
-
+
@@ -21,7 +19,7 @@
-
+
-
- 暂无数据
+
+ 加载中...
+
+ 暂无管理员数据
+
+
+ {{ item.real_name || '-' }}
+ {{ item.username }}
+
+
+ {{ role }}
+ -
+
+
+ {{ formatTime(item.last_login_at) }}
+ {{ item.last_login_ip || '-' }}
+
+
+
+
+ 编辑
+
+ 删除
+
+
+
+
+
+
+
@@ -42,136 +73,101 @@
-
diff --git a/pages/mall/admin/setting/auth/permission.uvue b/pages/mall/admin/setting/auth/permission.uvue
index 3403bab8..5ef2f815 100644
--- a/pages/mall/admin/setting/auth/permission.uvue
+++ b/pages/mall/admin/setting/auth/permission.uvue
@@ -1,188 +1,263 @@
-
-
-
- 按钮名称:
-
-
-
+
+
+
+
-
-
+
+
+
-
+
+ 加载中...
+
+
+ 暂无数据
+
+
+
+
- ▶
- {{ item.type }}
- {{ item.sort }}
+ {{ item.code }}
-
+
+ {{ item.type === 'menu' ? '菜单' : '按钮' }}
+
-
- 编辑
+ {{ item.sort_order }}
+
+
+
+
+
+ 新增子项
+ |
+ 编辑
+ |
+ 删除
+
+
+
+
+
+
+
+
+
+ 父级ID:
+
+
+
+ 名称:
+
+
+
+ 编码:
+
+
+
+ 类型:
+ form.type = e.detail.value">
+
+
+
+
+
+ 路由路径:
+
+
+
+ 排序:
+
+
+
+
+
+
+
-
diff --git a/pages/mall/admin/setting/auth/role.uvue b/pages/mall/admin/setting/auth/role.uvue
index ef9abf5c..ee050095 100644
--- a/pages/mall/admin/setting/auth/role.uvue
+++ b/pages/mall/admin/setting/auth/role.uvue
@@ -5,15 +5,13 @@
状态:
-
- {{ statusText }}
-
+
身份昵称:
-
+
-
+
@@ -21,17 +19,70 @@
-
+
-
+
+ 加载中...
+
+
暂无数据
+
+ {{ (page - 1) * pageSize + index + 1 }}
+ {{ item.name }} ({{ item.code }})
+
+
+
+
+ 编辑
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 身份名称:
+
+
+
+ 身份编码:
+
+
+
+ 描述:
+
+
+
+
@@ -39,136 +90,176 @@
-
diff --git a/pages/mall/admin/setting/delivery/staff.uvue b/pages/mall/admin/setting/delivery/staff.uvue
index 906f93c5..bbf2ab8a 100644
--- a/pages/mall/admin/setting/delivery/staff.uvue
+++ b/pages/mall/admin/setting/delivery/staff.uvue
@@ -6,157 +6,253 @@
-
+
-
- {{ item.id }}
+
+ 加载中...
+
+
+ 暂无配送员数据
+
+
+ {{ (page - 1) * pageSize + index + 1 }}
-
+
+ 👤
- {{ item.name }}
- {{ item.phone }}
+ {{ item.nickname }}
+ {{ item.phone }}
-
+
- {{ item.addTime }}
+ {{ formatTime(item.created_at) }}
编辑
- 删除
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+ 名称:
+
+
+
+ 手机号:
+
+
+
+ 头像:
+
+
+ +
+
+
+
+
+
-
diff --git a/pages/mall/admin/setting/delivery/station.uvue b/pages/mall/admin/setting/delivery/station.uvue
index eb412d80..ebfa8015 100644
--- a/pages/mall/admin/setting/delivery/station.uvue
+++ b/pages/mall/admin/setting/delivery/station.uvue
@@ -5,16 +5,9 @@
提货点搜索:
-
+
-
-
-
-
-
- 显示中的提货点(2)
- 隐藏中的提货点(0)
- 回收站中的提货点(9)
+
@@ -22,209 +15,272 @@
-
+
-
- {{ item.id }}
+
+ 加载中...
+
+
+ 暂无提货点数据
+
+
+ {{ (page - 1) * pageSize + index + 1 }}
-
+
+ 🖼️
- {{ item.name }}
- {{ item.phone }}
- {{ item.address }}
- {{ item.hours }}
+ {{ item.name }}
+ {{ item.phone }}
+ {{ item.address }}
-
+
编辑
- 删除
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+ 提货点名称:
+
+
+
+ 联系电话:
+
+
+
+ 详细地址:
+
+
+
+ 展示图片:
+
+
+ +
+
+
+
+
+
+
-
diff --git a/pages/mall/admin/setting/system/config.uvue b/pages/mall/admin/setting/system/config.uvue
index 6d9ac126..b19bcf98 100644
--- a/pages/mall/admin/setting/system/config.uvue
+++ b/pages/mall/admin/setting/system/config.uvue
@@ -2,332 +2,234 @@
-
+
-
-
-
- {{ tab.name }}
-
+
+
+
+ {{ tab.name }}
+
+
-
-
-
-
-
-
-
-
-
-
- 站点开启:
-
-
-
-
-
- 站点开启/关闭(用于升级等临时关闭),关闭后前端会弹窗显示站点升级中,请稍后访问
-
-
-
- 网站名称:
-
-
- 网站名称很多地方会显示的,建议认真填写
-
-
-
- 网站地址:
-
-
- 安装自动配置,不要轻易修改,更换后会影响网站访问、接口请求、本地文件储存、支付回调、微信授权、支付、小程序图片访问、部分二维码、官方授权等
-
-
-
- 消息队列:
-
-
-
-
-
- 是否启用消息队列,启用后提升程序运行速度,启用前必须配置Redis缓存,文档地址:https://doc.crmeb.com/single/crmeb_v4/7217
-
-
-
- 联系电话:
-
-
- 联系电话
-
-
-
- 授权密钥:
-
-
-
-
+
-
-
-
- 分享图片:
-
- 上传图片
- 分享图片比例5:4,建议小于50KB
-
+
+
+
+ 加载配置中...
-
- 分享标题:
-
-
+
+
+
+
+
+ 站点开启:
+
+
+
+
+
+ 站点开启/关闭(用于升级等临时关闭)
+
+
+
+ 网站名称:
+
+
+
+
+
+ 网站地址:
+
+
+
+
+
+ 联系电话:
+
+
+
+
-
-
- 分享简介:
-
-
-
-
-
-
-
-
- 后台登录LOGO:
-
- 上传截图
- 建议尺寸270*75
+
+
+
+ 分享标题:
+
+
+
+
+
+ 分享简介:
+
+
+
+
-
-
- 后台小LOGO:
-
- 上传图片
- 建议尺寸180*180
-
-
-
- 后台大LOGO:
-
- 上传图片
- 建议尺寸170*50
-
-
-
-
-
-
- 移动端JS:
-
-
+
+
+
+ 后台登录LOGO:
+
+
+
+ 上传图片
+
+
+
-
-
- 管理端JS:
-
-
-
-
-
- PC端JS:
-
-
-
-
-
-
-
-
- 腾讯地图KEY:
-
-
- 申请地址:https://lbs.qq.com
+
+
+
+ WAF类型:
+
+
+
+
+
+
+
+
+
+ WAF规则:
+
+
+
+
-
-
-
-
-
- 备案号:
-
-
+
+
+
+
+
+
-
- ICP链接:
-
-
-
-
-
-
-
-
-
- 功能开启:
-
-
-
-
-
-
-
-
-
-
-
-
-
- 远程登录地址:
-
-
-
-
-
-
-
-
-
- WAF类型:
-
-
-
-
-
-
- WAF类型:关闭(所有参数都能正常请求),拦截(匹配到WAF配置的参数阻断接口请求),过滤(匹配到WAF配置的参数过滤参数,正常请求接口)
-
-
-
- WAF配置:
-
-
- WAF配置验证参数,过滤掉不需要的参数或拦截请求,多个参数用回车换行分隔
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+.submit-section { display: flex; flex-direction: row; margin-top: 20px; }
+.btn-submit { background-color: #1890ff; border: none; padding: 0 24px; height: 36px; border-radius: 4px; cursor: pointer; }
+.btn-txt { color: #fff; font-size: 14px; }
+.btn-submit[disabled] { opacity: 0.6; }
+
diff --git a/pages/mall/admin/statistic/index.uvue b/pages/mall/admin/statistic/index.uvue
index 2526f6d2..ac7142ef 100644
--- a/pages/mall/admin/statistic/index.uvue
+++ b/pages/mall/admin/statistic/index.uvue
@@ -1,81 +1,177 @@
-
-
-
-
-
- 页面占位
- 该功能模块正在开发中
- 当前采用 CRMEB 路由体系 1:1 映射
+
+
+
+
+
+ {{ item.icon }}
+
+
+ {{ item.value }}
+ {{ item.label }}
+
+
+
+
+ 今日实时
+ 更新时间:{{ currentTime }}
+
+
+
+ {{ item.label }}
+ {{ item.value }}
+
+
+
+
+
+ 待办提醒
+
+
+
+
+ {{ item.label }}
+ {{ item.value }}
+
+
+ 去处理 >
+
+
+
+
+
+
+
+
+ 趋势图表正在对接中...
+
+
+
+
+ 数据聚合中...
+
diff --git a/services/admin/authService.uts b/services/admin/authService.uts
new file mode 100644
index 00000000..aa538f57
--- /dev/null
+++ b/services/admin/authService.uts
@@ -0,0 +1,144 @@
+import { rpcOrNull, rpcOrValue, rpcOrEmptyArray } from '@/services/analytics/rpc.uts'
+
+/**
+ * 管理员模型
+ */
+export type AdminUser = {
+ id: string
+ username: string
+ real_name: string | null
+ role: string
+ is_active: boolean
+ last_login_at: string | null
+ last_login_ip: string | null
+ roles: string[] | null
+}
+
+/**
+ * 角色模型
+ */
+export type AdminRole = {
+ id: string
+ name: string
+ code: string
+ description: string | null
+ is_active: boolean
+ created_at: string
+ updated_at: string
+}
+
+/**
+ * 权限/菜单模型
+ */
+export type AdminPermission = {
+ id: string
+ parent_id: string | null
+ name: string
+ code: string
+ type: string
+ path: string | null
+ icon: string | null
+ sort_order: number
+ is_visible: boolean
+ created_at: string
+ updated_at: string
+}
+
+/**
+ * 获取管理员列表
+ */
+export async function fetchAdminPage(
+ page: number,
+ pageSize: number,
+ search: string | null = null,
+ status: number | null = null
+): Promise<{ total: number, items: Array }> {
+ const res = await rpcOrNull('rpc_admin_get_admin_list', {
+ p_page: page,
+ p_page_size: pageSize,
+ p_search: search,
+ p_status: status
+ } as UTSJSONObject)
+
+ if (res == null) return { total: 0, items: [] as Array }
+ return {
+ total: (res as any).total as number,
+ items: (res as any).items as Array
+ }
+}
+
+/**
+ * 获取角色列表
+ */
+export async function fetchRolePage(
+ page: number,
+ pageSize: number,
+ search: string | null = null
+): Promise<{ total: number, items: Array }> {
+ const res = await rpcOrNull('rpc_admin_get_role_list', {
+ p_page: page,
+ p_page_size: pageSize,
+ p_search: search
+ } as UTSJSONObject)
+
+ if (res == null) return { total: 0, items: [] as Array }
+ return {
+ total: (res as any).total as number,
+ items: (res as any).items as Array
+ }
+}
+
+/**
+ * 保存角色
+ */
+export async function saveRole(role: any): Promise {
+ const res = await rpcOrValue('rpc_admin_save_role', {
+ p_id: role.id,
+ p_name: role.name,
+ p_code: role.code,
+ p_description: role.description,
+ p_is_active: role.is_active
+ } as any)
+ return res != null ? String(res) : null
+}
+
+/**
+ * 删除角色
+ */
+export async function deleteRole(id: string): Promise {
+ const ok = await rpcOrValue('rpc_admin_delete_role', { p_id: id } as any)
+ return ok === true
+}
+
+/**
+ * 获取权限菜单树列表
+ */
+export async function fetchPermissionList(): Promise> {
+ return await rpcOrEmptyArray('rpc_admin_get_permission_list', {} as any) as Array
+}
+
+/**
+ * 保存权限菜单
+ */
+export async function savePermission(permission: any): Promise {
+ const res = await rpcOrValue('rpc_admin_save_permission', {
+ p_id: permission.id,
+ p_parent_id: permission.parent_id,
+ p_name: permission.name,
+ p_code: permission.code,
+ p_type: permission.type,
+ p_path: permission.path,
+ p_icon: permission.icon,
+ p_sort_order: permission.sort_order,
+ p_is_visible: permission.is_visible
+ } as any)
+ return res != null ? String(res) : null
+}
+
+/**
+ * 删除权限菜单
+ */
+export async function deletePermission(id: string): Promise {
+ const ok = await rpcOrValue('rpc_admin_delete_permission', { p_id: id } as any)
+ return ok === true
+}
diff --git a/services/admin/deliveryService.uts b/services/admin/deliveryService.uts
new file mode 100644
index 00000000..1de939c9
--- /dev/null
+++ b/services/admin/deliveryService.uts
@@ -0,0 +1,129 @@
+import { rpcOrNull, rpcOrValue } from '@/services/analytics/rpc.uts'
+
+/**
+ * 配送员模型
+ */
+export type DeliveryStaff = {
+ id: string
+ uid: string | null
+ nickname: string
+ avatar: string | null
+ phone: string
+ status: number
+ is_active: boolean
+ created_at: string
+ updated_at: string
+}
+
+/**
+ * 提货点模型
+ */
+export type DeliveryStation = {
+ id: string
+ name: string
+ phone: string
+ address: string
+ image: string | null
+ lng: number | null
+ lat: number | null
+ status: number
+ sort_order: number
+ business_hours: UTSJSONObject | null
+ created_at: string
+ updated_at: string
+}
+
+/**
+ * 获取配送员列表
+ */
+export async function fetchDeliveryStaffPage(
+ page: number,
+ pageSize: number,
+ search: string | null = null,
+ status: number | null = null
+): Promise<{ total: number, items: Array }> {
+ const res = await rpcOrNull('rpc_admin_get_delivery_staff_list', {
+ p_page: page,
+ p_page_size: pageSize,
+ p_search: search,
+ p_status: status
+ } as UTSJSONObject)
+
+ if (res == null) return { total: 0, items: [] as Array }
+ return {
+ total: (res as any).total as number,
+ items: (res as any).items as Array
+ }
+}
+
+/**
+ * 保存配送员
+ */
+export async function saveDeliveryStaff(staff: any): Promise {
+ const res = await rpcOrValue('rpc_admin_save_delivery_staff', {
+ p_id: staff.id,
+ p_nickname: staff.nickname,
+ p_avatar: staff.avatar,
+ p_phone: staff.phone,
+ p_status: staff.status
+ } as any)
+ return res != null ? String(res) : null
+}
+
+/**
+ * 删除配送员
+ */
+export async function deleteDeliveryStaff(id: string): Promise {
+ const ok = await rpcOrValue('rpc_admin_delete_delivery_staff', { p_id: id } as any)
+ return ok === true
+}
+
+/**
+ * 获取提货点列表
+ */
+export async function fetchDeliveryStationPage(
+ page: number,
+ pageSize: number,
+ search: string | null = null,
+ status: number | null = null
+): Promise<{ total: number, items: Array }> {
+ const res = await rpcOrNull('rpc_admin_get_delivery_station_list', {
+ p_page: page,
+ p_page_size: pageSize,
+ p_search: search,
+ p_status: status
+ } as UTSJSONObject)
+
+ if (res == null) return { total: 0, items: [] as Array }
+ return {
+ total: (res as any).total as number,
+ items: (res as any).items as Array
+ }
+}
+
+/**
+ * 保存提货点
+ */
+export async function saveDeliveryStation(station: any): Promise {
+ const res = await rpcOrValue('rpc_admin_save_delivery_station', {
+ p_id: station.id,
+ p_name: station.name,
+ p_phone: station.phone,
+ p_address: station.address,
+ p_image: station.image,
+ p_lng: station.lng,
+ p_lat: station.lat,
+ p_status: station.status,
+ p_sort_order: station.sort_order,
+ p_business_hours: station.business_hours
+ } as any)
+ return res != null ? String(res) : null
+}
+
+/**
+ * 删除提货点
+ */
+export async function deleteDeliveryStation(id: string): Promise {
+ const ok = await rpcOrValue('rpc_admin_delete_delivery_station', { p_id: id } as any)
+ return ok === true
+}
diff --git a/services/admin/maintainService.uts b/services/admin/maintainService.uts
new file mode 100644
index 00000000..6aeb2840
--- /dev/null
+++ b/services/admin/maintainService.uts
@@ -0,0 +1,22 @@
+import { rpcOrNull } from '@/services/analytics/rpc.uts'
+
+/**
+ * 系统信息模型
+ */
+export type SystemInfo = {
+ server_os : string
+ web_server : string
+ db_engine : string
+ db_version : string
+ uts_runtime : string
+ auth_id : string
+}
+
+/**
+ * 获取服务器及系统环境信息
+ */
+export async function fetchSystemInfo() : Promise {
+ const res = await rpcOrNull('rpc_admin_get_system_info', {} as UTSJSONObject)
+ if (res == null) return null
+ return res as SystemInfo
+}
diff --git a/services/admin/systemConfigService.uts b/services/admin/systemConfigService.uts
new file mode 100644
index 00000000..8a0d8d81
--- /dev/null
+++ b/services/admin/systemConfigService.uts
@@ -0,0 +1,41 @@
+import { rpcOrNull, rpcOrValue } from '@/services/analytics/rpc.uts'
+
+/**
+ * 系统配置项模型
+ */
+export type SystemConfig = {
+ config_key : string
+ config_value : UTSJSONObject
+ description ?: string
+ updated_at ?: string
+}
+
+/**
+ * 获取指定键的系统配置
+ */
+export async function getSystemConfig(key : string) : Promise {
+ const res = await rpcOrNull('rpc_admin_system_config_get', {
+ p_key: key
+ } as UTSJSONObject)
+
+ if (res == null) return null
+ return res as UTSJSONObject
+}
+
+/**
+ * 保存/更新系统配置
+ */
+export async function saveSystemConfig(key : string, value : UTSJSONObject, description : string | null = null) : Promise {
+ const ok = await rpcOrValue('rpc_admin_system_config_save', {
+ p_key: key,
+ p_value: value,
+ p_description: description
+ } as any)
+
+ return ok === true
+}
+
+/**
+ * 批量获取配置 (可选扩展)
+ * 逻辑:如果后续有需求,可以补齐 rpc_admin_system_config_batch_get
+ */