手冊首頁 / 租戶與組織 / 使用者表單

使用者表單(User Form)

功能群:系統管理|三層 RBAC 模型與租戶/部門組織模型先讀 功能群總覽 §2、§3|上層導航頁見 使用者管理

事實基準:2026-07-05 從 FE src/views/user-manage/UserMTRBACForm.vue(641 行)+ BE api/auth/routes/user_route.py / change_password_route.py + jedi-auth user_service.py / user_domain_service.py + routes.json / db_schema.json 掃出。

變更紀錄

日期 FR 變更
2026-07-06 FR-047 user-manage.md 拆出:帳號新增/編輯表單頁(含授權組合指派)獨立成子頁

1. 功能描述

使用者表單是 使用者管理 列表「建立」/「編輯」導向的獨立表單頁(route user-form,實掛 /auth/user-form-mtrbacUserMTRBACForm.vue)。新增與編輯共用同一表單:上半填帳號基本欄位(登入名稱 / 姓名 / Email / 職稱 / 電話 / 生效到期日 /(編輯限定)代改密碼),下半以 DataTable 維護「租戶 × 部門 × 角色」的授權組合(多筆,恰一筆為預設)。儲存時基本欄位與授權組合陣列一起送出,user_roles / tenants / org_units 三表全量取代

主要使用者:系統 / 租戶管理員。權限治理(角色如何綁能力)由 角色權限管理 負責;本頁只做「使用者 ↔︎ 角色/租戶/部門」的指派。

角色速覽(完整定義見 _overview §2、§3):

角色 一句話
系統 / 租戶管理員(可見本選單者) 帳號 CRUD + 授權組合指派
is_admin(超級管理員 JWT 旗標) 額外可在編輯他人帳號時代改密碼;admin 帳號本身的敏感欄位(生效/到期日、刪除授權)鎖死
一般使用者 無此選單(ui_routes 可見性未命中)

⚠️ route 命名陷阱:FE views/user-manage/UserForm.vue(463 行)是死碼——router 中對應的 /auth/user-form route 已整段註解掉(config/router/index.js:138-145)。目前 user-formname)這個路由名實際指向 /auth/user-form-mtrbacUserMTRBACForm.vue(641 行,multi-tenant RBAC 版表單),本 spec 全部以此為準,不描述 UserForm.vue 的行為。

1.1 功能總覽(本頁全部功能)

