永輝資料中央廚房資料表-查帳系統

永輝資料中央廚房資料表-查帳系統

Data Central Kitchen(資料中央廚房)概念說明

「Data Central Kitchen」是一個企業級 AI 數據中台 + 文件自動化處理中心的架構概念。
就像餐飲業的中央廚房負責統一備料、加工、分送到各分店,Data Central Kitchen 則負責:
接收各種來源文件 → AI辨識/清洗/轉換 → 數據標準化 → 自動路由至 ERP/CRM/稅務/薪資系統
這種架構非常適合像永輝這種 跨國、多ERP、多法規、多語言的會計師事務所集團。

Table Design 命名原則

ndividual Table 的命名不要用”_” underline,每個Table 名字只 用一個英文或2個英文字連在一起,只有Junction Table 才用”_” underline
✅ Individual Table:不用 _
✅ 只用 1 個英文或 2 個英文連在一起
✅ Junction Table:才使用 _

1. Standard 標準版

可規模化:可反覆跑、可跨案複用、可自動檢錯
目標: 讓 Data Central Kitchen Hub 不只是上傳入口,而是「資料取得管線」:定義格式、欄位 mapping、驗證明細、DRP/ETL 任務、例外處理、狀態歷程都可追溯。

只列 9 個核心(Engagement / Entity / User / Document 假設已有)。


1.1 Request

欄位型態
id (PK)uuid
engagementIduuid FK → Engagement
entityIduuid FK → Entity
requestCodetext UNIQUE (per engagement)
datasetTypetext
periodStartdate
periodEnddate
instructiontext
requiredFieldsjsonb
preferredFormattext
deliveryMethodtext
prioritysmallint
statustext
dueAttimestamptz
ownerUserIduuid FK → User
createdAttimestamptz
createdByuuid
updatedAttimestamptz
updatedByuuid

1.2 RequestItem

欄位型態
id (PK)uuid
requestIduuid FK → Request
itemCodetext
itemNametext
datasetSubtypetext
schemaVersiontext
requiredFieldsjsonb
optionalFieldsjsonb
sampleDocumentIduuid FK → Document
expectedRowCountint
statustext
dueAttimestamptz
notestext
createdAttimestamptz
createdByuuid
updatedAttimestamptz
updatedByuuid

1.3 Submission

欄位型態
id (PK)uuid
requestIduuid FK → Request
submissionNoint
submittedByUserIduuid FK → User
submittedByTypetext
submittedAttimestamptz
channeltext
sourceSystemtext
statustext
notestext
acceptedAttimestamptz
acceptedByUserIduuid
rejectionReasontext
createdAttimestamptz
updatedAttimestamptz

UNIQUE(requestId, submissionNo)


1.4 Mapping

欄位型態
id (PK)uuid
engagementIduuid
entityIduuid
datasetTypetext
mappingVersiontext
isActiveboolean
effectiveFromdate
effectiveTodate
approvedByUserIduuid
approvedAttimestamptz
createdAttimestamptz
createdByuuid
updatedAttimestamptz
updatedByuuid

1.5 ValidationResult

欄位型態
id (PK)uuid
submissionItemIduuid FK → Submission_Item
validationProfiletext
passedboolean
severityMaxtext
issueCountTotalint
issueCountErrorint
issueCountWarnint
qualityScoresmallint
summaryjsonb
validatedAttimestamptz
validatedByTypetext
validatedByUserIduuid
createdAttimestamptz

1.6 ValidationIssue

欄位型態
id (PK)uuid
validationResultIduuid FK → ValidationResult
issueTypetext
severitytext
fieldNametext
rowReftext
expectedValuetext
actualValuetext
messagetext
fixSuggestiontext
statustext
handledByUserIduuid
handledAttimestamptz
resolutionNotetext
createdAttimestamptz

1.7 Batch

欄位型態
id (PK)uuid
engagementIduuid
submissionItemIduuid FK → Submission_Item
datasetTypetext
importModetext
isLatestboolean
importedAttimestamptz
importedByUserIduuid
rowInsertedint
rowUpdatedint
rowRejectedint
notestext
createdAttimestamptz

1.8 JobRun

欄位型態
id (PK)uuid
engagementIduuid
jobCodetext
submissionItemIduuid
batchIduuid
triggeredByUserIduuid
triggeredAttimestamptz
statustext
parametersjsonb
startedAttimestamptz
finishedAttimestamptz
runtimeSecondsint
errorCodetext
errorMessagetext
retryCountint
createdAttimestamptz

1.9 Submission_Item(Junction Table)

欄位
id (PK)
submissionId FK → Submission
requestItemId FK → RequestItem
status
validatedAt
acceptedAt
rowCount
currencyCode
checksum
notes
createdAt
updatedAt

UNIQUE(submissionId, requestItemId)


1.10 Submission_Document(Junction Table)

| submissionId |
| requestItemId |
| documentId |
| fileRole |
| linkedAt |
| linkedByUserId |
| versionNote |


1.11 JobRun_Document(Junction Table)

| jobRunId |
| documentId |
| docRole |
| linkedAt |
| linkedByUserId |
| notes |


