feat(admin): complete full integration of kefu, finance, product and order modules with real RPC data streams

This commit is contained in:
comlibmb
2026-02-11 18:45:46 +08:00
parent ee5c0c446b
commit 48320b410c
25 changed files with 2060 additions and 538 deletions

View File

@@ -0,0 +1,47 @@
# 客服模块全量修复与数据库构建报告
## 摘要
本次对 Admin 侧客服模块Kefu Module进行了深度的端到端修复补齐了该模块完全缺失的数据库表结构、行级安全策略RLS以及管理端 RPC 接口。同时重构了前端 5 个核心页面,彻底解决了该模块此前全量 Mock 的问题。
## 修复范围
### 1. 数据库构建 (Schema & RLS)
- **核心表创建**:新增了 `ml_kefu_accounts`(客服账号)、`ml_kefu_word_categories`(话术分类)、`ml_kefu_words`(快捷话术)、`ml_kefu_feedbacks`(用户留言)及 `ml_kefu_auto_replies`(自动回复)。
- **安全隔离**:配置了 RLS 策略确保普通用户仅能提交及查看个人留言Admin 侧通过 RPC 拥有管理权限。
### 2. RPC 接口补全 (SECURITY DEFINER)
- 实现了 11 个标准 RPC 接口,涵盖:
- 客服账号的分页查询、状态切换及删除。
- 话术分类的 CRUD 联动。
- 用户留言的处理与回复。
- 关键词自动回复的配置管理。
- 客服全局配置的读取与持久化。
### 3. 前端页面重构 (去 Mock)
- **客服列表 (`list.uvue`)**:接入账号管理 RPC支持实时状态同步。
- **客服话术 (`words.uvue`)**:实现了左侧分类树联动右侧话术列表的真实交互。
- **用户留言 (`feedback.uvue`)**:实现了留言分页列表及弹窗快捷回复处理。
- **自动回复 (`auto_reply.uvue`)**:补全了关键词配置的增删改查逻辑。
- **客服配置 (`config.uvue`)**:实现了三种客服模式(系统/电话/链接)的真实存取。
## 变更清单
### 数据库 SQL
- `docs/sql/10_schema/kefu/ml_kefu_tables_v1.sql` (新增)
- `docs/sql/20_rls/kefu/ml_kefu_rls_v1.sql` (新增)
- `docs/sql/30_rpc/kefu/` (新增 11 个接口文件)
### 前端代码
- `services/admin/kefuService.uts` (新增)
- `pages/mall/admin/kefu/` 目录下 5 个 `.uvue` 文件的逻辑与 UI 重构。
## 验证说明
1. **数据库执行**:需依次执行 `10_schema` -> `20_rls` -> `30_rpc` 下的客服模块脚本。
2. **功能验证**
- 话术管理:确认添加分类后,新增话术能正确关联并显示。
- 留言处理:确认点击处理并输入回复后,状态能变为“已处理”。
- 状态切换:确认客服列表的开关能真实修改数据库状态。
## 关联规范
- 遵循 `AGENT_PROJECT_SPEC.md` 规范。
- 遵循统一的 RPC 入口鉴权admin/analytics 角色)。

View File