# 功能 說明 位置 詳述
1 帳號基本欄位 登入名稱*(新增後不可改)/ 姓名* / Email* / 職稱 / 電話 表單頁上半 §6.2
2 帳號生效 / 到期日 日期選擇器;admin 帳號固定禁用 表單頁上半 §4
3 變更密碼(管理員代改) 僅登入者本身 is_admin 時顯示開關;開啟後顯示密碼 / 確認密碼欄 表單頁上半(編輯模式限定) §4
4 授權組合(租戶 × 部門 × 角色)清單 DataTable 呈現該使用者全部授權列,逐筆編輯 / 刪除,恰一筆標記「預設權限」 表單頁下半 UC-USER-02
5 新增 / 編輯授權組合 Dialog 選租戶 → 動態帶出該租戶部門與角色下拉 → 勾選是否預設 表單頁下半 Dialog UC-USER-02
6 儲存帳號 送出基本欄位 + 授權組合陣列(user_roles / tenants / org_units 皆全量取代 表單頁 UC-USER-01
7 取消編輯 二次確認後返回列表 表單頁

展開成 UC:UC-USER-01(建立 / 編輯帳號主流程)、UC-USER-02(授權組合指派)。停用 / 刪除 / 列表操作屬 使用者管理 hub。

2. Use Case

角色速覽(完整定義見 _overview §2、§3):

角色 一句話
系統 / 租戶管理員(可見本選單者) 帳號 CRUD + 授權組合指派
is_admin(超級管理員 JWT 旗標) 額外可在編輯他人帳號時代改密碼;admin 帳號本身的敏感欄位鎖死
一般使用者 無此選單

流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點。

UC-USER-01 建立 / 編輯帳號

項目 內容
角色 具本頁可見性的管理員
前置條件 新增:login_name / email 未被使用;編輯:帳號存在
產出 / 後置條件 public.users 新增 / 更新一列 + user_tenants / user_org_units / user_roles 全量重建
user_create start 進入使用者管理列表 click 點「建立帳號」或列上「編輯」/登入代碼 start->click form 表單載入 initStaticMenu + fetchRoleMenu 編輯時 GET /user/<uid> 回填 user_roles click->form fill 填登入名稱/姓名/Email/職稱/電話 至少一筆授權組合(UC-USER-02) form->fill chgpwd 是否勾選變更密碼?(僅編輯+is_admin可見) fill->chgpwd pwdrule BE 檢查:呼叫者非 is_admin 改他人密碼? can_change_password chgpwd->pwdrule validate FE vuelidate:必填欄位 + 密碼強度規則 chgpwd->validate pwdblock 擋下:非本人不可改他人密碼 pwdrule->pwdblock 是(非本人) pwdrule->validate 否(本人或is_admin) save 按儲存 POST /user(新增)或 PUT /user/<uid>(編輯) validate->save loginchk BE 檢查:login_name 是否重複? save->loginchk emailchk BE 檢查:email 是否重複? loginchk->emailchk 不重複 err_login 409 AUTH_409001 使用者已存在 loginchk->err_login 重複 refchk BE 檢查:租戶/部門/角色是否存在? emailchk->refchk 不重複 err_email 409 AUTH_409002 使用者信箱已存在 emailchk->err_email 重複 err_ref 404 AUTH_404003/4/5 角色/部門/租戶不存在 refchk->err_ref 不存在 write 寫入 users 全量重建 user_tenants/user_org_units/user_roles 新增才建 user_change_password_request refchk->write 皆存在 mail 新增時:未帶密碼則自動產生 寄送帳號建立信(含明碼密碼,僅信件內容) write->mail 新增模式 ok 成功 → 回列表 write->ok 編輯模式 mail->ok
UC-USER-01 流程:新增/編輯共用表單→驗證→存檔;login_name/email 重複與租戶/部門/角色不存在皆 409/404 擋下

UC-USER-02 授權組合(租戶 × 部門 × 角色)指派

項目 內容
角色 同上
前置條件 表單已載入 tenantMenu / roleMenu;至少一筆授權組合(必填)
產出 / 後置條件 表單本地 user_roles[] 陣列變化;實際寫入在 UC-USER-01 儲存時發生
user_auth start 表單下半:授權組合清單 isadmin 目前編輯對象是 admin 帳號? start->isadmin del 刪除某筆授權(二次確認) start->del block 擋下:新增/編輯/刪除授權鈕皆禁用 isadmin->block click 點「新增授權」或某列「編輯」 isadmin->click dialog Dialog 開啟 選租戶 → onTenantChange 過濾該租戶的部門/角色下拉 click->dialog fillauth 選部門 / 角色 / 是否為預設權限 dialog->fillauth savelocal 存入本地 user_roles[] 陣列 (編輯=取代該筆,新增=push) fillauth->savelocal onlyone 唯一一筆? savelocal->onlyone forcedft 強制設為 is_default=true onlyone->forcedft isdft 此筆標為 is_default? onlyone->isdft done Dialog 關閉,表格更新 forcedft->done clearoth 其餘全部改 is_default=false isdft->clearoth isdft->done clearoth->done nodft 刪除後是否已無任何 is_default? del->nodft nodft->done autofill 自動補第一筆為 is_default nodft->autofill autofill->done
UC-USER-02 流程:Dialog 選租戶→動態過濾部門/角色→存入本地陣列→唯一 is_default 規則

3. 權限矩陣

專案層 RBAC 基調見 _overview §5。本頁的實際檢查點:

操作 FE 判定 BE 強制 BE 檢查位置
建立帳號 有建立按鈕即可 ⚠️ 僅 @jwt_required無角色 / 能力守門 user_route.py:75-88UserRoute.post
編輯帳號 同上 ⚠️ 僅 @jwt_required user_route.py:90-101UserRoute.put
代改他人密碼 僅登入者 userInfo.is_admin 時顯示開關(UserMTRBACForm.vue:502 :非 is_admin 改他人密碼會被 can_change_password 擋下 jedi-auth user_domain_service.py:259can_change_password)→ user_service.py:270-272
改自己密碼 個人資料頁另一條路徑(見 個人資料),非本頁

⚠️ 本頁寫入端點缺角色守門UserRoute 全部 method 皆只掛 @jwt_required(),jedi-auth user_service.add_user/update_user 內部也無角色或能力檢查——任何已登入帳號皆可透過直打 API 新增 / 編輯他人帳號(含 admin,因「不可動 admin」只是 FE 隱藏按鈕)。唯一的例外是密碼代改update_user 內有 can_change_password 明確擋非管理員改他人密碼。與 _overview §5role-manage.md §3 一致(本群多頁通病)。收進 §12,也見 使用者管理 §12

4. 狀態機與前置條件

帳號本身狀態(users.status)與列表操作 gate 見 使用者管理 §4。本表單頁 gate:

條件 效果 出處
login_name === 'admin'(表單內) 生效日 / 到期日 Calendar 禁用;新增授權按鈕禁用;授權列的編輯 / 刪除鈕禁用 UserMTRBACForm.vue:485,498,544,551-552
mode === 'EDIT' 登入名稱欄位鎖死(不可改) UserMTRBACForm.vue:434
userInfo.is_admin && mode === 'EDIT' 顯示「變更密碼」開關;非 admin 登入者看不到此區塊(僅能改自己密碼,走 user-profile 頁) UserMTRBACForm.vue:502
canEdit(currUser) = mode==ADD || userInfo.is_admin || userInfo.uid===currUser.uid 姓名 / Email 欄位可編輯性 UserMTRBACForm.vue:299-310
授權組合陣列長度 = 1 該筆自動設為 is_default UserMTRBACForm.vue:361-364
設某筆 is_default=true 其餘全部改 is_default=false(前端保證唯一預設) UserMTRBACForm.vue:365-370
刪除授權列後無任何 is_default 自動補第一筆為預設 UserMTRBACForm.vue:400-404
BE 寫入無狀態前置檢查 add_user / update_user 本身不檢查 status,任何狀態的帳號都可被改(唯一狀態轉換在 update_user_status 專職) jedi-auth user_service.py

5. UI 設計

使用者表單(新增/編輯共用)— 整包送出,非逐欄儲存 開啟:§6.2 使用者 CRUD request/response 上半 · 帳號基本欄位 登入名稱*(EDIT 鎖死)|姓名*|Email*|職稱|電話 生效日/到期日(admin 禁用) POST /user(建立) PUT /user/{uid}(更新,關聯全量取代) → §6.2(點我) 開啟:§4 狀態機與前置條件 變更密碼(條件顯示) 僅 is_admin + EDIT 模式 開關 → 密碼/確認密碼欄 ≥12 碼+大小寫+數字 can_change_password 守門 → §4(點我) 開啟:UC-USER-02 授權組合指派 下半 · 授權組合(租戶 × 部門 × 角色)DataTable 逐列:租戶|部門|角色|預設(is_default 唯一)|編輯/刪除 送出時衍生 payload.tenants / org_units(去重 id,全量取代) user_roles[] → users.tenant_id/org_unit_id 由 is_default 反推 → UC-USER-02(點我) 授權 Dialog(疊層) 選租戶 → 動態帶部門/角色下拉 勾選是否預設 → 存入本地陣列 GET /tenants|org-units|roles/menu 儲存(vuelidate 驗證後整包送出)|取消(二次確認返回列表) 彩色=可點跳本頁小節錨點(teal=§6 基本欄位/coral=UC-USER-02 授權)|灰=結構描述|虛線框=條件顯示/疊層 Dialog API 僅列代表性,完整規格見 §6
使用者表單頁版面 wireframe:上半基本欄位+(編輯限定)變更密碼開關、下半授權組合 DataTable+新增/編輯 Dialog;標代表性 API POST/PUT /user

📸 實機截圖待補(亮色模式)。待補清單:表單頁上半基本欄位、表單頁下半授權組合列表、新增/編輯授權組合 Dialog、admin 帳號鎖死狀態(生效/到期日禁用)。

表單頁骨架

user_form_layout layout 使用者表單(user-form → UserMTRBACForm.vue) 登入名稱* (編輯模式鎖死) 姓名* Email* 職稱 電話 帳號生效日 (admin鎖死) 帳號到期日 (admin鎖死) 變更密碼開關(僅編輯模式 + 呼叫者 is_admin 可見) 展開:密碼/確認密碼(≥12碼+大小寫+數字) 授權組合*(租戶×部門×角色,至少一筆)  [+ 新增授權] 功能(編輯/刪除) 租戶 部門 角色 預設權限 編輯|刪除 (admin目標鎖死) 租戶A 部門A 管理員 [取消]  [儲存] dialog 新增/編輯授權組合 Dialog 租戶下拉 部門下拉(依所選租戶動態過濾) 角色下拉(依所選租戶動態過濾) 是否為預設權限(開關;唯一) [取消] [儲存] layout->dialog 點「新增授權」/「編輯」開啟
使用者表單版面:上半基本欄位(登入名稱/姓名/Email/職稱/電話/生效到期日/變更密碼開關)+下半授權組合DataTable(租戶/部門/角色/預設)+新增編輯Dialog

狀態呈現:表單頁儲存 / 讀取中蓋一層全螢幕遮罩 + ProgressSpinnersubmitted / loading 兩個獨立旗標,避免進頁誤顯示「儲存中」)。

