回饋管理(Feedback Management)
功能群:協作(collab)|本群無獨立 _overview;公告見 公告管理 / 公告檢視;SPEC 索引見 README
事實基準:2026-07-05 從 FE / BE code 掃出;BE route 對
api/feedback/routes/feedback_route.py、serviceapp/feedback/service/feedback_service.py、schema 名對db_schema.json
1. 功能描述
回饋管理讓使用者提交系統使用回饋(bug /
建議),並提供管理者列表檢視、編輯、匯出。回饋記錄實際存於
public.issues(透過外部 jedi-issue
套件),並以 feedback_issues link row 掛租戶 /
部門並可選鏡像到 GitLab / GitHub issue(見 [議題整合設定])。
重要現況(本頁最大坑):UI 上有「系統回饋」與「韌性巡航回饋(巡檢回饋)」兩塊,但只有系統回饋是可用的。巡檢回饋在 FE 是停用中的殘留 scaffolding:TabView 內的巡檢 tab 已被註解掉(
FeedbackManageTabView.vue:71-76),其獨立管理頁送{type:1}篩選會打爆 BE(FeedbackIssueQueryEntity無type欄,FeedbackIssueQueryEntity(**kwargs)會TypeError)。後端無巡檢專屬模組 / 表 / discriminator 欄位——系統 / 巡檢共用同一組feedback端點,差別只在 FE 送的 payload。詳見 §12。
主要使用者:一般使用者(提交自己的回饋)、管理者(is_admin,檢視 / 編修 / 匯出全部)。
角色速覽:
| 角色 | 一句話 |
|---|---|
| 一般使用者 | 提交回饋、檢視 / 編輯 / 刪除自己的回饋(FE 判定) |
| 管理者(is_admin) | 檢視 / 編輯 / 刪除 / 匯出全部回饋 |
1.1 功能總覽(本頁全部功能)
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 系統回饋管理列表 | 分頁 / 排序 /
搜尋(server-side,useTableQuery);欄:動作 / 類別 / 標題
/ 功能 / 建立者 / 時間 |
系統回饋 tab(SystemFeedbackManage) |
UC-FB-01 |
| 2 | 新增回饋 | 標題 / 類別(label)/ 功能(label)/ 內文(Editor)/ 附件(多檔 advanced) | 系統回饋表單(SystemFeedbackForm) |
UC-FB-02 |
| 3 | 編輯回饋 | 同新增;per-row canEdit(見 §3) |
同上 | UC-FB-02 |
| 4 | 刪除回饋 | confirm → DELETE;連動關閉 / 刪 GitLab·GitHub issue(best-effort) | 列表動作 | §6 |
| 5 | 檢視回饋 | 唯讀:類別 / 功能 / 內文(v-dompurify-html)/
附件下載 |
系統回饋檢視(SystemFeedbackView) |
§5 |
| 6 | 附件上傳 / 下載 / 刪除 | 多檔上傳(≤5MB,白名單副檔);下載走
/file/download;刪走
/feedback/file/{uid}/{fileUid} |
表單 / 檢視 | §6 |
| 7 | Excel 匯出 | 系統回饋列表匯出 xlsx(/feedback/export/excel) |
系統回饋列表 | §6 |
| 8 | 類別 / 功能下拉 | 類別來源 /labels/menu/FEEDBACK_TYPE、功能
/labels/menu/FUNCTION(依使用者可見選單過濾) |
表單 | §6 |
| 9 | 巡檢回饋管理 / 表單 / 檢視(停用中) | 獨立 route 尚存但功能不完整(client-side 分頁、無匯出、無
ownership、下拉 hardcode、{type:1} 會炸 BE) |
cruising-feedback-* |
§12 |
UC(§2)只展開 1(列表檢視)、2(提交 / 編輯)這些系統回饋核心路徑;巡檢回饋因停用不展開 UC,只在 §12 記錄現況。
2. Use Case
角色速覽:
| 角色 | 一句話 |
|---|---|
| 一般使用者 | 提交回饋、檢視 / 編輯 / 刪除自己的回饋(FE 判定) |
| 管理者(is_admin) | 檢視 / 編輯 / 刪除 / 匯出全部回饋 |
流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-FB-01 檢視 / 篩選回饋列表
| 項目 | 內容 |
|---|---|
| 角色 | 一般使用者 / 管理者 |
| 前置條件 | 已登入 |
| 產出 / 後置條件 | 無寫入;回列表(server-side 分頁 / 排序 / 搜尋 title·建立者·修改者) |
UC-FB-02 提交 / 編輯回饋
| 項目 | 內容 |
|---|---|
| 角色 | 一般使用者(編輯限自己或 admin) |
| 前置條件 | 已登入;page_permissions 含 CREATE / UPDATE |
| 產出 / 後置條件 | 建 / 改 issues + feedback_issues + label /
file 關聯;可選鏡像 GitLab / GitHub(best-effort,失敗只 log) |
3. 權限矩陣
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 進列表 / 檢視 | 登入即可 | 僅 @jwt_required() |
api/feedback/routes/feedback_route.py |
| 新增 / 編輯 | page_permissions(getUserPagePermissions)含
CREATE/UPDATE;per-row canEdit:admin 全部 / 否則
item.created_user === login_name |
無角色守門,僅 JWT | 同上 |
| 刪除 | 同 canEdit | 無角色守門 | 同上 |
| 匯出 | 頁面可見即可 | 僅 JWT | 同上 |
⚠️ 安全缺口(§12):回饋所有寫入端點 BE 只驗 JWT,無 owner / role 檢查——非 admin 直接打
PUT/DELETE /feedback/{uid}可改 / 刪別人的回饋,canEdit只是 FE 藏按鈕。與公告頁同樣缺口(見 bulletin-manage.md §12)。
4. 狀態機與前置條件
回饋是扁平 CRUD,FE 無狀態工作流(無 status dropdown
/ badge / 回覆 thread)。注意 public.issues 有
state 欄(回饋狀態),但目前 FE 完全不 surface——狀態由外部
issue 生命週期(如關閉)驅動,非本頁 UI 操作。
| 條件 | 效果 | 出處 |
|---|---|---|
permissions.length === 1 |
tab 傳 :view=true → 列表唯讀模式(隱藏 CRUD) |
FeedbackManageTabView.vue:69 |
canEdit(item) |
決定 per-row 編 / 刪按鈕 | SystemFeedbackManage.vue:161-171 |
| 匯出型別 ∉ {csv, excel} | FEEDBACK_400001(不支援匯出型別) |
feedback_service.py:348 |
5. UI 設計
📸 實際畫面截圖待補(一律亮色模式拍攝):以 playwright 對 STG 擷取,預計補三張到
assets/img/:① 系統回饋列表(含匯出 / 搜尋)② 新增回饋表單(Editor + 多檔上傳)③ 回饋檢視(附件下載)。
版面骨架(TabView 僅系統 tab 生效;巡檢 tab 已註解):
狀態呈現:系統列表 server-side
分頁(useTableQuery)、全螢幕 loading overlay;表單
CKEditor 富文本 + FileUpload advanced 多檔;檢視
v-dompurify-html 淨化內文。無 socket。巡檢管理頁為
client-side 分頁(一次撈全量)、較不完整。
6. API 規格
Envelope:成功 {"code": 1, "data": …}、失敗
{"code": 0, "msg": "…"}。
6.1 總清單(本頁呼叫的全部 endpoint)
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 列表 | POST /feedbacks |
回饋分頁列表(pager / sort / filters) | §6.2 |
| 明細 | GET /feedback/{uid} |
單筆回饋(含 issue 內容 / labels / files) | §6.2 |
| 建立 | POST /feedback |
新增(multipart:json blob + file[]) |
§6.3 |
| 更新 | PUT /feedback/{uid} |
更新(同 multipart) | §6.3 |
| 刪除 | DELETE /feedback/{uid} |
刪除(連動關 / 刪外部 issue,best-effort) | §6.3 |
| 附件 | DELETE /feedback/file/{uid}/{file_uid} |
刪單一附件 | GAI-SD-02 |
| 匯出 | POST /feedback/export/{export_type} |
csv / excel 匯出 | GAI-SD-02 |
| 下拉 | GET
/labels/menu/FEEDBACK_TYPE・/labels/menu/FUNCTION |
類別 / 功能 label 下拉 | GAI-SD-02 |
| 下載 | GET /file/download/{file_uid} |
附件下載(blob) | GAI-SD-02 |
6.2 讀取類
[POST] /feedbacks
Request(FeedbackPageQueryRequest,繼承
RequestMetaSchema):
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| pager | object | 否 | 分頁({page, page_size, with_total}) |
| sort | object | 否 | 排序 |
| filters | object | 否 | 篩選;schema 宣告
title/content/function/category/type/is_delete,但實際
server-side 只認 title / category——傳
content/function/type/is_delete
會落入 FeedbackIssueQueryEntity(**kwargs) 導致
TypeError(§12 坑) |
Response(FeedbackPageQueryResponse,data[]
為 FeedbackResponse,見
§6.3)。出處:api/feedback/routes/feedback_route.py:19-36、feedback_service.py:65-97
[GET] /feedback/{uid}
Response(FeedbackResponse):
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | string | 回饋識別 |
| issue_uid | string | → issues.uid |
| gitlab_issue_uid | string | 外部 GitLab 鏡像 issue |
| github_issue_uid | string | 外部 GitHub 鏡像 issue |
| title | string | 標題(由巢狀 .issue 物件取) |
| description | string | 內文(由巢狀 .issue 物件取) |
| labels | array | 標籤(由巢狀 .issue 物件取) |
| files | array | 附件(由巢狀 .issue 物件取) |
| created_user | string | 建立者 login_name |
| created_user_name | string | 建立者 nickname |
| updated_user | string | 修改者 login_name |
| updated_user_name | string | 修改者 nickname |
| created_at | date | 建立時間 |
| updated_at | date | 修改時間 |
出處:api/feedback/serializers/feedback.py:30-58
6.3 寫入類
[POST] /feedback ・ [PUT] /feedback/{uid}
multipart/form-data:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| json | object (Blob) | 是 | 含
{title, description, labels:[category_label_uid, function_label_uid]} |
| file | array | 否 | 附件(多檔) |
json blob 內含欄位:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| title | string | 是 | 標題 |
| description | string | 是 | 內文(BE 取為 content) |
| labels | array | 否 | [category_label_uid, function_label_uid] |
BE add_feedback / update_feedback 取
title / content(=description) /
labels / files;建 issues 記錄 +
issue_label_mapping +
issue_upload_file_mapping,並可選鏡像 GitLab / GitHub
issue(依 議題整合設定,失敗只 log
不擋)。Response:FeedbackResponse。出處:feedback_route.py:49-83、feedback_service.py:140-236
[DELETE] /feedback/{uid}
關閉 / 刪除本地 issue + 外部 GitLab·GitHub issue(best-effort)→ 刪
feedback_issues
link。出處:feedback_service.py:238-260
7. 前端檔案地圖(compliance-manager-fe/)
| 檔案 | 角色 |
|---|---|
src/views/feedback/FeedbackManageTabView.vue |
Tab 殼(系統 tab 生效 / 巡檢 tab 註解;path-driven
showTab) |
src/views/feedback/system/SystemFeedbackManage.vue |
系統回饋列表(server 分頁 useTableQuery / 匯出 /
canEdit) |
src/views/feedback/system/SystemFeedbackForm.vue |
新增 / 編輯(Editor + 多檔 advanced 上傳;type=0) |
src/views/feedback/system/SystemFeedbackView.vue |
唯讀檢視(labels 推導類別 / 功能 + 附件下載) |
src/views/feedback/cruising/* |
巡檢回饋(停用中殘留,見 §12) |
src/config/api/api.js |
端點常數(FEEDBACKS/FEEDBACK/FEEDBACK_FILE/FEEDBACK_EXPORT/LABELS_MENU/DOWNLOAD_FILE) |
src/config/locales/i18n/{zh-tw,en}/feedback.json |
i18n(namespace lang.feedback_manage.*) |
src/service/FeedbackService.js |
死碼(讀 demo JSON,未被引用) |
8. 後端檔案地圖(本頁核心鏈路)
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 列表 / 明細 | api/feedback/routes/feedback_route.py::FeedbacksRoute /
FeedbackRoute.get |
app/feedback/service/feedback_service.py::get_feedbacks_and_pager
/ get_feedback(enrich IssueService) |
FeedbackIssueDomainService →
feedback_issues repo + 外部 jedi-issue 的
issues |
| 建 / 改 / 刪 | 同檔 FeedbackRoute.post/put/delete |
add_feedback / update_feedback /
delete_feedback(GitLab / GitHub 鏡像 best-effort) |
IssueService(jedi-issue
LOCAL/GITLAB/GITHUB provider)+ label / file mapping |
| 附件 / 匯出 | FeedbackFileRoute /
FeedbackExportRoute |
delete_feedback_file /
export_feedbacks |
jedi-issue attachment + upload_files |
DDD 提醒:FeedbackService 每個 public
method 有 @transaction;外部 jedi-issue
provider 呼叫依賴 caller 的 transaction
scope。寫入端點無角色檢查(§3 缺口,新 code
不要比照)。
9. DB
9.1 資料表總清單(本頁讀寫的全部表)
| 表 | 讀/寫 | 說明 | 欄位詳述 |
|---|---|---|---|
| public.issues | 寫 | 回饋主體(jedi-issue 套件:title / description / state) | §9.3 |
| public.feedback_issues | 寫 | 回饋 ↔︎ 外部 issue link(gitlab / github uuid、tenant / org) | §9.3 |
| public.labels | 讀 | 標籤(scope=FEEDBACK_TYPE / FUNCTION 即類別 /
功能) |
GAI-SD-03 |
| public.issue_label_mapping | 寫 | 回饋 ↔︎ label 多對多 | GAI-SD-03 |
| public.issue_upload_file_mapping | 寫 | 回饋 ↔︎ 附件多對多 | GAI-SD-03 |
| public.upload_files | 寫 | 附件本體 | GAI-SD-03 |
| public.issue_assignee_mapping | (未用) | 回饋 ↔︎ 負責人(目前 assignees 恆傳 []) | GAI-SD-03 |
9.3 核心表欄位(取自 DEV DB dump,含 DB comment)
public.issues — 回饋表(jedi-issue)
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | varchar(50) | 回饋 UUID(= FE 的 feedback uid 對應 issue_uid) |
| project | varchar(255) | 所屬專案 |
| title / description | varchar(255) / text | 標題 / 內文 |
| state | varchar(50) | 回饋狀態(FE 未 surface;外部 issue 生命週期驅動) |
public.feedback_issues — 回饋 ↔︎ Issue 關聯
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | varchar(50) | link 識別 |
| issue_uid | varchar(50) | → issues.uid |
| gitlab_issue_uid / github_issue_uid | varchar(50)|null | 外部鏡像 issue(議題整合啟用時) |
| tenant_id / org_unit_id | int|null | 租戶 / 部門(FK RESTRICT) |
類別 / 功能 =
labels(scopeFEEDBACK_TYPE / FUNCTION,純字串無 Python enum)經issue_label_mapping掛。附件經issue_upload_file_mapping→upload_files。
10. 頁面邏輯與資料對應
載入時序:進頁 →
POST /feedbacks(useTableQuery 組
pager/sort/filters)→ 逐筆 enrich .issue → 依
canEdit 顯示動作。表單載入時另拉類別 / 功能 label
下拉。
關鍵欄位對應(畫面 ↔︎ API):
| 畫面元素 | FE state | API 欄位 |
|---|---|---|
| 類別 / 功能 | currData.category / .function |
labels(scope FEEDBACK_TYPE / FUNCTION)name |
| 內文 | currData.description(系統)/
.content(巡檢) |
issue.description |
| 附件 | currData.files |
issue.upload_files(issue_upload_file_mapping) |
| 建立者 | item.created_user |
created_user(enrich _name nickname) |
儲存流程:multipart(json blob + files)一次送;BE 建 issue + mapping + 選擇性鏡像外部 issue。刪除連動關外部 issue。
錯誤對應:BE envelope →
toast;FEEDBACK_ISSUE_404001(回饋不存在)、FEEDBACK_400001(不支援匯出型別)。
11. 背景行為與外部依賴
| 類型 | 內容 |
|---|---|
| GitLab / GitHub 鏡像 | 建 / 改 / 刪回饋時依「議題整合設定」best-effort 鏡像到外部
issue(失敗只 log 不擋);provider LOCAL / GITLAB /
GITHUB(common/code/feedback_code.py) |
| Socket | 本頁無 socket / 輪詢 |
| jedi-* 套件 | jedi-issue(issue 主體 / 附件 / 外部
provider)、jedi-auth(JWT) |
| 系統參數 | 類別 / 功能來自
labels(scope);議題整合開關來自系統設定 |
12. 邊界情況與已知坑
- 巡檢回饋(cruising)停用 + 會炸:FE 巡檢 tab
已註解(
FeedbackManageTabView.vue:71-76);獨立巡檢管理頁送{type:1},但 BEFeedbackIssueQueryEntity無type欄 →FeedbackIssueQueryEntity(**kwargs)會TypeError。後端無巡檢專屬模組 / 表 / discriminator。要復用巡檢前,需 BE 補 type 欄位 / 篩選、FE 修下拉 hardcode。目前只有系統回饋端到端可用 - 寫入端點無 BE
角色守門:
PUT/DELETE /feedback/{uid}僅 JWT,非 admin 可改 / 刪他人回饋;canEdit只是 FE 藏鈕(同公告頁缺口) - filters 只認 title / category:schema 宣告多欄,但
server-side
FeedbackIssueQueryEntity只有title/category——傳其他 filter key 會TypeError issues.state未 surface:有狀態欄但 FE 無工作流 UI(無 status / 回覆 thread)- 系統 vs 巡檢 discriminator 是 FE-only:FE 送
type=0/1,但 BEadd_feedback不消費 type(signature 無此參數);系統 / 巡檢共用同組端點與表,差別只在 FE - 多處死碼:
FeedbackService.js(demo JSON)、verify_feedback_issue_is_exist(無 call site)、FeedbackRequest/FeedbackFileRequestschema(route 手解request.files['json']未用)、ALLOWED_EXTENSIONS常數(定義未強制) - 巡檢表單下拉
hardcode:
CruisingFeedbackForm.vue類別 / 功能是寫死 stub(真 API 呼叫定義了但沒接 onMounted);巡檢刪附件 bug(清空全部 files 而非單一) - error code 前綴 FEEDBACK_ 不是 GRC_:在
common/code/error_code.py(共用檔)
13. 開發與驗證
- 跑起來:BE
python main_socketio.py(port 8000,log 在log/app.log);FE 在compliance-manager-fe/起 Vite dev server。BE 改 service code 後必須重啟(無 hot reload) - 測試帳號:dev 環境一般使用者(提交)+ admin(管理 /
匯出);密碼見
.env/ 部署文件 - 導航路徑:登入 → 側邊「回饋」→
/feedback/system-feedback-manage - 前置資料:
labels有 FEEDBACK_TYPE / FUNCTION scope 標籤;議題整合設定(可選)啟用才鏡像外部 issue - E2E:
compliance-manager-test/repo;以回饋/feedback搜尋 - 相關文件:公告見 公告管理;議題整合設定(GitLab/GitHub)另有系統管理頁 spec;DB / API 全量見 GAI-SD-02/03