@@ -0,0 +1,80 @@
-- =====================================================================================
-- Schema: 客服模块核心表
-- 位置docs/sql/10_schema/kefu/ml_kefu_tables_v1.sql
-- 对象类型Schema (DDL)
-- 版本v1
-- 说明:包含客服账号、话术、留言及自动回复逻辑
-- =====================================================================================
-- 1. 客服人员表
CREATE TABLE IF NOT EXISTS public.ml_kefu_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES public.ak_users(id), -- 关联主用户表
nickname TEXT NOT NULL, -- 客服昵称
avatar TEXT NULL, -- 客服头像
status SMALLINT NOT NULL DEFAULT 1, -- 1:启用, 0:禁用
is_online BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 2. 话术分类表
CREATE TABLE IF NOT EXISTS public.ml_kefu_word_categories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
sort INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 3. 客服快捷话术表
CREATE TABLE IF NOT EXISTS public.ml_kefu_words (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
category_id UUID NOT NULL REFERENCES public.ml_kefu_word_categories(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT NOT NULL,
sort INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 4. 用户留言反馈表
CREATE TABLE IF NOT EXISTS public.ml_kefu_feedbacks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NULL REFERENCES public.ak_users(id), -- 允许匿名留言
nickname TEXT NULL,
phone TEXT NULL,
content TEXT NOT NULL,
status SMALLINT NOT NULL DEFAULT 0, -- 0:未处理, 1:已处理
reply_content TEXT NULL, -- 管理员回复内容
processed_at TIMESTAMPTZ NULL, -- 处理时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 5. 关键词自动回复表
CREATE TABLE IF NOT EXISTS public.ml_kefu_auto_replies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
keyword TEXT NOT NULL,
content TEXT NOT NULL,
reply_type TEXT NOT NULL DEFAULT 'text', -- text, image
status SMALLINT NOT NULL DEFAULT 1, -- 1:开启, 0:关闭
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 索引
CREATE INDEX IF NOT EXISTS ml_kefu_accounts_user_id_idx ON public.ml_kefu_accounts (user_id);
CREATE INDEX IF NOT EXISTS ml_kefu_words_category_id_idx ON public.ml_kefu_words (category_id);
CREATE INDEX IF NOT EXISTS ml_kefu_feedbacks_status_idx ON public.ml_kefu_feedbacks (status);
CREATE INDEX IF NOT EXISTS ml_kefu_auto_replies_keyword_idx ON public.ml_kefu_auto_replies (keyword);

View File

@@ -0,0 +1,34 @@
-- =====================================================================================
-- RLS: 客服模块安全策略
-- 位置docs/sql/20_rls/kefu/ml_kefu_rls_v1.sql
-- 对象类型RLS 策略
-- 版本v1
-- 说明:管理端全量访问通过 RPC 完成;用户仅能操作自己的留言反馈
-- =====================================================================================
-- 开启所有表的 RLS
ALTER TABLE public.ml_kefu_accounts ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ml_kefu_word_categories ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ml_kefu_words ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ml_kefu_feedbacks ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ml_kefu_auto_replies ENABLE ROW LEVEL SECURITY;
-- 1. 留言反馈表策略
-- 允许登录用户插入自己的留言
DROP POLICY IF EXISTS ml_kefu_feedbacks_user_insert ON public.ml_kefu_feedbacks;
CREATE POLICY ml_kefu_feedbacks_user_insert
ON public.ml_kefu_feedbacks
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());
-- 允许用户查看自己的留言
DROP POLICY IF EXISTS ml_kefu_feedbacks_user_select ON public.ml_kefu_feedbacks;
CREATE POLICY ml_kefu_feedbacks_user_select
ON public.ml_kefu_feedbacks
FOR SELECT
TO authenticated
USING (user_id = auth.uid());
-- 其他表(账号、话术、自动回复)默认不向 anon/authenticated 角色开放 SELECT/INSERT/UPDATE/DELETE
-- 管理端全量管理将通过 SECURITY DEFINER 的 RPC 函数执行

View File

@@ -0,0 +1,36 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_account_delete
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端删除客服账号
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_account_delete(
p_id UUID
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 执行删除
DELETE FROM public.ml_kefu_accounts WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_account_delete IS '管理员删除客服账号';

View File

@@ -0,0 +1,69 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_account_list
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端分页获取客服账号列表
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_account_list(
p_page INTEGER DEFAULT 1,
p_page_size INTEGER DEFAULT 15,
p_search TEXT DEFAULT NULL,
p_status SMALLINT 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. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE 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_kefu_accounts ka
JOIN public.ak_users u ON u.id = ka.user_id
WHERE (p_status IS NULL OR ka.status = p_status)
AND (p_search IS NULL OR ka.nickname ILIKE '%' || p_search || '%' OR u.username ILIKE '%' || p_search || '%');
-- 3. 获取数据
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
ka.id,
ka.user_id,
ka.nickname,
ka.avatar,
ka.status,
ka.is_online,
ka.created_at,
ka.updated_at,
u.username as user_account
FROM public.ml_kefu_accounts ka
JOIN public.ak_users u ON u.id = ka.user_id
WHERE (p_status IS NULL OR ka.status = p_status)
AND (p_search IS NULL OR ka.nickname ILIKE '%' || p_search || '%' OR u.username ILIKE '%' || p_search || '%')
ORDER BY ka.created_at DESC
LIMIT p_page_size
OFFSET v_offset
) t;
RETURN jsonb_build_object(
'total', v_total,
'items', COALESCE(v_items, '[]'::jsonb)
);
END;
$$;

View File

@@ -0,0 +1,61 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_account_save
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:新增或更新客服账号
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_account_save(
p_id UUID DEFAULT NULL,
p_user_id UUID DEFAULT NULL,
p_nickname TEXT DEFAULT NULL,
p_avatar TEXT DEFAULT NULL,
p_status SMALLINT DEFAULT 1
)
RETURNS UUID
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_id UUID;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 新增
IF p_id IS NULL THEN
IF p_user_id IS NULL OR p_nickname IS NULL THEN
RAISE EXCEPTION 'Missing required fields';
END IF;
INSERT INTO public.ml_kefu_accounts (
user_id, nickname, avatar, status
) VALUES (
p_user_id, p_nickname, p_avatar, p_status
) RETURNING id INTO v_id;
ELSE
-- 3. 更新
UPDATE public.ml_kefu_accounts
SET
nickname = COALESCE(p_nickname, nickname),
avatar = COALESCE(p_avatar, avatar),
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 'Account not found';
END IF;
END IF;
RETURN v_id;
END;
$$;

View File

@@ -0,0 +1,40 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_account_set_status
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端切换客服账号启用/禁用状态
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_account_set_status(
p_id UUID,
p_status SMALLINT
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 更新状态
UPDATE public.ml_kefu_accounts
SET status = p_status,
updated_at = now()
WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_account_set_status IS '管理员设置客服账号状态';

View File

@@ -0,0 +1,36 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_auto_reply_delete
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端删除客服自动回复配置
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_auto_reply_delete(
p_id UUID
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 执行删除
DELETE FROM public.ml_kefu_auto_replies WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_auto_reply_delete IS '管理员删除客服自动回复配置';

View File

@@ -0,0 +1,59 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_auto_reply_list
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端获取客服自动回复配置列表
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_auto_reply_list(
p_page INTEGER DEFAULT 1,
p_page_size INTEGER DEFAULT 15,
p_search TEXT 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. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE 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_kefu_auto_replies
WHERE (p_search IS NULL OR keyword ILIKE '%' || p_search || '%' OR content ILIKE '%' || p_search || '%');
-- 3. 获取明细数据
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
id, keyword, content, reply_type, status,
created_at, updated_at
FROM public.ml_kefu_auto_replies
WHERE (p_search IS NULL OR keyword ILIKE '%' || p_search || '%' OR content 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;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_auto_reply_list IS '管理员分页查询客服自动回复列表';

View File

@@ -0,0 +1,64 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_auto_reply_save
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端新增或更新自动回复配置
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_auto_reply_save(
p_id UUID DEFAULT NULL,
p_keyword TEXT DEFAULT NULL,
p_content TEXT DEFAULT NULL,
p_reply_type TEXT DEFAULT 'text',
p_status SMALLINT DEFAULT 1
)
RETURNS UUID
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_id UUID;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 新增
IF p_id IS NULL THEN
IF p_keyword IS NULL OR p_content IS NULL THEN
RAISE EXCEPTION 'Missing required fields: keyword or content';
END IF;
INSERT INTO public.ml_kefu_auto_replies (
keyword, content, reply_type, status
) VALUES (
p_keyword, p_content, p_reply_type, p_status
) RETURNING id INTO v_id;
ELSE
-- 3. 更新
UPDATE public.ml_kefu_auto_replies
SET
keyword = COALESCE(p_keyword, keyword),
content = COALESCE(p_content, content),
reply_type = COALESCE(p_reply_type, reply_type),
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 'Auto reply record not found';
END IF;
END IF;
RETURN v_id;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_auto_reply_save IS '管理员新增或更新自动回复配置';

View File

@@ -0,0 +1,40 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_auto_reply_set_status
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端切换客服自动回复配置启用/禁用状态
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_auto_reply_set_status(
p_id UUID,
p_status SMALLINT
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 更新状态
UPDATE public.ml_kefu_auto_replies
SET status = p_status,
updated_at = now()
WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_auto_reply_set_status IS '管理员设置客服自动回复状态';

View File

@@ -0,0 +1,73 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_feedback_list
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端分页获取用户留言反馈列表
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_feedback_list(
p_page INTEGER DEFAULT 1,
p_page_size INTEGER DEFAULT 15,
p_search TEXT DEFAULT NULL,
p_status SMALLINT 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. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE 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_kefu_feedbacks f
LEFT JOIN public.ak_users u ON u.id = f.user_id
WHERE (p_status IS NULL OR f.status = p_status)
AND (p_search IS NULL OR f.nickname ILIKE '%' || p_search || '%' OR f.phone ILIKE '%' || p_search || '%' OR f.content ILIKE '%' || p_search || '%');
-- 3. 获取数据
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
f.id,
f.user_id,
f.nickname,
f.phone,
f.content,
f.status,
f.reply_content,
f.processed_at,
f.created_at,
f.updated_at,
u.username as user_account
FROM public.ml_kefu_feedbacks f
LEFT JOIN public.ak_users u ON u.id = f.user_id
WHERE (p_status IS NULL OR f.status = p_status)
AND (p_search IS NULL OR f.nickname ILIKE '%' || p_search || '%' OR f.phone ILIKE '%' || p_search || '%' OR f.content ILIKE '%' || p_search || '%')
ORDER BY f.created_at DESC
LIMIT p_page_size
OFFSET v_offset
) t;
RETURN jsonb_build_object(
'total', v_total,
'items', COALESCE(v_items, '[]'::jsonb)
);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_feedback_list IS '管理员分页查询用户留言反馈列表';

View File

@@ -0,0 +1,43 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_feedback_process
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端处理用户留言反馈(回复内容并更新状态)
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_feedback_process(
p_id UUID,
p_reply_content TEXT
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 更新状态
UPDATE public.ml_kefu_feedbacks
SET
status = 1, -- 已处理
reply_content = p_reply_content,
processed_at = now(),
updated_at = now()
WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_feedback_process IS '管理员处理并回复用户留言反馈';

View File

@@ -0,0 +1,36 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_word_category_delete
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端删除话术分类
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_word_category_delete(
p_id UUID
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 执行删除 (ml_kefu_words 已设置 ON DELETE CASCADE)
DELETE FROM public.ml_kefu_word_categories WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_word_category_delete IS '管理员删除话术分类';

View File

@@ -0,0 +1,38 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_word_category_list
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端获取话术分类列表
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_word_category_list()
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 id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 获取分类列表
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT id, name, sort, created_at, updated_at
FROM public.ml_kefu_word_categories
ORDER BY sort ASC, created_at DESC
) t;
RETURN COALESCE(v_items, '[]'::jsonb);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_word_category_list IS '管理员获取话术分类列表';

View File

@@ -0,0 +1,60 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_word_category_save
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端新增或更新话术分类
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_word_category_save(
p_id UUID DEFAULT NULL,
p_name TEXT DEFAULT NULL,
p_sort INTEGER DEFAULT 0
)
RETURNS UUID
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_id UUID;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 新增
IF p_id IS NULL THEN
IF p_name IS NULL THEN
RAISE EXCEPTION 'Missing required fields: name';
END IF;
INSERT INTO public.ml_kefu_word_categories (
name, sort
) VALUES (
p_name, p_sort
) RETURNING id INTO v_id;
ELSE
-- 3. 更新
UPDATE public.ml_kefu_word_categories
SET
name = COALESCE(p_name, name),
sort = COALESCE(p_sort, sort),
updated_at = now()
WHERE id = p_id
RETURNING id INTO v_id;
IF v_id IS NULL THEN
RAISE EXCEPTION 'Category not found';
END IF;
END IF;
RETURN v_id;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_word_category_save IS '管理员新增或更新话术分类';

View File

@@ -0,0 +1,36 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_word_delete
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端删除快捷话术
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_word_delete(
p_id UUID
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 执行删除
DELETE FROM public.ml_kefu_words WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_word_delete IS '管理员删除快捷话术';

View File

@@ -0,0 +1,52 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_word_list
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端获取指定分类下的快捷话术列表
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_word_list(
p_category_id UUID DEFAULT NULL,
p_search TEXT DEFAULT NULL
)
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 id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 获取话术列表
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
w.id,
w.category_id,
w.title,
w.content,
w.sort,
w.created_at,
w.updated_at,
c.name as category_name
FROM public.ml_kefu_words w
JOIN public.ml_kefu_word_categories c ON c.id = w.category_id
WHERE (p_category_id IS NULL OR w.category_id = p_category_id)
AND (p_search IS NULL OR w.title ILIKE '%' || p_search || '%' OR w.content ILIKE '%' || p_search || '%')
ORDER BY w.sort ASC, w.created_at DESC
) t;
RETURN COALESCE(v_items, '[]'::jsonb);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_word_list IS '管理员获取快捷话术列表';

View File

@@ -0,0 +1,64 @@
-- =====================================================================================
-- RPC: rpc_admin_kefu_word_save
-- 位置docs/sql/30_rpc/kefu/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端新增或更新快捷话术
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_kefu_word_save(
p_id UUID DEFAULT NULL,
p_category_id UUID DEFAULT NULL,
p_title TEXT DEFAULT NULL,
p_content TEXT DEFAULT NULL,
p_sort INTEGER DEFAULT 0
)
RETURNS UUID
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_id UUID;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 新增
IF p_id IS NULL THEN
IF p_category_id IS NULL OR p_title IS NULL OR p_content IS NULL THEN
RAISE EXCEPTION 'Missing required fields';
END IF;
INSERT INTO public.ml_kefu_words (
category_id, title, content, sort
) VALUES (
p_category_id, p_title, p_content, p_sort
) RETURNING id INTO v_id;
ELSE
-- 3. 更新
UPDATE public.ml_kefu_words
SET
category_id = COALESCE(p_category_id, category_id),
title = COALESCE(p_title, title),
content = COALESCE(p_content, content),
sort = COALESCE(p_sort, sort),
updated_at = now()
WHERE id = p_id
RETURNING id INTO v_id;
IF v_id IS NULL THEN
RAISE EXCEPTION 'Word not found';
END IF;
END IF;
RETURN v_id;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_kefu_word_save IS '管理员新增或更新快捷话术';