6. API 規格

Envelope:成功 {"code": 1, "data": …}、失敗 {"code": 0, "msg": "…"}

6.1 總清單(本頁呼叫的全部 endpoint)

分類 Method + Path 說明 完整規格
明細 GET /api/1.0/user/<uid> 單一使用者(含 auth_providers,排除 salt/password) §6.2
建立 POST /api/1.0/user 建立使用者 + 授權組合 §6.2
更新 PUT /api/1.0/user/<uid> 更新使用者 + 授權組合全量重建(可選帶密碼) §6.2
下拉資料 GET /tenants/menu/org-units/menu/roles/menu/capabilities/menu 授權組合 Dialog 的租戶 / 部門 / 角色下拉 GAI-SD-02

列表 / 狀態變更 / 刪除 / Excel 匯入端點見 使用者管理 §6Excel 批次匯入 §6

6.2 使用者 CRUD(request / response)

[GET] /api/1.0/user/

Response:UserResponse(exclude salt/password),額外含 auth_providers[]UserAuthProviderResponse)。欄位卡見 使用者管理 §6.2 列表回傳。出處:user_route.py:57-73

[POST] /api/1.0/user(建立)/ [PUT] /api/1.0/user/(更新)

Request(UserRequestapi/auth/serializers/user.py:5-19):

