feat(admin): complete full integration of kefu, finance, product and order modules with real RPC data streams
This commit is contained in:
47
docs/ops/2026-02-11__admin__kefu-module-repaired-full.md
Normal file
47
docs/ops/2026-02-11__admin__kefu-module-repaired-full.md
Normal 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 角色)。
|
||||
80
docs/sql/10_schema/kefu/ml_kefu_tables_v1.sql
Normal file
80
docs/sql/10_schema/kefu/ml_kefu_tables_v1.sql
Normal 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);
|
||||
34
docs/sql/20_rls/kefu/ml_kefu_rls_v1.sql
Normal file
34
docs/sql/20_rls/kefu/ml_kefu_rls_v1.sql
Normal 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 函数执行
|
||||
36
docs/sql/30_rpc/kefu/rpc_admin_kefu_account_delete_v1.sql
Normal file
36
docs/sql/30_rpc/kefu/rpc_admin_kefu_account_delete_v1.sql
Normal 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 '管理员删除客服账号';
|
||||
69
docs/sql/30_rpc/kefu/rpc_admin_kefu_account_list_v1.sql
Normal file
69
docs/sql/30_rpc/kefu/rpc_admin_kefu_account_list_v1.sql
Normal 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;
|
||||
$$;
|
||||
61
docs/sql/30_rpc/kefu/rpc_admin_kefu_account_save_v1.sql
Normal file
61
docs/sql/30_rpc/kefu/rpc_admin_kefu_account_save_v1.sql
Normal 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;
|
||||
$$;
|
||||
@@ -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 '管理员设置客服账号状态';
|
||||
36
docs/sql/30_rpc/kefu/rpc_admin_kefu_auto_reply_delete_v1.sql
Normal file
36
docs/sql/30_rpc/kefu/rpc_admin_kefu_auto_reply_delete_v1.sql
Normal 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 '管理员删除客服自动回复配置';
|
||||
59
docs/sql/30_rpc/kefu/rpc_admin_kefu_auto_reply_list_v1.sql
Normal file
59
docs/sql/30_rpc/kefu/rpc_admin_kefu_auto_reply_list_v1.sql
Normal 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 '管理员分页查询客服自动回复列表';
|
||||
64
docs/sql/30_rpc/kefu/rpc_admin_kefu_auto_reply_save_v1.sql
Normal file
64
docs/sql/30_rpc/kefu/rpc_admin_kefu_auto_reply_save_v1.sql
Normal 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 '管理员新增或更新自动回复配置';
|
||||
@@ -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 '管理员设置客服自动回复状态';
|
||||
73
docs/sql/30_rpc/kefu/rpc_admin_kefu_feedback_list_v1.sql
Normal file
73
docs/sql/30_rpc/kefu/rpc_admin_kefu_feedback_list_v1.sql
Normal 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 '管理员分页查询用户留言反馈列表';
|
||||
43
docs/sql/30_rpc/kefu/rpc_admin_kefu_feedback_process_v1.sql
Normal file
43
docs/sql/30_rpc/kefu/rpc_admin_kefu_feedback_process_v1.sql
Normal 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 '管理员处理并回复用户留言反馈';
|
||||
@@ -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 '管理员删除话术分类';
|
||||
@@ -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 '管理员获取话术分类列表';
|
||||
@@ -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 '管理员新增或更新话术分类';
|
||||
36
docs/sql/30_rpc/kefu/rpc_admin_kefu_word_delete_v1.sql
Normal file
36
docs/sql/30_rpc/kefu/rpc_admin_kefu_word_delete_v1.sql
Normal 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 '管理员删除快捷话术';
|
||||
52
docs/sql/30_rpc/kefu/rpc_admin_kefu_word_list_v1.sql
Normal file
52
docs/sql/30_rpc/kefu/rpc_admin_kefu_word_list_v1.sql
Normal 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 '管理员获取快捷话术列表';
|
||||
64
docs/sql/30_rpc/kefu/rpc_admin_kefu_word_save_v1.sql
Normal file
64
docs/sql/30_rpc/kefu/rpc_admin_kefu_word_save_v1.sql
Normal 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 '管理员新增或更新快捷话术';
|
||||
Reference in New Issue
Block a user