sql数据流,amdin业务逻辑接入
This commit is contained in:
71
docs/ops/2026-02-04__db-schema__fix-ak-users-constraints.md
Normal file
71
docs/ops/2026-02-04__db-schema__fix-ak-users-constraints.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 操作文档:修复 `ak_users` 表写入协调问题
|
||||
|
||||
- **日期**: 2026-02-04
|
||||
- **范围**: 数据库 Schema (`public.ak_users`)
|
||||
- **标题**: 修复 `auth.users` -> `ak_users` 自动同步的写入协调问题
|
||||
|
||||
## 摘要
|
||||
|
||||
本操作通过修改 `public.ak_users` 表的结构,解决了新用户在 Supabase Auth 注册后,无法在业务表 `ak_users` 中自动创建对应记录的问题。核心改动是放宽了 `username` 和 `email` 字段的 `NOT NULL` 约束。
|
||||
|
||||
## 动机
|
||||
|
||||
在当前实现中,`auth.users` 表上存在一个 `on_auth_user_created` 触发器,该触发器在有新用户注册时会调用 `handle_new_user()` 函数,尝试在 `ak_users` 表中插入一条对应的业务用户记录。然而,由于 `ak_users` 表的 `username` 和 `email` 字段被设置为 `NOT NULL` 且没有默认值,而 `handle_new_user()` 函数在执行时无法提供这两个值,导致 `INSERT` 操作因违反约束而静默失败。这造成了 `auth.users` 和 `public.ak_users` 之间的数据不一致,使得新注册的用户无法被业务系统识别,后续的权限、角色等功能全部失效。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- **数据库表**: `public.ak_users`
|
||||
- **数据库函数**: `public.handle_new_user()` (现在可以成功执行)
|
||||
- **前端逻辑**: `utils/sapi.uts` 中的 `ensureUserProfile` 函数(现在在用户首次登录时,即使触发器失败,它也能成功插入记录)。
|
||||
- **业务流程**: 新用户注册、首次登录的数据同步流程。
|
||||
|
||||
## 变更清单
|
||||
|
||||
- **新增文件**:
|
||||
- `docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql`: 包含了本次 DDL 变更的权威 SQL 脚本。
|
||||
- `docs/ops/2026-02-04__db-schema__fix-ak-users-constraints.md`: 本操作文档。
|
||||
|
||||
- **修改内容**: `public.ak_users` 表结构
|
||||
- `username` 字段的 `NOT NULL` 约束被移除。
|
||||
- `email` 字段的 `NOT NULL` 约束被移除。
|
||||
- `role` 字段的默认值被更新为 `'customer'`。
|
||||
|
||||
## 兼容性与风险
|
||||
|
||||
- **数据兼容性**: 此变更为向下兼容。允许 `username` 和 `email` 为空,不会影响现有记录。应用层代码(如 `ensureUserProfile`)会在用户首次登录时尝试填充这些值。
|
||||
- **风险**: 理论上,现在 `ak_users` 表中可能存在 `username` 或 `email` 为空的记录。依赖这两个字段为非空的前端页面或业务逻辑需要做好空值处理,但这符合用户首次登录后才完善资料的常见模式。
|
||||
|
||||
## 回滚方案
|
||||
|
||||
如果需要撤销此变更,可以执行以下 SQL 命令,将表的约束恢复到之前的状态:
|
||||
|
||||
```sql
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE public.ak_users
|
||||
ALTER COLUMN username SET NOT NULL,
|
||||
ALTER COLUMN email SET NOT NULL;
|
||||
|
||||
-- 注意:回滚前必须确保表中没有 username 或 email 为 NULL 的记录,否则会失败。
|
||||
|
||||
-- 将 role 的默认值恢复为 'student'(如果需要)
|
||||
ALTER TABLE public.ak_users
|
||||
ALTER COLUMN role SET DEFAULT 'student';
|
||||
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
## 验证方式
|
||||
|
||||
1. **执行变更**: 在数据库中执行 `docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql` 的内容。
|
||||
2. **注册新用户**: 在 App 中注册一个全新的用户(例如 `new_user@example.com`)。
|
||||
3. **验证 `ak_users` 表**: 在 SQL Editor 中查询,确认 `ak_users` 表中已自动创建了与新用户 `auth_id` 对应的记录。
|
||||
```sql
|
||||
SELECT auth_id, email, username, role FROM public.ak_users WHERE email = 'new_user@example.com';
|
||||
```
|
||||
4. **验证 `consistency_status`**: 再次执行之前的一致性检查 SQL,确认新用户的状态为 `CONSISTENT`。
|
||||
|
||||
## 关联文档
|
||||
|
||||
- `docs/AGENT_PROJECT_SPEC.md`
|
||||
- `docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql`
|
||||
56
docs/ops/2026-02-04__db-trigger__fix-handle-new-user-sync.md
Normal file
56
docs/ops/2026-02-04__db-trigger__fix-handle-new-user-sync.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# 操作文档:修复 `handle_new_user` 触发器函数
|
||||
|
||||
- **日期**: 2026-02-04
|
||||
- **范围**: 数据库触发器函数 (`public.handle_new_user`)
|
||||
- **标题**: 修复 `auth.users` -> `public.ak_users` 的自动用户同步逻辑
|
||||
|
||||
## 摘要
|
||||
|
||||
本操作通过更新 `public.handle_new_user()` 触发器函数的定义,解决了新用户在 Supabase Auth 注册后,无法在核心业务表 `public.ak_users` 中自动创建对应记录的根本问题。
|
||||
|
||||
## 动机
|
||||
|
||||
经过排查,发现 `on_auth_user_created` 触发器调用的 `handle_new_user()` 函数并未向 `public.ak_users` 表执行任何 `INSERT` 操作,而是错误地将数据写入了 `public.user_roles` 和 `auth.users.raw_user_meta_data`。这导致 `auth.users` 和 `public.ak_users` 之间的数据完全脱节,使得所有依赖 `ak_users.role` 的权限校验(如 RPC 鉴权)全部失败。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- **数据库函数**: `public.handle_new_user()`
|
||||
- **数据库触发器**: `on_auth_user_created` (在 `auth.users` 表上)
|
||||
- **数据库表**: `public.ak_users` (现在可以被自动写入), `public.user_roles` (保持兼容写入)
|
||||
- **业务流程**: 新用户注册的数据同步流程。
|
||||
|
||||
## 变更清单
|
||||
|
||||
- **新增文件**:
|
||||
- `docs/sql/30_rpc/auth/handle_new_user_v2.sql`: 包含了修正后的 `handle_new_user` 函数的权威 SQL 脚本。
|
||||
- `docs/ops/2026-02-04__db-trigger__fix-handle-new-user-sync.md`: 本操作文档。
|
||||
|
||||
- **修改内容**: `public.handle_new_user()` 函数的定义
|
||||
- **新增**: 向 `public.ak_users` 表 `INSERT` 新用户记录的逻辑,包含 `auth_id`, `email`, `username`, 和 `role`。
|
||||
- **新增**: 使用 `ON CONFLICT` 确保操作的幂等性。
|
||||
- **保留**: 保留了向 `public.user_roles` 和 `auth.users.raw_user_meta_data` 的写入,以兼容现有逻辑。
|
||||
- **安全**: 函数已遵循 `SECURITY DEFINER` 和 `SET search_path = public` 的安全规范。
|
||||
|
||||
## 兼容性与风险
|
||||
|
||||
- **数据兼容性**: 此变更为向前兼容。它修复了新用户的数据同步问题,不会影响现有的 `ak_users` 记录。
|
||||
- **风险**: 无明显风险。函数现在会正确地执行其预期的核心任务。
|
||||
|
||||
## 回滚方案
|
||||
|
||||
如果需要撤销此变更,可以从版本控制(Git)中找回旧的 `handle_new_user()` 函数定义,或者从数据库备份中恢复,然后重新执行 `CREATE OR REPLACE FUNCTION` 即可。
|
||||
|
||||
## 验证方式
|
||||
|
||||
1. **执行变更**: 在数据库中执行 `docs/sql/30_rpc/auth/handle_new_user_v2.sql` 的内容,以更新函数定义。
|
||||
2. **注册新用户**: 在 App 中注册一个全新的用户(例如 `final_test@example.com`)。
|
||||
3. **验证 `ak_users` 表**: 在 SQL Editor 中查询,确认 `ak_users` 表中已自动创建了与新用户 `auth_id` 对应的记录,并且 `role` 已根据邮箱规则(或默认为 `customer`)被正确设置。
|
||||
```sql
|
||||
SELECT auth_id, email, username, role FROM public.ak_users WHERE email = 'final_test@example.com';
|
||||
```
|
||||
4. **验证一致性**: 再次执行一致性检查 SQL,确认新用户的 `consistency_status` 为 `CONSISTENT`。
|
||||
|
||||
## 关联文档
|
||||
|
||||
- `docs/AGENT_PROJECT_SPEC.md`
|
||||
- `docs/sql/30_rpc/auth/handle_new_user_v2.sql`
|
||||
73
docs/ops/2026-02-05__auth__fix-login-registration-sync.md
Normal file
73
docs/ops/2026-02-05__auth__fix-login-registration-sync.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 操作文档:修复登录注册与数据同步全链路问题
|
||||
|
||||
- **日期**: 2026-02-05
|
||||
- **范围**: 用户认证、注册、数据库同步及页面守卫
|
||||
- **标题**: 修复因“写入协调”失败导致的 Admin 功能权限问题
|
||||
|
||||
## 摘要
|
||||
|
||||
本操作彻底解决了新用户注册后,因数据库写入协调失败,导致业务用户表 (`public.ak_users`) 记录缺失,进而使得所有需要 `admin`/`analytics` 角色的 RPC 调用和页面访问全部失败的问题。修复遵循 `@docs/AGENT_PROJECT_SPEC.md` 规范,涉及数据库 DDL、触发器函数、前端服务和页面守卫的全链路调整。
|
||||
|
||||
## 动机
|
||||
|
||||
- **直接问题**: Admin 后台的统计图表始终显示为 0,即使在数据库中有数据。
|
||||
- **根本原因**: 前端 RPC 调用因权限不足被拒绝。
|
||||
- **深层原因**: 权限检查依赖的 `get_current_user_role()` 函数因在 `ak_users` 表中找不到当前用户的 `auth_id` 而返回 `NULL`。
|
||||
- **最终根源**: 新用户在 `auth.users` 表注册后,自动同步到 `ak_users` 表的数据库触发器 `handle_new_user()` 函数逻辑错误(未向 `ak_users` 写入),导致数据不一致。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- **数据库**: `public.ak_users` 表结构, `public.handle_new_user()` 函数, `auth.users` 表上的 `on_auth_user_created` 触发器。
|
||||
- **前端服务**: `utils/sapi.uts` (`ensureUserProfile`), `utils/store.uts` (`getCurrentUser`), `services/analytics/authGuard.uts`.
|
||||
- **前端页面**: `pages/user/login.uvue`, `pages/user/register.uvue`, `layouts/admin/AdminLayout.uvue`.
|
||||
|
||||
## 变更清单
|
||||
|
||||
### 数据库侧 (权威 SQL)
|
||||
|
||||
1. **修复 `ak_users` 表结构约束**:
|
||||
- **文件**: `docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql`
|
||||
- **内容**: 移除了 `username` 和 `email` 的 `NOT NULL` 约束,以确保自动同步不会因缺少值而失败。
|
||||
|
||||
2. **修复 `handle_new_user` 触发器函数**:
|
||||
- **文件**: `docs/sql/30_rpc/auth/handle_new_user_v3.sql`
|
||||
- **内容**: 将函数逻辑修正为正确地向 `public.ak_users` 表 `INSERT` 新用户记录,并兼容写入 `public.user_roles`。
|
||||
|
||||
3. **创建 `get_current_user_role` 函数**:
|
||||
- **文件**: `docs/sql/30_rpc/auth/get_current_user_role_v1.sql`
|
||||
- **内容**: 创建了用于 RPC 鉴权的权威函数,通过 `auth_id` 查询 `ak_users` 表获取角色。
|
||||
|
||||
### 前端侧
|
||||
|
||||
1. **修复 `ensureUserProfile` 写入逻辑**:
|
||||
- **文件**: `utils/sapi.uts`
|
||||
- **内容**: 修正了 `INSERT` 逻辑,确保写入的是 `auth_id` 而不是业务主键 `id`,并设置了默认角色。
|
||||
|
||||
2. **修复登录逻辑**:
|
||||
- **文件**: `pages/user/login.uvue`
|
||||
- **内容**: 移除了生产环境下的 `admin/admin` 本地绕过登录,强制所有登录走 Supabase Auth 以获取有效 Token。并在登录成功后调用 `ensureUserProfile`。
|
||||
|
||||
3. **实现统一页面守卫**:
|
||||
- **文件**: `layouts/admin/AdminLayout.uvue`
|
||||
- **内容**: 在布局的 `onMounted` 钩子中加入了统一的权限守卫,覆盖所有使用该布局的 Admin 页面。
|
||||
|
||||
## 最终解决方案与验证流程
|
||||
|
||||
1. **数据库部署**: 按顺序执行以下权威 SQL 文件:
|
||||
- `docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql`
|
||||
- `docs/sql/30_rpc/auth/get_current_user_role_v1.sql`
|
||||
- `docs/sql/30_rpc/auth/handle_new_user_v3.sql`
|
||||
- `ALTER TABLE auth.users ENABLE TRIGGER on_auth_user_created;` (确保触发器是启用的)
|
||||
|
||||
2. **新用户注册**: 在 App 中注册一个全新的用户。
|
||||
|
||||
3. **验证数据同步**: 在 SQL Editor 中执行“三表一致性”查询,确认新用户的 `status` 为 `OK` 或至少 `ak_users` 不再 `MISSING`。
|
||||
|
||||
4. **提升权限**: 手动将新用户在 `public.ak_users` 表中的 `role` 字段更新为 `'admin'`。
|
||||
|
||||
5. **登录并访问**: 使用新注册并已提权的账号登录 App,访问 Admin 后台的统计页面,确认数据能正常加载。
|
||||
|
||||
## 关联文档
|
||||
|
||||
- `docs/AGENT_PROJECT_SPEC.md`
|
||||
- `docs/sql/11_roles_and_permissions_strategy.md`
|
||||
@@ -0,0 +1,389 @@
|
||||
# 2026-02-05 / mall:配送-商家-消费者 数据流统一分析(SQL + 项目规范口径)
|
||||
|
||||
## 摘要
|
||||
|
||||
本文基于本仓库 **权威 SQL 目录** `docs/sql/`(并参考 `mall_sql/` 中的 `complete_mall_database.sql` 实现细节),结合项目工程与安全规范 `docs/project_spec/AGENT_PROJECT_SPEC.md`、`docs/sql/11_roles_and_permissions_strategy.md`,对 **消费者 / 商家 / 配送员** 三方在电商场景中的数据流运转进行统一分析。
|
||||
|
||||
输出内容包括:
|
||||
|
||||
- 三方域模型与关键表关系(文字版 ER)
|
||||
- 端到端事件链路:下单、支付、履约配送、完成、评价
|
||||
- 订单与配送状态机(字段口径与关键歧义点)
|
||||
- 读模型(各端典型查询视角)
|
||||
- 权限边界(RLS / RPC / 前端守卫)在本项目中的统一口径
|
||||
|
||||
> 本文为分析文档,不包含代码/SQL 变更。
|
||||
|
||||
---
|
||||
|
||||
## 动机
|
||||
|
||||
- 统一“谁能看/改什么数据”的口径:角色权威字段、RLS 与 RPC 的安全闭环要求必须一致。
|
||||
- 统一“数据如何落表与流转”的口径:避免前后端/不同模块对订单、配送、商家模型理解不一致。
|
||||
- 为后续落地(配置页、配送履约闭环、统计看板等)提供可审计的基础说明。
|
||||
|
||||
---
|
||||
|
||||
## 影响范围
|
||||
|
||||
- **数据库层**:`docs/sql/` 中的表结构、触发器、RLS、RPC 策略口径(本文不修改)。
|
||||
- **前端工程层**:页面必须通过 `services/` 访问数据;admin/analytics 需通过 RPC 获取全局数据(本文不修改)。
|
||||
- **业务模块**:
|
||||
- 消费者端(consumer)订单链路
|
||||
- 商家端(merchant)商品与订单履约
|
||||
- 配送端(delivery)接单/取货/送达
|
||||
- 管理端(admin)配置与全局视角
|
||||
|
||||
---
|
||||
|
||||
## 变更清单
|
||||
|
||||
- **新增文件**:
|
||||
- `docs/ops/2026-02-05__mall__delivery-merchant-customer-dataflow-analysis.md`
|
||||
- **修改文件**:无
|
||||
- **删除文件**:无
|
||||
|
||||
---
|
||||
|
||||
## 权威输入(引用口径)
|
||||
|
||||
- 数据库与流程说明:
|
||||
- `docs/sql/02_relationships_er.md`
|
||||
- `docs/sql/03_enums_status_dict.md`
|
||||
- `docs/sql/07_business_workflows.md`
|
||||
- `docs/sql/11_roles_and_permissions_strategy.md`
|
||||
- 工程与安全规范:
|
||||
- `docs/project_spec/AGENT_PROJECT_SPEC.md`
|
||||
- 参考实现(非权威口径,但含完整 DDL/触发器/RLS 示例):
|
||||
- `mall_sql/schemas/complete_mall_database.sql`
|
||||
- 管理端设置页现状(占位):
|
||||
- `pages/mall/admin/system-settings.uvue`
|
||||
|
||||
---
|
||||
|
||||
## 1. 统一域模型:三方是谁(同一身份体系下的三类角色)
|
||||
|
||||
- **统一用户主表**:`public.ak_users`
|
||||
- **角色唯一权威字段**:`ak_users.role`
|
||||
- **商城用户扩展档案**:`public.ml_user_profiles`
|
||||
- 关系:`ak_users` 1:1 `ml_user_profiles`(`ml_user_profiles.user_id UNIQUE`)
|
||||
|
||||
三方角色定义(业务口径):
|
||||
|
||||
- 消费者:`ak_users.role = 'customer'`
|
||||
- 商家:`ak_users.role = 'merchant'`
|
||||
- 配送员:`ak_users.role = 'delivery'`
|
||||
|
||||
结论:三方并非三套用户体系,而是“同一 `ak_users` + 不同域表扩展”。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心表关系(文字版 ER)
|
||||
|
||||
### 2.1 消费者(Customer)
|
||||
|
||||
- `ak_users (customer)` 1:N `ml_user_addresses`
|
||||
- `ak_users (customer)` 1:N `ml_shopping_cart`
|
||||
- `ak_users (customer)` 1:N `ml_orders`(`ml_orders.user_id`)
|
||||
|
||||
### 2.2 商家(Merchant)
|
||||
|
||||
- `ak_users (merchant)` 1:1 `ml_shops`(`ml_shops.merchant_id UNIQUE`)
|
||||
- 当前模型含义:一商家一店铺
|
||||
- `ak_users (merchant)` 1:N `ml_products`(`ml_products.merchant_id`)
|
||||
- `ak_users (merchant)` 1:N `ml_orders`(`ml_orders.merchant_id`)
|
||||
|
||||
### 2.3 商品(Product)
|
||||
|
||||
- `ml_products` 1:N `ml_product_skus`
|
||||
- `ml_products` 1:N `ml_product_specs`
|
||||
- SKU 变更触发汇总:`ml_product_skus` 变更 -> 触发器刷新 `ml_products.total_stock/available_stock`
|
||||
|
||||
### 2.4 订单(Order)
|
||||
|
||||
- `ml_orders` 1:N `ml_order_items`
|
||||
- `ml_orders` 1:1 `ml_delivery_tasks`(`ml_delivery_tasks.order_id UNIQUE`)
|
||||
- 当前模型含义:一订单最多一个配送任务
|
||||
|
||||
### 2.5 配送(Delivery)
|
||||
|
||||
- `ak_users (delivery)` 1:1 `ml_delivery_drivers`(`ml_delivery_drivers.user_id UNIQUE`)
|
||||
- `ml_delivery_drivers` 1:N `ml_delivery_tasks`(通过 `ml_delivery_tasks.driver_id` 关联)
|
||||
|
||||
---
|
||||
|
||||
## 3. 三方端到端数据流(事件 → 落表 → 状态变化)
|
||||
|
||||
### 3.1 消费者链路(浏览→加购→下单→支付→收货→评价)
|
||||
|
||||
#### 事件 A:维护地址
|
||||
|
||||
- 写表:`ml_user_addresses`
|
||||
- 约束:同一用户最多一个默认地址
|
||||
- 由触发器 `ensure_single_default_address()` 保证
|
||||
|
||||
#### 事件 B:加购/改购
|
||||
|
||||
- 写表:`ml_shopping_cart`
|
||||
- 约束:`UNIQUE(user_id, product_id, sku_id)`(便于 upsert 累加)
|
||||
|
||||
#### 事件 C:下单(创建订单 + 明细快照)
|
||||
|
||||
- 写表:
|
||||
- `ml_orders`(订单主表)
|
||||
- `ml_order_items`(订单明细快照)
|
||||
- 关键设计点:
|
||||
- `ml_orders.shipping_address` 为 JSONB 地址快照,避免订单与地址簿耦合
|
||||
- `ml_order_items` 快照化写入商品名/规格/图/价格,避免商品变更影响历史
|
||||
|
||||
> 一致性提示(现状):当前权威文档指出未体现“扣库存/冻结库存”落地,库存一致性需由应用层事务或后续 DB/RPC 补齐。
|
||||
|
||||
#### 事件 D:支付成功
|
||||
|
||||
- 更新:`ml_orders`
|
||||
- `order_status: 1 -> 2`
|
||||
- `payment_status: 1 -> 2`
|
||||
- `paid_amount = total_amount`
|
||||
- 触发器副作用:`handle_order_status_change()` 在 `1->2` 写 `paid_at`
|
||||
|
||||
#### 事件 E:收货完成/订单完成
|
||||
|
||||
- 更新:`ml_orders`
|
||||
- `order_status: 3 -> 4`
|
||||
- `shipping_status -> 4`
|
||||
- 触发器副作用:写 `delivered_at/completed_at`,并累计更新 `ml_products.sale_count`
|
||||
|
||||
#### 事件 F:评价
|
||||
|
||||
- 写表:`ml_product_reviews`
|
||||
- 强绑定来源:`order_id + order_item_id`
|
||||
|
||||
---
|
||||
|
||||
### 3.1.1 退款/取消在当前模型中的最小表达(现状与缺口)
|
||||
|
||||
当前数据库没有独立的退款申请表或售后流程表,只能通过 `ml_orders` 的状态组合表达:
|
||||
|
||||
- **退款分支**:
|
||||
- `payment_status: 3(部分退款)` / `4(全额退款)`
|
||||
- `order_status` 通常进入 `6(退款中)` -> `7(已退款)`
|
||||
- **取消分支**:
|
||||
- `order_status: 5`(歧义点,见第 4.2 节)
|
||||
- `cancel_reason`(文本字段)可用于记录取消原因
|
||||
|
||||
> **缺口提示**:
|
||||
> - 缺少退款申请流水、审批流、支付对账表,复杂售后需后续扩表或接支付系统。
|
||||
> - 缺少 `cancelled_at`、`refunded_at` 等审计时间字段。
|
||||
> - 配送已取货/在途/已送达时的退款/取消口径未在当前模型体现(需业务层定义)。
|
||||
|
||||
---
|
||||
|
||||
### 3.2 商家链路(供给→接单→履约)
|
||||
|
||||
#### 事件 A:创建/编辑商品
|
||||
|
||||
- 写表:`ml_products`、`ml_product_skus`、`ml_product_specs`
|
||||
- 库存汇总:SKU 触发器刷新 SPU 汇总库存字段
|
||||
|
||||
#### 事件 B:发货/履约推进
|
||||
|
||||
- 更新:`ml_orders`
|
||||
- `order_status: 2 -> 3`
|
||||
- `shipping_status: 1 -> 2`
|
||||
- 触发器副作用:`2->3` 时写 `shipped_at`
|
||||
|
||||
---
|
||||
|
||||
### 3.2.1 单商家订单模型的限制(需求提醒)
|
||||
|
||||
当前订单主表 `ml_orders` 直接持有 `merchant_id`,且未见“主单/子单”结构,因此该模型天然对应:
|
||||
|
||||
- **一笔订单仅归属一个商家/店铺**(单商家订单)
|
||||
|
||||
> 若产品需求为“一单多商家(多店铺)”,通常需要:
|
||||
> - 引入主/子订单拆分(例如主订单聚合支付与收货信息,子订单按商家拆分履约与结算),或
|
||||
> - 在下单阶段强制拆单(多个 `ml_orders`)并建立父子关联字段。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 配送链路(派单/接单→取货→配送→送达)
|
||||
|
||||
#### 事件 A:创建配送任务
|
||||
|
||||
- 写表:`ml_delivery_tasks`
|
||||
- 关键字段:
|
||||
- `order_id UNIQUE`
|
||||
- `driver_id` 可空(先生成后指派)
|
||||
- `pickup_address`(取货点)
|
||||
- `delivery_address`(配送点,建议源自订单地址快照)
|
||||
|
||||
#### 事件 B:接单/指派
|
||||
|
||||
- 更新:`ml_delivery_tasks`
|
||||
- `status: 1 -> 2`
|
||||
- `assigned_at` 写入
|
||||
- 绑定 `driver_id`
|
||||
|
||||
#### 事件 C:取货→配送→送达(或失败)
|
||||
|
||||
- 更新:`ml_delivery_tasks`
|
||||
- `2 -> 3`(写 `picked_at`)
|
||||
- `3 -> 4`
|
||||
- `4 -> 5`(写 `delivered_at`)
|
||||
- 失败:`status = 6` + `failure_reason`
|
||||
|
||||
> 一致性提示(现状):未看到 DB 侧“配送送达 → 自动联动订单状态”的权威触发器/RPC 设计,需在 service/RPC 层承担联动更新职责。
|
||||
|
||||
---
|
||||
|
||||
### 3.3.1 配送状态 → 订单状态的建议映射(统一口径)
|
||||
|
||||
为避免三方对“配送进展如何反映到订单状态”理解不一致,建议采用以下最小映射(可在 service/RPC 事务中实现):
|
||||
|
||||
| 配送任务 `status` | 建议同步的 `ml_orders.shipping_status` | 建议同步的 `ml_orders.order_status` | 说明 |
|
||||
| ----------------- | -------------------------------------- | ----------------------------------------------------- | ------------------------- |
|
||||
| 1 待接单 | 1 未发货 | 2 待发货(已支付) | 任务生成但未接单 |
|
||||
| 2 已接单 | 2 已发货 | 2 待发货 | 商家/平台已指派,但未取货 |
|
||||
| 3 取货中 | 3 运输中 | 3 待收货 | 骑手已取货,在途 |
|
||||
| 4 配送中 | 3 运输中 | 3 待收货 | 仍在途 |
|
||||
| 5 已送达 | 4 已送达 | 3 待收货(或直接 4 已完成,取决于产品是否需用户确认) | 骑手确认送达 |
|
||||
| 6 配送失败 | 1 未发货(或自定义状态) | 2 待发货(或进入异常分支) | 需人工介入 |
|
||||
|
||||
> **关键决策点**:`status=5` 时,`order_status` 是“待收货”还是直接“已完成”,取决于产品是否需要用户“确认收货”这一步。若不需要,可直接 `4`。
|
||||
|
||||
---
|
||||
|
||||
### 3.3.2 派单依据(任务池可见性)的字段来源
|
||||
|
||||
`ml_delivery_drivers` 与 `ml_delivery_tasks` 中的以下字段是“任务池筛选/派单算法”的数据基础:
|
||||
|
||||
- **服务区域**:`ml_delivery_drivers.service_areas`(JSONB),用于按区域过滤可接单骑手
|
||||
- **实时位置**:`current_lat/current_lng`,用于距离排序/就近派单
|
||||
- **工作状态**:`work_status`(在线/忙碌/离线),用于过滤可用骑手
|
||||
- **任务状态**:`ml_delivery_tasks.status=1`,用于构建“待接单任务池”
|
||||
|
||||
> 权限提示:若任务池需对骑手可见,需配合 RLS 或 RPC 实现“仅可看同区域且状态=1 的任务”。
|
||||
|
||||
---
|
||||
|
||||
## 4. 状态机统一口径(必须统一的单一真相)
|
||||
|
||||
### 4.1 订单并行状态字段
|
||||
|
||||
`ml_orders` 存在三条并行状态线:
|
||||
|
||||
- `order_status`:订单流程
|
||||
- `payment_status`:支付/退款
|
||||
- `shipping_status`:发货/物流
|
||||
|
||||
推荐统一驱动关系:
|
||||
|
||||
- 支付成功驱动 `order_status` 进入“待发货”阶段
|
||||
- 发货/配送接管驱动 `order_status` 进入“待收货”阶段
|
||||
- 签收/确认收货驱动 `order_status` 完成
|
||||
- 退款以 `payment_status` 为主线,同时影响 `order_status` 分支
|
||||
|
||||
### 4.2 `order_status = 5` 的歧义
|
||||
|
||||
文档指出 `5` 在不同脚本存在“已取消/已取货”表述差异。
|
||||
|
||||
- 无自提流程:建议统一为“已取消”
|
||||
- 有自提流程:建议拆出独立状态值(避免取消与自提完成混用)
|
||||
|
||||
---
|
||||
|
||||
## 5. 读模型(各端典型查询视角)
|
||||
|
||||
> 本节描述“应该怎么查”,不涉及具体实现。
|
||||
|
||||
### 5.1 消费者端
|
||||
|
||||
- 商品列表/详情:优先基于商品详情视图(若存在)或 `ml_products`(上架商品)
|
||||
- 我的订单列表:`ml_orders.user_id = 当前用户` + 时间倒序
|
||||
- 订单详情:`ml_orders` + `ml_order_items`
|
||||
- 配送进度:若需展示,则读取 `ml_delivery_tasks`(需权限策略或 RPC 支持)
|
||||
|
||||
### 5.2 商家端
|
||||
|
||||
- 我的商品:`ml_products.merchant_id = 当前商家`
|
||||
- 注意:若 `ml_products` 的 RLS select 仅允许 `status=1`,商家后台查看草稿/下架需额外策略或 RPC
|
||||
- 店铺订单:`ml_orders.merchant_id = 当前商家` + `ml_order_items`
|
||||
- 配送任务看板:按订单关联 `ml_delivery_tasks`
|
||||
|
||||
> **现状风险(RLS)**:`ml_products_select_policy` 仅允许 `status=1`,会导致商家后台直查表时看不到自己的草稿/下架商品。建议扩展 RLS 或为商家后台提供专用 RPC。
|
||||
|
||||
### 5.3 配送端
|
||||
|
||||
- 待接单任务池:`ml_delivery_tasks.status = 1`(通常需按服务区域过滤)
|
||||
- 我的任务:`ml_delivery_tasks.driver_id = 当前配送员`
|
||||
- 任务详情:最小化暴露订单/个人敏感信息(原则层面)
|
||||
|
||||
---
|
||||
|
||||
## 6. 权限边界(RLS / RPC / 前端守卫的闭环)
|
||||
|
||||
### 6.1 前端工程约束
|
||||
|
||||
- 数据访问唯一入口:必须通过 `services/`
|
||||
- 客户端路由守卫用于快速失败,但最终权限以数据库为准
|
||||
|
||||
### 6.2 admin / analytics 的全局数据访问
|
||||
|
||||
按项目规范与策略文档:
|
||||
|
||||
- 不建议对 admin/analytics 直接开放业务表全局 `SELECT`
|
||||
- 应通过 RPC(`SECURITY DEFINER` + 固定 `search_path` + 入口 `get_current_user_role()` 鉴权)返回最小必要字段/聚合数据
|
||||
|
||||
---
|
||||
|
||||
## 7. 金额字段的归因与缺口(运费/优惠/抽佣)
|
||||
|
||||
| 金额字段 | 数据来源 | 归因说明 | 现状缺口 |
|
||||
| --------------------------- | ----------------------------------------------------- | ------------------------------------------------- | --------------------------------------- |
|
||||
| `ml_orders.shipping_fee` | `ml_system_configs.shipping_fee`(或按距离/重量计算) | 配送费,可配置默认值或按规则计算 | 缺少运费规则表(如按区域/重量/距离) |
|
||||
| `ml_orders.discount_amount` | 优惠券/活动/满减 | 优惠金额,使用券时关联 `ml_user_coupons.order_id` | 缺少活动/满减规则表,复杂优惠需扩表 |
|
||||
| `ml_orders.total_amount` | 商品金额 + 运费 - 优惠 | 订单最终应付 | — |
|
||||
| `platform_commission` | `ml_system_configs.platform_commission`(比例) | 平台抽佣,通常在结算时计算 | 缺少结算单/分账流水表,抽佣未落结算流水 |
|
||||
|
||||
> **结论**:金额归因在“配置 → 落表”层面基本可表达,但缺少结算/对账/复杂优惠的支撑表。
|
||||
|
||||
---
|
||||
|
||||
## 8. 系统配置(system-settings)与“配置驱动流程”的现状说明
|
||||
|
||||
- DB 已存在:`ml_system_configs`,并在参考脚本中初始化了配置项(如 `shipping_fee`、`platform_commission`、`order_auto_confirm_days`)。
|
||||
- 管理端页面现状:`pages/mall/admin/system-settings.uvue` 目前为占位页,仅展示 query 参数,未接入配置读写。
|
||||
|
||||
结论:配置承载表存在,但尚未形成“配置 → 业务流程”的实际驱动闭环。
|
||||
|
||||
---
|
||||
|
||||
## 兼容性与风险
|
||||
|
||||
- **状态机风险**:三条并行状态字段若缺少统一约束,易出现互相矛盾的状态组合。
|
||||
- **一致性风险**:库存扣减/冻结、配送送达与订单联动等关键一致性未在当前权威 SQL 中体现,需在应用层或后续 RPC/触发器补齐。
|
||||
- **权限风险**:配送域 RLS/RPC 若未补齐,会导致配送端无法形成可运转的“接单→更新状态”闭环。
|
||||
|
||||
---
|
||||
|
||||
## 回滚方案
|
||||
|
||||
本文仅新增文档,无业务变更;删除该 markdown 文件即可回滚。
|
||||
|
||||
---
|
||||
|
||||
## 验证方式
|
||||
|
||||
- 打开本文档,核对引用路径均存在:
|
||||
- `docs/sql/*`、`docs/project_spec/*`
|
||||
- 与现有页面目录结构、角色字段口径(`ak_users.role`)一致。
|
||||
|
||||
---
|
||||
|
||||
## 关联文档
|
||||
|
||||
- `docs/project_spec/AGENT_PROJECT_SPEC.md`
|
||||
- `docs/sql/02_relationships_er.md`
|
||||
- `docs/sql/03_enums_status_dict.md`
|
||||
- `docs/sql/07_business_workflows.md`
|
||||
- `docs/sql/11_roles_and_permissions_strategy.md`
|
||||
0
docs/sql/00_meta/.keep
Normal file
0
docs/sql/00_meta/.keep
Normal file
9
docs/sql/00_meta/README.md
Normal file
9
docs/sql/00_meta/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# docs/sql/00_meta
|
||||
|
||||
本目录存放:
|
||||
- 规范/策略/说明类文档
|
||||
- 角色与权限策略
|
||||
- RPC 安全模型
|
||||
- 其他元信息
|
||||
|
||||
**注意**:本目录不包含 DDL/RLS/RPC/GRANT 等可执行对象,仅用于策略与说明。
|
||||
0
docs/sql/10_schema/.keep
Normal file
0
docs/sql/10_schema/.keep
Normal file
8
docs/sql/10_schema/README.md
Normal file
8
docs/sql/10_schema/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# docs/sql/10_schema
|
||||
|
||||
本目录存放:
|
||||
- 表/类型/索引等 DDL
|
||||
- 按域分组(如 `analytics/`、`user/`、`order/` 等)
|
||||
- 文件命名:`<object>_v<version>.sql`
|
||||
|
||||
**禁止**:在此目录混入 RLS、RPC、GRANT 等对象。
|
||||
19
docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql
Normal file
19
docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- =====================================================================================
|
||||
-- Schema: public.ak_users
|
||||
-- Version: v1
|
||||
-- Purpose: 修复 auth.users -> ak_users 自动同步的写入协调问题
|
||||
-- Change: 放宽 username 和 email 的 NOT NULL 约束,以允许数据库触发器成功插入新用户记录。
|
||||
-- 同时,将 role 的默认值更新为 'customer' 以符合业务逻辑。
|
||||
-- =====================================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1 & 2: 允许 username/email 为空,并更新 role 默认值
|
||||
-- 这样数据库的自动用户同步触发器就不会因为缺少 NOT NULL 的值而失败。
|
||||
-- 前端代码 (ensureUserProfile) 会在用户首次登录时尝试填充这些值。
|
||||
ALTER TABLE public.ak_users
|
||||
ALTER COLUMN username DROP NOT NULL,
|
||||
ALTER COLUMN email DROP NOT NULL,
|
||||
ALTER COLUMN role SET DEFAULT 'customer';
|
||||
|
||||
COMMIT;
|
||||
0
docs/sql/20_rls/.keep
Normal file
0
docs/sql/20_rls/.keep
Normal file
8
docs/sql/20_rls/README.md
Normal file
8
docs/sql/20_rls/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# docs/sql/20_rls
|
||||
|
||||
本目录存放:
|
||||
- RLS 启用与策略(`ENABLE ROW LEVEL SECURITY`、`CREATE/ALTER/DROP POLICY`)
|
||||
- 按域分组(如 `analytics/`、`user/` 等)
|
||||
- 文件命名:`<table>_rls_v<version>.sql`
|
||||
|
||||
**禁止**:在此目录混入 DDL、RPC、GRANT 等对象。
|
||||
0
docs/sql/30_rpc/.keep
Normal file
0
docs/sql/30_rpc/.keep
Normal file
0
docs/sql/30_rpc/README.md
Normal file
0
docs/sql/30_rpc/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
-- =====================================================================================
|
||||
-- RPC: rpc_analytics_user_gender_distribution
|
||||
-- Version: v1
|
||||
-- Purpose: 统计指定周期内新增用户的性别分布(用于 Admin/Analytics 图表)
|
||||
-- Security: SECURITY DEFINER + 固定 search_path + 入口角色鉴权
|
||||
-- Depends: public.ak_users, public.get_current_user_role()
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.rpc_analytics_user_gender_distribution(
|
||||
p_start_date DATE,
|
||||
p_end_date DATE
|
||||
)
|
||||
RETURNS TABLE (
|
||||
name TEXT,
|
||||
value BIGINT
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
BEGIN
|
||||
IF public.get_current_user_role() NOT IN ('admin', 'analytics') THEN
|
||||
RAISE EXCEPTION 'Permission denied: required role admin or analytics';
|
||||
END IF;
|
||||
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
CASE
|
||||
WHEN gender IS NULL OR TRIM(gender::text) = '' THEN '未知'
|
||||
WHEN LOWER(TRIM(gender::text)) = 'male' THEN '男'
|
||||
WHEN LOWER(TRIM(gender::text)) = 'female' THEN '女'
|
||||
WHEN LOWER(TRIM(gender::text)) = 'other' THEN '未知'
|
||||
ELSE '未知'
|
||||
END AS name,
|
||||
COUNT(*)::BIGINT AS value
|
||||
FROM public.ak_users
|
||||
WHERE created_at::DATE BETWEEN p_start_date AND p_end_date
|
||||
GROUP BY 1
|
||||
ORDER BY value DESC;
|
||||
END;
|
||||
$$;
|
||||
19
docs/sql/30_rpc/auth/get_current_user_role_v1.sql
Normal file
19
docs/sql/30_rpc/auth/get_current_user_role_v1.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- =====================================================================================
|
||||
-- RPC: get_current_user_role
|
||||
-- Version: v1
|
||||
-- Purpose: 获取当前登录用户的角色(用于 RPC 入口鉴权)
|
||||
-- Security: SECURITY DEFINER + 固定 search_path
|
||||
-- Depends: public.ak_users (auth_id, role)
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.get_current_user_role()
|
||||
RETURNS TEXT
|
||||
LANGUAGE sql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
SELECT role
|
||||
FROM public.ak_users
|
||||
WHERE auth_id = auth.uid()
|
||||
LIMIT 1;
|
||||
$$;
|
||||
76
docs/sql/30_rpc/auth/handle_new_user_v2.sql
Normal file
76
docs/sql/30_rpc/auth/handle_new_user_v2.sql
Normal file
@@ -0,0 +1,76 @@
|
||||
-- =====================================================================================
|
||||
-- Trigger Function: handle_new_user
|
||||
-- Version: v2
|
||||
-- Purpose: auth.users 新用户创建后,同步写入 public.ak_users(权威用户表)并保持 user_roles 兼容写入
|
||||
-- Security: SECURITY DEFINER + 固定 search_path
|
||||
-- Depends:
|
||||
-- - public.ak_users(auth_id,email,username,role)
|
||||
-- - public.user_roles(user_id,role,created_by) (如存在)
|
||||
-- Notes:
|
||||
-- - 角色权威口径为 public.ak_users.role
|
||||
-- - user_roles 为历史/兼容表:存在则写入,不存在则跳过
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
user_role TEXT := 'customer';
|
||||
user_email TEXT := NEW.email;
|
||||
user_name TEXT;
|
||||
has_user_roles BOOLEAN := FALSE;
|
||||
BEGIN
|
||||
-- 1) 基于邮箱规则分配默认角色(可按需调整)
|
||||
IF user_email ILIKE '%@teacher.%' OR user_email ILIKE '%@edu.%' THEN
|
||||
user_role := 'teacher';
|
||||
ELSIF user_email ILIKE '%@admin.%' THEN
|
||||
user_role := 'admin';
|
||||
END IF;
|
||||
|
||||
-- 2) 默认 username:取邮箱 @ 前缀
|
||||
IF user_email IS NOT NULL AND POSITION('@' IN user_email) > 1 THEN
|
||||
user_name := SPLIT_PART(user_email, '@', 1);
|
||||
ELSE
|
||||
user_name := 'user';
|
||||
END IF;
|
||||
|
||||
-- 3) 写入 ak_users(权威)
|
||||
-- 使用 ON CONFLICT 确保幂等:同一 auth_id 只会有一条记录
|
||||
INSERT INTO public.ak_users (auth_id, email, username, role)
|
||||
VALUES (NEW.id, user_email, user_name, user_role)
|
||||
ON CONFLICT (auth_id)
|
||||
DO UPDATE SET
|
||||
email = COALESCE(EXCLUDED.email, public.ak_users.email),
|
||||
username = COALESCE(EXCLUDED.username, public.ak_users.username),
|
||||
role = COALESCE(public.ak_users.role, EXCLUDED.role),
|
||||
updated_at = now();
|
||||
|
||||
-- 4) 兼容写入 user_roles(如果表存在)
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema='public'
|
||||
AND table_name='user_roles'
|
||||
) INTO has_user_roles;
|
||||
|
||||
IF has_user_roles THEN
|
||||
BEGIN
|
||||
INSERT INTO public.user_roles (user_id, role, created_by)
|
||||
VALUES (NEW.id, user_role, NEW.id);
|
||||
EXCEPTION WHEN unique_violation THEN
|
||||
-- 忽略重复
|
||||
NULL;
|
||||
END;
|
||||
END IF;
|
||||
|
||||
-- 5) 更新 auth.users 元数据(可选保留)
|
||||
UPDATE auth.users
|
||||
SET raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || jsonb_build_object('user_role', user_role)
|
||||
WHERE id = NEW.id;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
81
docs/sql/30_rpc/auth/handle_new_user_v3.sql
Normal file
81
docs/sql/30_rpc/auth/handle_new_user_v3.sql
Normal file
@@ -0,0 +1,81 @@
|
||||
-- =====================================================================================
|
||||
-- Trigger Function: handle_new_user
|
||||
-- Version: v3
|
||||
-- Purpose: auth.users 新用户创建后,同步写入 public.ak_users(权威)和 public.user_roles(兼容)。
|
||||
-- 此版本修复了向 user_roles 写入时可能因 role 为 NULL 导致的 NOT NULL 约束失败问题。
|
||||
-- Security: SECURITY DEFINER + 固定 search_path
|
||||
-- Depends:
|
||||
-- - public.ak_users(auth_id,email,username,role)
|
||||
-- - public.user_roles(user_id,role)
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
user_role TEXT;
|
||||
user_email TEXT := NEW.email;
|
||||
user_name TEXT;
|
||||
has_user_roles BOOLEAN := FALSE;
|
||||
BEGIN
|
||||
-- 1) 基于邮箱规则分配默认角色(可按需调整)
|
||||
-- 确保 user_role 总有一个非 NULL 的值
|
||||
user_role := CASE
|
||||
WHEN user_email ILIKE '%@admin.%' THEN 'admin'
|
||||
WHEN user_email ILIKE '%@teacher.%' OR user_email ILIKE '%@edu.%' THEN 'teacher'
|
||||
ELSE 'consumer' -- 默认角色
|
||||
END;
|
||||
|
||||
-- 2) 默认 username:取邮箱 @ 前缀
|
||||
IF user_email IS NOT NULL AND POSITION('@' IN user_email) > 1 THEN
|
||||
user_name := SPLIT_PART(user_email, '@', 1);
|
||||
ELSE
|
||||
user_name := 'user_' || SUBSTRING(NEW.id::text, 1, 8); -- 使用 user_ + uid前8位作为备用名
|
||||
END IF;
|
||||
|
||||
-- 3) 写入 ak_users(权威)
|
||||
-- 使用 ON CONFLICT 确保幂等:同一 auth_id 只会有一条记录
|
||||
INSERT INTO public.ak_users (auth_id, email, username, role)
|
||||
VALUES (NEW.id, user_email, user_name, user_role)
|
||||
ON CONFLICT (auth_id)
|
||||
DO UPDATE SET
|
||||
email = COALESCE(EXCLUDED.email, public.ak_users.email),
|
||||
username = COALESCE(EXCLUDED.username, public.ak_users.username),
|
||||
-- 只有当现有 role 为空时才更新,避免覆盖手动设置的 admin 角色
|
||||
role = COALESCE(public.ak_users.role, EXCLUDED.role),
|
||||
updated_at = now();
|
||||
|
||||
-- 4) 兼容写入 user_roles(如果表存在)
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema='public'
|
||||
AND table_name='user_roles'
|
||||
) INTO has_user_roles;
|
||||
|
||||
IF has_user_roles THEN
|
||||
BEGIN
|
||||
-- 确保插入的 role 不为 NULL,即使上面的逻辑有误
|
||||
INSERT INTO public.user_roles (user_id, role, created_by)
|
||||
VALUES (NEW.id, COALESCE(user_role, 'customer'), NEW.id);
|
||||
EXCEPTION
|
||||
WHEN unique_violation THEN
|
||||
-- 忽略重复插入的错误
|
||||
NULL;
|
||||
WHEN not_null_violation THEN
|
||||
-- 记录非空约束错误,但不中断整个触发器
|
||||
RAISE NOTICE '[handle_new_user] WARNING: Failed to INSERT into user_roles due to NOT NULL violation. user_id: %, role: %', NEW.id, user_role;
|
||||
END;
|
||||
END IF;
|
||||
|
||||
-- 5) 更新 auth.users 元数据(可选保留)
|
||||
UPDATE auth.users
|
||||
SET raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || jsonb_build_object('user_role', user_role)
|
||||
WHERE id = NEW.id;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
0
docs/sql/40_grants/.keep
Normal file
0
docs/sql/40_grants/.keep
Normal file
0
docs/sql/40_grants/README.md
Normal file
0
docs/sql/40_grants/README.md
Normal file
0
docs/sql/90_archive/.keep
Normal file
0
docs/sql/90_archive/.keep
Normal file
7
docs/sql/90_archive/README.md
Normal file
7
docs/sql/90_archive/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# docs/sql/90_archive
|
||||
|
||||
本目录存放:
|
||||
- 历史/废弃 SQL(只读归档)
|
||||
- 不再作为权威引用口径
|
||||
|
||||
如需保留旧版本 RPC/策略,请迁移到此目录并在新版本中注明替代关系。
|
||||
Reference in New Issue
Block a user