欄位 型別 必填 說明
login_name string 登入代碼(建立後不可改,FE 鎖死)
email string Email(重複會 409)
nickname string 否(FE 端 required) 顯示名稱
job_title / tel string 職稱 / 電話
password string 有值才觸發改密碼流程
effective_date / expire_date datetime YYYY-MM-DD HH:MM:SS 帳號生效 / 到期日
user_roles list[UserRoleRequest] 是(FE 端 required,至少 1 筆) 授權組合:{role_uid, tenant_id, org_unit_id, scope, is_default, start_at, end_at}
tenants list[int] 由 FE 從 user_roles[].tenant.id 去重組出,全量取代該使用者的 user_tenants
org_units list[int] 同上邏輯,全量取代 user_org_units
status int 否(僅編輯回填顯示,非本頁編輯欄位)

Response:同 GET(exclude salt/password/last_login_success_time/last_login_failed_time)。

BE 鏈路UserRoute.post/putuser_route.py:75-101)→ jedi-auth UserService.add_user / update_useruser_service.py:100-338)。關鍵行為:

  • 授權組合陣列的第一筆或標 is_default 的那筆決定 user_entity.tenant_id / org_unit_id(使用者的預設租戶/部門欄位)
  • tenants / org_units / user_roles 三者在 update_user先刪後插(全量取代)user_tenant_domain_service.delete_by_user → 重建;user_org_unit_domain_service.delete_by_user → 重建;user_role_domain_service.delete_user_role_by_user_id → 重建
  • 新增時若 password 未帶,generate_complex_password(12) 自動產生亂碼密碼,寄送帳號建立通知信(含明碼密碼,僅信件內、不落 log;user_service.py:102-104 明確禁止把含密碼的 kwargs 整包 dump 進 log)
  • 編輯改密碼:非 is_admin 呼叫者會先過 can_change_password(curr_user.uid, uid)(只能改自己),並寫入 user_change_pwd_logs
  • 檢核順序:login_name 重複 → email 重複 → 租戶存在 → 部門存在 → 角色存在(uid 清單)

出處:user_service.py:99-214add_user)、:216-338update_user)。

7. 前端檔案地圖(compliance-manager-fe/)