1.12 Mapping_Line(Junction Table)

| id |
| mappingId |
| sourceField |
| targetField |
| transformRule |
| requiredFlag |
| defaultValue |
| validationRule |
| sortOrder |
| createdAt |
| createdBy |



2.Enterprise 版本:

新增 tables 清單(+10 → 合計 22)
新增 Individual tables(不含 _

  1. Tenant(併購後多事務所共用 / 多租戶隔離)
  2. ExternalUser(客戶/外部上傳者帳號)
  3. ExternalRole(外部角色)
  4. ExternalPerm(外部權限粒度)
  5. AccessPolicy(可見性/授權規則,支援 Row-level)
  6. AuditEvent(不可竄改稽核事件)
  7. RetentionPolicy(保存/刪除策略:國別/客戶等級)
  8. KeyRef(客戶級加密/金鑰參照)
  9. KpiSnapshot(KPI 快照:週期、補件率、失敗率等)
  10. NotifyOutbox(通知 Outbox:Email/SMS/Webhook)

新增 Junction tables(允許 _

  • ExternalUser_Role(ExternalUser ↔ ExternalRole)
  • ExternalRole_Perm(ExternalRole ↔ ExternalPerm)

你原本列的 external_user_permission 若一定要有,也可以改成 junction:ExternalUser_Perm(同規則用 _),但我建議用「Role 為主」,User 例外授權再加這張即可(可選)。



2.1 Tenant(多租戶 / 併購後共用的根)

PK: id

欄位型態必填說明
iduuidYPK
tenantCodetextY例 EVTW / EVJP / PartnerMX
tenantNametextY顯示名稱
statustextYactive/suspended/closed
regiontextNAPAC/EU/US…
defaultTztextY例 Asia/Taipei
createdAttimestamptzY
createdByuuidY

Unique: (tenantCode)
Index: (status)

設計建議(很重要):Standard 的表全部加 tenantId,並做 (tenantId, id) 的查詢索引;外部登入也要綁 tenant。


2.2 ExternalUser(外部使用者)

PK: id
FK: tenantId → Tenant(id)

欄位型態必填說明
iduuidYPK
tenantIduuidY多租戶
emailtextY登入帳號(建議全小寫)
displayNametextY
statustextYinvited/active/locked/disabled
mfaEnabledboolY
lastLoginAttimestamptzN
failedLoginCountintY
lockedUntiltimestamptzN
passwordHashtextN若用 SSO 可為 null
ssoProvidertextNoidc/saml
ssoSubjecttextNprovider subject
createdAttimestamptzY
createdByuuidYinternal User

Unique: (tenantId, email)
Index: (tenantId, status), (tenantId, lastLoginAt desc)


2.3 ExternalRole(外部角色)

PK: id
FK: tenantId → Tenant(id)

欄位型態必填
iduuidY
tenantIduuidY
roleCodetextY
roleNametextY
isSystemboolY
createdAttimestamptzY
createdByuuidY

Unique: (tenantId, roleCode)


2.4 ExternalPerm(外部權限)

PK: id
FK: tenantId → Tenant(id)

欄位型態必填說明
iduuidY
tenantIduuidY
permCodetextYREQUEST_VIEW / FILE_UPLOAD / FILE_DOWNLOAD / STATUS_CHANGE…
permNametextY
categorytextYrequest/file/job/audit/admin
createdAttimestamptzY

Unique: (tenantId, permCode)


2.5 ExternalUser_Role(Junction Table:外部使用者 ↔ 外部角色)

PK(建議複合): (externalUserId, externalRoleId)
FK: externalUserId → ExternalUser(id), externalRoleId → ExternalRole(id)

欄位型態必填
externalUserIduuidY
externalRoleIduuidY
assignedAttimestamptzY
assignedByuuidY

Index: (externalUserId), (externalRoleId)


2.6 ExternalRole_Perm(Junction Table:角色 ↔ 權限)

PK(建議複合): (externalRoleId, externalPermId)
FK: externalRoleId → ExternalRole(id), externalPermId → ExternalPerm(id)

欄位型態必填
externalRoleIduuidY
externalPermIduuidY
grantedAttimestamptzY
grantedByuuidY

Index: (externalRoleId), (externalPermId)


2.7 AccessPolicy(可見性規則 / Row-level 授權)

這張表是 Enterprise 的核心:外部使用者只能看到哪些 Request / Submission / Document
我建議支援「角色授權」與「個人授權」兩種。

PK: id
FK: tenantId → Tenant(id)
(可選 FK)externalUserId → ExternalUser(id) / externalRoleId → ExternalRole(id)

欄位型態必填說明
iduuidY
tenantIduuidY
subjectTypetextYuser/role
externalUserIduuidNsubjectType=user 才填
externalRoleIduuidNsubjectType=role 才填
objectTypetextYRequest/RequestItem/Submission/Document
objectIduuidY目標資料列 id
accessLeveltextYview/upload/download/manage
conditionjsonbN進階條件(例:只能看某期間/某 entity)
expiresAttimestamptzN臨時授權
createdAttimestamptzY
createdByuuidYinternal User
revokedAttimestamptzN
revokedByuuidN

Index(非常重要)

  • (tenantId, subjectType, externalUserId)
  • (tenantId, subjectType, externalRoleId)
  • (tenantId, objectType, objectId)

你要做 Row-Level Security (RLS) 或應用層權限,都會靠它跑得快。


2.8 AuditEvent(不可竄改稽核事件)

這張不能只是一般 log,要做到「不可竄改可驗證」:
建議做 hash chain(prevHash + eventHash),或用 WORM/Append-only。

PK: id
FK: tenantId → Tenant(id)

欄位型態必填說明
iduuidY
tenantIduuidY
actorTypetextYinternal/external/system
actorUserIduuidNinternal User
actorExtIduuidNExternalUser
actiontextYview/download/upload/delete/status_change/login_failed…
objectTypetextYRequest/Submission/Document…
objectIduuidY
ipAddrinetN
userAgenttextN
requestIdtextNtrace id
payloadjsonbN差異內容(不要塞敏感原文)
prevHashtextNhash chain
eventHashtextYhash(payload+prevHash+…)
createdAttimestamptzY

Index: (tenantId, createdAt desc), (tenantId, actorType), (tenantId, objectType, objectId)


2.9 RetentionPolicy(保存/刪除策略)

PK: id
FK: tenantId → Tenant(id)

欄位型態必填
iduuidY
tenantIduuidY
policyCodetextY
countrytextN
clientTiertextN
datasetTypetextN
keepDaysintY
deleteModetextY
legalHoldboolY
encryptRequiredboolY
effectiveFromdateY
effectiveTodateN
createdAttimestamptzY

Unique: (tenantId, policyCode)
Index: (tenantId, country, clientTier), (tenantId, datasetType)


2.10 KeyRef(加密金鑰參照)

若你要做到「客戶級加密(每個客戶/租戶不同 key)」或「依國別/等級用不同 key」,這張表讓資料層能追溯與輪替。

PK: id
FK: tenantId → Tenant(id)

欄位型態必填
iduuidY
tenantIduuidY
keyScopetextY
scopeIduuidN
kmsProvidertextY
keyAliastextY
keyVersiontextN
statustextY
rotatedAttimestamptzN
createdAttimestamptzY

Index: (tenantId, keyScope, status)


2.11 KpiSnapshot(KPI 快照)

PK: id
FK: tenantId → Tenant(id), engagementId → Engagement(id)

欄位型態必填
iduuidY
tenantIduuidY
engagementIduuidN
periodTypetextY
periodStartdateY
periodEnddateY
requestCountintY
avgCycleHoursintY
resubmitRatenumeric(6,3)Y
validateFailRatenumeric(6,3)Y
hoursSavednumeric(12,2)N
snapshotjsonbY
createdAttimestamptzY

Unique: (tenantId, engagementId, periodType, periodStart, periodEnd)


2.12 NotifyOutbox(通知 Outbox)

Outbox pattern:資料庫先落一筆待送通知,由 worker 發送,成功再標記 sent,失敗可重試,並可稽核。

PK: id
FK: tenantId → Tenant(id)

欄位型態必填
iduuidY
tenantIduuidY
channeltextY
templateCodetextY
subjecttextN
payloadjsonbY
recipientTypetextY
recipientUserIduuidN
recipientExtIduuidN
relatedTypetextN
relatedIduuidN
statustextY
attemptCountintY
nextAttemptAttimestamptzN
lastErrortextN
createdAttimestamptzY
sentAttimestamptzN

Index: (tenantId, status, nextAttemptAt), (tenantId, createdAt desc)


2.13 Enterprise 關鍵「內控/資安」設計點

A. 多租戶隔離(併購後共用的核心)

  • Tenant 必須是第一級隔離鍵
  • 幾乎每張表都應該有 tenantId(包含 Document / Request / Submission / AuditEvent / AccessPolicy)
  • 索引優先用 (tenantId, status)(tenantId, createdAt),避免跨租戶掃描

B. 外部 RBAC + Row-level Access

  • RBAC 只解決「能不能做某類事」
  • AccessPolicy 解決「能看哪一筆資料」(例如 client A 只能看自己的 Request/Document)
  • 建議 subjectType=role 為主,例外才 subjectType=user(可控也好維運)

C. 稽核不可竄改(AuditEvent)

  • 不能只是一般 log:要做到「事後可驗證」
  • hash chain(prevHash + eventHash) 是成本低、效果強的做法
  • 下載/檢視/狀態變更/刪除(含 soft delete)一定要打點

D. 保存/刪除與法律保全(RetentionPolicy)

  • 你跨國後必會遇到「不同國別、不同客戶等級、不同保存年限」
  • RetentionPolicy + legalHold 能避免「被迫刪」「不該刪」的內控事故

E. 客戶級加密(KeyRef)

  • 大客戶常要求「每客戶獨立 key / key rotation」
  • KeyRef 讓你可以把 Document 的加密策略(keyScope/scopeId)關聯起來,稽核也交代得清楚
Top