登录注册接入数据库
This commit is contained in:
85
pages/mall/admin/MULTI_TERMINAL_REGISTRATION_GUIDE.md
Normal file
85
pages/mall/admin/MULTI_TERMINAL_REGISTRATION_GUIDE.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# 多端用户注册与身份识别实现指南 (Multi-Terminal Registration Guide)
|
||||
|
||||
本档说明了如何在当前的 Supabase 架构下实现“消费者端”、“商家端”及“管理端”的统一注册逻辑,并确保用户身份(Role)在入库时能够自动、准确地被识别。
|
||||
|
||||
## 1. 核心架构原理
|
||||
|
||||
系统采用 **“前端声明意图 + 后端自动触发”** 的模式:
|
||||
1. **前端 App**:在调用接口注册时,通过 `raw_user_meta_data` 声明用户的目标角色(如 `consumer` 或 `merchant`)。
|
||||
2. **Supabase Auth**:接收并存储这些元数据。
|
||||
3. **数据库触发器 (Trigger)**:在 `auth.users` 产生新记录的一瞬间,由数据库自动读取元数据,并将用户信息连带其正确的角色属性同步到业务表 `ak_users` 中。
|
||||
|
||||
---
|
||||
|
||||
## 2. 前端实现步骤 (代码参考)
|
||||
|
||||
在各端 App 的注册逻辑中,需在调用 `signUp` 接口时传递 `options.data`。
|
||||
|
||||
### 消费者端 (Consumer App)
|
||||
在 `pages/user/register.uvue` 中:
|
||||
```typescript
|
||||
const options = new UTSJSONObject()
|
||||
const metaData = new UTSJSONObject()
|
||||
metaData.set('user_role', 'consumer') // 核心:声明为消费者
|
||||
options.set('data', metaData)
|
||||
|
||||
const result = await supa.signUp(email, password, options)
|
||||
```
|
||||
|
||||
### 商家端 (Merchant App)
|
||||
在商家端的注册页面中,只需修改 `user_role` 的值:
|
||||
```typescript
|
||||
metaData.set('user_role', 'merchant') // 核心:声明为商家
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据库实现步骤 (SQL 设置)
|
||||
|
||||
为了让数据库能够“看碟下菜”,必须在 Supabase SQL Editor 中运行以下脚本,安装/更新智能触发器:
|
||||
|
||||
```sql
|
||||
-- 1. 创建或更新处理函数
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS trigger AS $$
|
||||
BEGIN
|
||||
-- 向业务表插入数据,并智能识别角色
|
||||
INSERT INTO public.ak_users (id, auth_id, email, role, nickname, status)
|
||||
VALUES (
|
||||
NEW.id,
|
||||
NEW.id, -- 统一使用 Auth ID
|
||||
NEW.email,
|
||||
-- 核心逻辑:读取 metadata 中的 user_role,如果没有传则默认为 'consumer'
|
||||
COALESCE(NEW.raw_user_meta_data->>'user_role', 'consumer'),
|
||||
-- 默认昵称取邮箱前缀
|
||||
split_part(NEW.email, '@', 1),
|
||||
1 -- 默认激活状态
|
||||
);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- 2. 绑定触发器
|
||||
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
|
||||
CREATE TRIGGER on_auth_user_created
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 三端互不干扰的优势
|
||||
|
||||
* **全自动入库**:一旦 SQL 触发器设置完成,前端不再需要手动调用 `ensureUserProfile` 或 `insert` 接口,减少了网络请求和前端报错几率。
|
||||
* **物理隔离与 RLS 安全**:通过 `ak_users` 表的 RLS 策略(`auth.uid() = id`),确保即使用户通过 API 尝试修改他人数据,也会被数据库直接拦截。
|
||||
* **统一维护**:所有端的注册逻辑在数据库层面是统一的,未来若需增加新角色(如 `admin_manager`),只需修改触发器逻辑即可,无需大规模重构代码。
|
||||
|
||||
---
|
||||
|
||||
## 5. 开发建议
|
||||
|
||||
* **强制校验**:在生产环境下,可以在触发器内增加校验逻辑,防止普通用户通过伪造元数据获得 `admin` 角色。
|
||||
* **日志排查**:如果新用户注册后 `ak_users` 表没有数据,请检查 Supabase 控制台的 `Database -> Logs`,查看触发器执行是否有报错(通常是唯一索引冲突导致)。
|
||||
|
||||
---
|
||||
*最后更新时间:2026-03-10*
|
||||
@@ -1,100 +1,10 @@
|
||||
index.uvue:991 GET http://localhost:5173/pages/mall/admin/product/product-management/index.uvue?vue&type=style&index=0&scoped=6161a702&lang.scss net::ERR_ABORTED 500 (Internal Server Error)
|
||||
main.uts:16 [Vue warn]: Unhandled error during execution of async component loader
|
||||
at <AsyncComponentWrapper>
|
||||
at <PageBody>
|
||||
at <Page>
|
||||
at <Anonymous>
|
||||
at <KeepAlive>
|
||||
at <RouterView>
|
||||
at <Layout>
|
||||
at <App>
|
||||
warnHandler @ uni-h5.es.js:19975
|
||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||
warn$1 @ vue.runtime.esm.js:1207
|
||||
logError @ vue.runtime.esm.js:1438
|
||||
errorHandler @ uni-h5.es.js:19600
|
||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||
handleError @ vue.runtime.esm.js:1421
|
||||
onError @ vue.runtime.esm.js:3724
|
||||
(anonymous) @ vue.runtime.esm.js:3767
|
||||
Promise.catch
|
||||
setup @ vue.runtime.esm.js:3766
|
||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||
setupStatefulComponent @ vue.runtime.esm.js:8985
|
||||
setupComponent @ vue.runtime.esm.js:8946
|
||||
mountComponent @ vue.runtime.esm.js:7262
|
||||
processComponent @ vue.runtime.esm.js:7228
|
||||
patch @ vue.runtime.esm.js:6694
|
||||
mountChildren @ vue.runtime.esm.js:6942
|
||||
processFragment @ vue.runtime.esm.js:7158
|
||||
patch @ vue.runtime.esm.js:6668
|
||||
mountChildren @ vue.runtime.esm.js:6942
|
||||
processFragment @ vue.runtime.esm.js:7158
|
||||
patch @ vue.runtime.esm.js:6668
|
||||
mountChildren @ vue.runtime.esm.js:6942
|
||||
mountElement @ vue.runtime.esm.js:6849
|
||||
processElement @ vue.runtime.esm.js:6814
|
||||
patch @ vue.runtime.esm.js:6682
|
||||
mountChildren @ vue.runtime.esm.js:6942
|
||||
mountElement @ vue.runtime.esm.js:6849
|
||||
processElement @ vue.runtime.esm.js:6814
|
||||
patch @ vue.runtime.esm.js:6682
|
||||
mountChildren @ vue.runtime.esm.js:6942
|
||||
processFragment @ vue.runtime.esm.js:7158
|
||||
patch @ vue.runtime.esm.js:6668
|
||||
componentUpdateFn @ vue.runtime.esm.js:7372
|
||||
run @ vue.runtime.esm.js:153
|
||||
instance.update @ vue.runtime.esm.js:7497
|
||||
setupRenderEffect @ vue.runtime.esm.js:7507
|
||||
mountComponent @ vue.runtime.esm.js:7274
|
||||
processComponent @ vue.runtime.esm.js:7228
|
||||
patch @ vue.runtime.esm.js:6694
|
||||
mountChildren @ vue.runtime.esm.js:6942
|
||||
mountElement @ vue.runtime.esm.js:6849
|
||||
processElement @ vue.runtime.esm.js:6814
|
||||
patch @ vue.runtime.esm.js:6682
|
||||
componentUpdateFn @ vue.runtime.esm.js:7372
|
||||
run @ vue.runtime.esm.js:153
|
||||
instance.update @ vue.runtime.esm.js:7497
|
||||
setupRenderEffect @ vue.runtime.esm.js:7507
|
||||
mountComponent @ vue.runtime.esm.js:7274
|
||||
processComponent @ vue.runtime.esm.js:7228
|
||||
patch @ vue.runtime.esm.js:6694
|
||||
componentUpdateFn @ vue.runtime.esm.js:7372
|
||||
run @ vue.runtime.esm.js:153
|
||||
instance.update @ vue.runtime.esm.js:7497
|
||||
setupRenderEffect @ vue.runtime.esm.js:7507
|
||||
mountComponent @ vue.runtime.esm.js:7274
|
||||
processComponent @ vue.runtime.esm.js:7228
|
||||
patch @ vue.runtime.esm.js:6694
|
||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
||||
run @ vue.runtime.esm.js:153
|
||||
instance.update @ vue.runtime.esm.js:7497
|
||||
updateComponent @ vue.runtime.esm.js:7305
|
||||
processComponent @ vue.runtime.esm.js:7239
|
||||
patch @ vue.runtime.esm.js:6694
|
||||
componentUpdateFn @ vue.runtime.esm.js:7453
|
||||
run @ vue.runtime.esm.js:153
|
||||
instance.update @ vue.runtime.esm.js:7497
|
||||
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||
flushJobs @ vue.runtime.esm.js:1585
|
||||
Promise.then
|
||||
queueFlush @ vue.runtime.esm.js:1494
|
||||
queueJob @ vue.runtime.esm.js:1488
|
||||
scheduler @ vue.runtime.esm.js:3179
|
||||
resetScheduling @ vue.runtime.esm.js:236
|
||||
triggerEffects @ vue.runtime.esm.js:280
|
||||
triggerRefValue @ vue.runtime.esm.js:1033
|
||||
set value @ vue.runtime.esm.js:1078
|
||||
finalizeNavigation @ vue-router.mjs?v=ed041164:2474
|
||||
(anonymous) @ vue-router.mjs?v=ed041164:2384
|
||||
Promise.then
|
||||
pushWithRedirect @ vue-router.mjs?v=ed041164:2352
|
||||
push @ vue-router.mjs?v=ed041164:2278
|
||||
install @ vue-router.mjs?v=ed041164:2631
|
||||
use @ vue.runtime.esm.js:5190
|
||||
initRouter @ uni-h5.es.js:19886
|
||||
install @ uni-h5.es.js:19955
|
||||
use @ vue.runtime.esm.js:5190
|
||||
(anonymous) @ main.uts:16
|
||||
main.uts:16 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/homePage/index.uvue?import
|
||||
signIn result:
|
||||
AkSupaSignInResult {access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4Y…HNlfQ.5BvQq26lJUF23AEglMvA7EzJZvYN_hrp1Q2JA6o0s-w', refresh_token: 'ehhxnwgvgeyk', expires_at: 1773136334, user: UTSJSONObject2, token_type: 'bearer', …}
|
||||
login.uvue:175 🔍 开始校验商家端角色 -> UID: 8bdf11be-2838-4d96-8552-0949cde076d4, Email: test19@163.com
|
||||
login.uvue:216 ❌ 查询角色过程异常: TypeError: res.getData is not a function
|
||||
at login.uvue:180:23
|
||||
at Generator.next (<anonymous>)
|
||||
login.uvue:435 登录错误: Error: 商家身份校验失败,请联系管理员检查用户数据
|
||||
at login.uvue:221:9
|
||||
at Generator.next (<anonymous>)。
|
||||
打印这些东西
|
||||
Reference in New Issue
Block a user