檔案 角色
src/views/user-manage/UserMTRBACForm.vue route user-form(實際掛在 /auth/user-form-mtrbac):建立 / 編輯共用表單 + 授權組合 Dialog
src/views/user-manage/UserForm.vue ⚠️ 死碼:無任何 route 指向,router 對應項已整段註解(見 §1)
src/config/router/index.js:126-172 /auth 群組:user-manage / user-form(→UserMTRBACForm.vue)/ role-manage / role-form
src/stores/menuStore.js fetchRoleMenu() / tenantMenu / orgUnitMenu / capabilityMenu(授權組合 Dialog 下拉來源)
src/utils/userUtil.js userInfo(含 is_admin / uid / login_name,供本頁多處權限判斷)
src/config/api/api.js:32-42 USER / ROLE_MENU 等端點常數

8. 後端檔案地圖

鏈路 Route App Service 底層
使用者 CRUD user_route.pyUserRoute 直接委派(無主專案 app service 包裝) jedi-auth user_service.pyuser_domain_service.py → repo
忘記密碼 / 改密請求 api/auth/routes/change_password_route.py 直接委派 jedi-auth user_change_password_service.py

DDD 提醒:使用者 CRUD 的 route 直接注入 jedi-auth 的 UserService,主專案在這條鏈路無 app service 包裝。大半 domain 邏輯在 jedi-auth 套件(~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/),改行為要分清邊界(CLAUDE.md 外部套件異動規範)。此鏈路模式與 role-manage.md §8 相同(本群通用模式)。

9. DB

9.1 資料表總清單(本頁讀寫的全部表)

讀/寫 說明 詳述
public.users 讀 / 帳號本體(含 salt/password,敏感欄位僅描述不列值) §9.3
public.user_roles 讀 / 寫(全量取代) 使用者 ↔︎ 角色的授權組合(帶 tenant_id/org_unit_id 授權邊界) §9.3
public.user_tenants 讀 / 寫(全量取代) 使用者 ↔︎ 租戶(is_default _overview §3
public.user_org_units 讀 / 寫(全量取代) 使用者 ↔︎ 部門(is_primary _overview §3
public.user_change_password_request 新增帳號時建立首次註冊改密請求(type=FIRST_REGISTER §9.3
public.user_change_pwd_logs 管理員代改密碼時寫入歷史 §9.3
public.user_auth_providers 使用者第三方登入綁定(GET 明細帶出,本頁不維護寫入) §9.3
public.roles 角色下拉、verify_role_exist_by_uids 檢核 role-manage.md §9
public.tenants 租戶下拉、verify_tenant_exist_by_id 檢核 _overview §6
public.org_units 部門下拉、verify_org_unit_exist_by_id 檢核 _overview §6

9.2 ER 圖

user_er users public.users (帳號本體,寫) user_roles public.user_roles (授權組合,寫,全量取代) users->user_roles user_id user_tenants public.user_tenants (寫,全量取代) users->user_tenants user_id user_org_units public.user_org_units (寫,全量取代) users->user_org_units user_id chg_req public.user_change_password_request (寫,新增時建立) users->chg_req user_id chg_log public.user_change_pwd_logs (寫,代改密碼時) users->chg_log user_id auth_providers public.user_auth_providers (唯讀) users->auth_providers user_id roles public.roles (唯讀依賴) user_roles->roles role_id tenants public.tenants (唯讀依賴) user_roles->tenants tenant_id(授權邊界,nullable) org_units public.org_units (唯讀依賴) user_roles->org_units org_unit_id(授權邊界,nullable) user_tenants->tenants tenant_id user_org_units->org_units org_unit_id
使用者表單讀寫範圍:綠=本頁寫入(users/user_roles/user_tenants/user_org_units/改密相關)、黃=唯讀依賴(roles/tenants/org_units)

9.3 核心表欄位(取自 DEV DB dump,含 DB comment)

public.users — 帳號本體

欄位 型別 說明
id / uid integer / varchar(36) 內部序號 / 對外識別碼
login_name varchar(255) 登入名稱(唯一,建立後不可改)
nickname varchar(255) 顯示名稱
salt / password varchar(250) 密碼鹽值 / 加密密碼(敏感欄位,僅描述存在,不列值;API response 一律 exclude=["salt","password"]
email varchar(100) 電子郵件(唯一)
job_title / tel varchar(100) / varchar(50) 職稱 / 電話(皆選填)
effective_date / expire_date timestamp 帳號生效 / 到期日
status integer 0 未啟用 / 1 啟用 / -1 停用 / -2 刪除
last_login_success_time / last_login_failed_time / login_failed_count timestamp / timestamp / integer 登入軌跡(列表可排序顯示)
mfa_type varchar(50) 多因素驗證類型(TOTP 設定見 個人資料
topt_secret text TOTP 密鑰(敏感欄位;命名沿用套件既有拼法,非本頁 typo)
is_super_admin boolean 超級管理員旗標(DB 欄位;JWT context 的 is_admin 由此衍生)
tenant_id / org_unit_id integer 使用者的預設租戶 / 部門(由 user_roles 中標 is_default 的那筆回填,非獨立輸入)
employee_id varchar(20) 員工編號(選填,表單無對應輸入欄,僅 API 層可帶)
created_user / created_at / updated_user / updated_at varchar(50) / timestamp 審計欄

public.user_roles — 授權組合(本頁核心寫入表)

欄位 型別 說明
id bigint 主鍵
user_id / role_id integer → users / roles
scope varchar(10) global / tenant / org(依是否帶 tenant_id/org_unit_id 推導,非使用者直接選)
tenant_id / org_unit_id integer(nullable) 授權邊界:該角色只在此租戶 / 部門內生效
starts_at / ends_at timestamp 角色生效 / 結束時間(表單目前未提供輸入,API 層可帶)
is_default boolean 是否為使用者的預設授權(唯一,決定 users.tenant_id/org_unit_id

public.user_tenants / public.user_org_units — 租戶 / 部門歸屬

user_tenantsuser_id / tenant_id / is_default / starts_at / ends_atuser_org_unitsuser_id / org_unit_id / is_primary / starts_at / ends_at。兩表在 update_user 時皆 delete_by_user 後依 user_roles 推導的租戶/部門集合重建(全量取代,非差異更新)。

public.user_change_password_request / public.user_change_pwd_logs

user_change_password_requestuid / user_id / type(0 首次註冊 / 1 密碼重設 / 2 忘記密碼 / 3 密碼過期 / 9 自行變更)/ status(0 未使用 / 1 已使用)/ expired;新增使用者時固定建一筆 type=FIRST_REGISTER、7 天後過期。user_change_pwd_logsuser_id / type / password(加密後);僅管理員代改密碼成功時寫入一筆。

public.user_auth_providers(唯讀)

uid / user_id / provider / provider_user_id:第三方登入綁定,本頁 GET 明細會回傳但無編輯 UI。

10. 頁面邏輯與資料對應

表單載入時序

user_load_sequence mounted onMounted staticmenu menuStore.initStaticMenu({t}) (tenantMenu / orgUnitMenu / capabilityMenu / scopeMenu) mounted->staticmenu rolemenu menuStore.fetchRoleMenu() GET /roles/menu staticmenu->rolemenu hasuid route.query.uid 存在? rolemenu->hasuid getuser GET /user/<uid> 回填 currUser(含 user_roles[]) hasuid->getuser addmode mode = ADD (currUser 保持初始空殼) hasuid->addmode editmode mode = EDIT breadcrumb 加 -edit getuser->editmode ready 表單就緒: 授權組合表格顯示 currUser.user_roles addmode->ready editmode->ready
使用者表單載入:onMounted→initStaticMenu+fetchRoleMenu→有uid則GET使用者→回填user_roles顯示於授權組合表格

關鍵欄位對應(API ↔︎ 畫面):

畫面元素 FE state API 欄位
登入名稱 / 姓名 / Email / 職稱 / 電話 currUser.login_name/nickname/email/job_title/tel UserRequest 同名欄位
生效日 / 到期日 currUser.effective_date/expire_date(Calendar,自訂格式化) 同名,送出前補 00:00:00
授權組合表格 currUser.user_roles[] payload.user_roles[]{role_uid, tenant_id, org_unit_id, is_default}) + 衍生 payload.tenants / payload.org_units(去重 id 陣列)
授權組合 Dialog:租戶/部門/角色下拉 currAuth.tenant/org_unit/role(物件) 存入陣列前轉成 *_id / *_name / *_uid 扁平欄位
變更密碼開關 + 密碼欄 currSecret.secret/confirm_secret payload.password(僅有值才附帶)

儲存流程:表單整包送出(非逐欄位獨立儲存,不同於專案規劃頁);onSaveClick 先跑 vuelidate(基本欄位必填 + 密碼強度規則:≥12 碼、含大小寫與數字),通過才組 payload 呼叫 POST /user(新增)或 PUT /user/<uid>(編輯);成功後導回列表(router.push({name:'user-manage'}))。

錯誤對應:BE error envelope → toast 顯示;常見碼:AUTH_409001login_name 已存在)、AUTH_409002email 已存在)、AUTH_404005(租戶不存在)、AUTH_404004(部門不存在)、AUTH_404003(角色不存在)。

11. 背景行為與外部依賴

類型 內容
通知 新增帳號成功 → 寄送帳號建立信(含登入名稱 + 明碼密碼,僅信件內容,不落 log);notification_service 為 optional 注入,未設定時靜默跳過
Job / 排程
Socket
jedi-* 套件 jedi-auth 擁有使用者 / 角色 / 租戶 / 部門的 service / domain / repo / ORM 全部邏輯;本頁 CRUD 直接委派套件,無主專案 wrapper
密碼規則 新增未帶密碼 → generate_complex_password(12) 產生亂碼;FE 端管理員代改密碼強制 ≥12 碼 + 大小寫 + 數字(vuelidate secretRules),BE 本身無獨立密碼強度檢查(僅 hash 儲存)

12. 邊界情況與已知坑

  1. UserForm.vue 是死碼:463 行完整表單元件,但 router 對應 /auth/user-form route 整段被註解、無任何 import 引用;實際生效的是 UserMTRBACForm.vue(multi-tenant RBAC 版)。維護時勿誤改死碼。
  2. 本頁寫入端點缺角色守門(同 角色權限管理 模式):UserRoute 全部 method 僅 @jwt_required(),jedi-auth service 層也無角色 / 能力檢查——任何登入帳號可直打 API 新增 / 編輯任何人(含 admin)。唯一的例外:改他人密碼有 can_change_password 明確擋非 is_admin 者。本 spec 只記錄不修
  3. user_roles/tenants/org_units 三表皆全量取代update_user 對這三個關聯先 delete_by_user 再重建;FE 表單編輯時務必先讀回完整既有授權組合再送出,漏帶等於刪除該筆授權。
  4. users.tenant_id/org_unit_id(預設值)由 user_roles 反推,非獨立輸入:新增/編輯時,若送出的授權組合都沒有標 is_default,BE 會強制把第一筆設為預設(user_service.py:122-126,239-243),可能與使用者原意不符(FE 已同步這條規則於本地陣列操作,但兩端各自實作、非共用單一 source)。
  5. 敏感欄位(salt/password/topt_secret)一律 exclude:GET/POST/PUT response 都手動 UserResponse(exclude=["salt","password", ...]),新增回傳欄位或改 serializer 務必保留這個 exclude list,避免意外外洩雜湊密碼。
  6. nickname 在 BE serializer 非 required,但 FE vuelidate 端強制必填:兩端必填集合不完全對稱(BE UserRequest.nicknamerequired=True),直打 API 可送出無 nickname 的使用者。
  7. admin 帳號的鎖死規則分散在多處判斷(生效/到期日禁用、新增授權禁用、既有授權列編輯/刪除禁用):皆為 FE isTargetAdmin / 字串比對 login_name==='admin',BE 無對應保護(見坑 2)——非 FE 判斷點若新增功能,記得同步排除 admin

13. 開發與驗證

  • 跑起來:BE python main_socketio.py(port 8000,log 在 log/app.log);FE 在 compliance-manager-fe/ 起 Vite dev server。BE 改 service code 後必須重啟(無 hot reload)。
  • 測試帳號:管理員帳號見 memory reference_dev_login / .env(憑證不寫入本文件)。
  • 導航路徑:登入 → 側選單「系統管理」→「使用者管理」(/auth/user-manage)→ 建立 / 點某列「編輯」進表單頁。
  • 前置資料:至少一個租戶 + 一個部門 + 一個角色(tenant_id NULL 的系統角色亦可)才能完成授權組合指派。
  • E2Ecompliance-manager-test/ repo(Cucumber + Playwright);本頁相關 feature 檔以 使用者管理 / user-form 搜尋。
  • 相關文件:上層導航 使用者管理Excel 批次匯入;權限系統設計說明書 docs/system-design/permission/角色權限管理功能群總覽