文档: 完成 architecture.md P5 现状更新,修正可观测性栈定位
这个提交包含在:
+125
-460
@@ -1,23 +1,14 @@
|
|||||||
# hyb-oa 系统架构概览
|
# hyb-oa 系统架构概览
|
||||||
|
|
||||||
**版本**:1.1
|
**版本**:1.2
|
||||||
**日期**:2026-02-09
|
**日期**:2026-02-09
|
||||||
**目的**:帮助新成员快速理解系统"怎么做"的全貌(并提供演进方向)。
|
**目的**:描述系统当前实现(P5/F25 完备态)并为未来演进提供参考。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 阅读指南:As‑Is vs To‑Be
|
## 1. 部署视图 (As-Is)
|
||||||
|
|
||||||
- **As‑Is(现状)**:以当前仓库代码、试运行部署包为准,用于回归、排障、交接。
|
> 现状特点:**单机/集群兼容、进程级别拆分、支持云原生存储与监控。**
|
||||||
- **To‑Be(前瞻)**:面向 P5+ 的演进目标,用于指导架构升级;所有未来项都会明确标注 **[To‑Be/未实现]**,避免误解为已交付。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 系统架构(部署视图)
|
|
||||||
|
|
||||||
### 1.1 部署视图(As‑Is / 单机试运行)
|
|
||||||
|
|
||||||
> 现状特点:**单机可运行、同域可部署、作业同进程**(node-cron),外部依赖最小。
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
@@ -25,495 +16,169 @@ graph TB
|
|||||||
Browser["浏览器 (React SPA)"]
|
Browser["浏览器 (React SPA)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Node["单机(dev/trialrun)"]
|
subgraph Cluster["部署环境 (Docker Compose Profiles)"]
|
||||||
Proxy["可选:Nginx/Caddy\nTLS/反代/限流/上传体积控制"]
|
Proxy["可选:Nginx/Caddy\nTLS/反代/前端托管"]
|
||||||
App["Fastify Server\n(API + 可选静态托管 client/dist)"]
|
|
||||||
Jobs["同进程 Jobs (node-cron)\nOCR Job / 委派回收 Job"]
|
subgraph Server_Nodes["计算层"]
|
||||||
PG["PostgreSQL (15+/16)"]
|
API["Fastify API\n(JOBS_ENABLED=false)"]
|
||||||
Storage["本地存储 (票据文件)"]
|
Worker["Worker 进程\n(npm run worker)"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Infra_Services["基础设施"]
|
||||||
|
PG["PostgreSQL (16) + pg-boss"]
|
||||||
|
OSS["MinIO (S3 兼容) / Local FS"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph External["外部服务(可选)"]
|
subgraph External["外部服务 / 可选集"]
|
||||||
TencentOCR["腾讯云 OCR"]
|
TencentOCR["腾讯云 OCR"]
|
||||||
|
OIDC["OIDC Provider (Okta/Authing等)"]
|
||||||
|
Prom["可选:Prometheus (抓取 /metrics)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
Browser -.->|可选 HTTPS| Proxy
|
Browser -->|HTTPS/HTTP| Proxy
|
||||||
Browser -->|dev 可直连| App
|
Proxy -->|/api/*| API
|
||||||
Proxy -->|/api/*| App
|
Proxy -->|/*| API
|
||||||
Proxy -->|/*| App
|
|
||||||
|
|
||||||
App -->|Prisma| PG
|
API -->|Prisma| PG
|
||||||
Jobs -->|Prisma| PG
|
Worker -->|pg-boss| PG
|
||||||
App -->|文件读写| Storage
|
API -->|Storage Service| OSS
|
||||||
Jobs -->|文件读写| Storage
|
Worker -->|Storage Service| OSS
|
||||||
Jobs -->|API 调用| TencentOCR
|
Worker -->|API 调用| TencentOCR
|
||||||
|
API -->|OIDC Auth| OIDC
|
||||||
|
Prom -.->|Scrape| API
|
||||||
```
|
```
|
||||||
|
|
||||||
**说明**
|
**关键变更说明**:
|
||||||
- `PostgreSQL` 版本口径:开发 compose 可为 15,试运行包建议 16(两者均可)。
|
- **Worker 进程独立**:支持通过 `docker compose --profile worker` 拆分 API 与定时作业进程。
|
||||||
- `Proxy`(Nginx/Caddy)是可选项:试运行/上线建议配置(TLS、上传限制、限流),开发联调可跳过。
|
- **存储抽象层**:适配本地文件系统与 S3/MinIO,通过 `STORAGE_DRIVER` 切换。
|
||||||
- `App` 可通过 `SERVE_CLIENT_DIST=true` 托管 `client/dist`,实现同域 cookie 与 SPA fallback。
|
- **治理接入**:内置 Prometheus 指标采集 `/metrics`。
|
||||||
- `Jobs` 现状与 `App` 同进程(node-cron);未来可拆分独立 Worker(见 To‑Be)。
|
|
||||||
|
|
||||||
### 1.2 部署视图(To‑Be / 可扩展但仍可单机)
|
|
||||||
|
|
||||||
> 目标:在不改变“单机可跑”前提下,逐步具备“可横向扩展、可观测、可运维”的形态。
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph TB
|
|
||||||
subgraph Client["客户端"]
|
|
||||||
Browser["浏览器 (React SPA)"]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Edge["边缘层 [To‑Be/未实现]"]
|
|
||||||
Proxy["Nginx/Caddy/WAF\nTLS/反代/限流/上传策略"]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph App["应用层"]
|
|
||||||
API["Fastify API (stateless)\n可选静态托管 client/dist"]
|
|
||||||
Worker["Job Worker [To‑Be/未实现]\nOCR / 委派回收 / 清理作业"]
|
|
||||||
Queue["队列/Redis [To‑Be/未实现]\nBullMQ/Streams(可选)"]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Data["数据层"]
|
|
||||||
PG["PostgreSQL\nPgBouncer [To‑Be/未实现]"]
|
|
||||||
ObjectStorage["对象存储 [To‑Be/未实现]\nS3/OSS/MinIO"]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Obs["可观测性 [To‑Be/未实现]"]
|
|
||||||
Logs["结构化日志聚合\nLoki/ELK"]
|
|
||||||
Metrics["metrics\nPrometheus"]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph External["外部服务"]
|
|
||||||
TencentOCR["腾讯云 OCR"]
|
|
||||||
end
|
|
||||||
|
|
||||||
Browser --> Proxy
|
|
||||||
Proxy --> API
|
|
||||||
|
|
||||||
API --> PG
|
|
||||||
Worker --> PG
|
|
||||||
|
|
||||||
API --> ObjectStorage
|
|
||||||
Worker --> ObjectStorage
|
|
||||||
|
|
||||||
Worker <--> Queue
|
|
||||||
Worker --> TencentOCR
|
|
||||||
|
|
||||||
API --> Logs
|
|
||||||
Worker --> Logs
|
|
||||||
API --> Metrics
|
|
||||||
Worker --> Metrics
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 核心业务流程(报销审批 / As‑Is)
|
## 2. 核心业务流程 (As-Is / 异步作业)
|
||||||
|
|
||||||
> 核心链路:**票据上传 →(异步)OCR → 绑定票据 → 提交报销 → 工作流审批 → 财务付款 → 票据归档**。
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant E as 员工
|
participant E as 员工/行政
|
||||||
participant S as Server(Fastify)
|
participant API as API Server
|
||||||
participant J as OCR Job(node-cron)
|
participant Q as pg-boss (Postgres)
|
||||||
participant O as 腾讯云OCR
|
participant W as Worker
|
||||||
participant WF as 工作流引擎
|
participant O as 腾讯云/Mock OCR
|
||||||
participant M as 经理
|
participant WF as Workflow Engine
|
||||||
participant F as 财务
|
|
||||||
|
|
||||||
E->>S: 上传票据 (invoice_pool: pending_ocr/available)
|
E->>API: 上传票据 (invoice_pool: pending_ocr)
|
||||||
S-->>E: 返回 invoiceId + status
|
API->>Q: 发布 ocr.tick 任务
|
||||||
|
API-->>E: 返回票据 ID (状态: pending_ocr)
|
||||||
|
|
||||||
J->>S: 认领 pending_ocr (FOR UPDATE SKIP LOCKED + 原子回写锁)
|
W->>Q: 获取任务 (Fetch Job)
|
||||||
J->>O: OCR 识别(可 mock/off)
|
W->>W: 获取票据缓存或文件
|
||||||
O-->>J: 识别结果/错误
|
W->>O: 调用 OCR
|
||||||
J->>S: 更新 invoice_pool (available/ocr_failed)
|
O-->>W: 结果
|
||||||
|
W->>API: 更新数据库 (available / ocr_failed)
|
||||||
|
|
||||||
E->>S: GET /api/invoice-pool/:id (轮询/刷新)
|
E->>API: 提交报销 (submit)
|
||||||
S-->>E: 返回最新 status 与字段
|
API->>WF: 初始化实例 (0..n 关系)
|
||||||
|
WF-->>API: 创建 Task
|
||||||
E->>S: 绑定票据到报销单 (available -> bound)
|
API->>API: 触发通知 (邮件/站内)
|
||||||
E->>S: 提交报销 (draft -> reviewing)
|
|
||||||
S->>WF: 创建 workflow_instance + 首个 task
|
|
||||||
WF-->>M: 待办 task(assignee_id 或 assignee_role)
|
|
||||||
|
|
||||||
M->>S: approve / return / reject / transfer
|
|
||||||
WF->>WF: 推进流程并写 workflow_history
|
|
||||||
|
|
||||||
F->>S: pay (approved -> paid)
|
|
||||||
S->>S: bound 票据 -> archived (used_complete)
|
|
||||||
S->>S: 写 audit_logs(关键运营/安全动作)
|
|
||||||
```
|
|
||||||
|
|
||||||
**证据链边界(现状)**
|
|
||||||
- `workflow_history`:流程证据(针对某个 workflow_instance/task 的动作轨迹),用于还原审批过程。
|
|
||||||
- `audit_logs`:合规/安全证据(登录、委派回收、用户管理等跨域操作),用于审计与风控追踪。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 数据模型(ER 图 / As‑Is)
|
|
||||||
|
|
||||||
> 说明:为可读性仅列关键字段与关系;具体字段以 Prisma schema 为准。
|
|
||||||
|
|
||||||
### 3.1 组织 / 权限 / 会话 / 审计
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
Department ||--o{ Employee : contains
|
|
||||||
Department ||--o{ Department : children
|
|
||||||
|
|
||||||
Employee ||--o{ EmployeeRole : has
|
|
||||||
Employee ||--o{ AuthSession : sessions
|
|
||||||
Employee ||--o{ PasswordResetCode : resets
|
|
||||||
Employee ||--o{ AuditLog : writes
|
|
||||||
Employee ||--o{ DelegationGrant : principal
|
|
||||||
Employee ||--o{ DelegationGrant : delegate
|
|
||||||
|
|
||||||
Department {
|
|
||||||
uuid id PK
|
|
||||||
string name
|
|
||||||
uuid parent_id FK
|
|
||||||
}
|
|
||||||
|
|
||||||
Employee {
|
|
||||||
uuid id PK
|
|
||||||
string employee_no UK
|
|
||||||
uuid department_id FK
|
|
||||||
uuid direct_manager_id FK
|
|
||||||
string status
|
|
||||||
int token_version
|
|
||||||
}
|
|
||||||
|
|
||||||
EmployeeRole {
|
|
||||||
uuid employee_id PK,FK
|
|
||||||
string role_code PK
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthSession {
|
|
||||||
uuid id PK
|
|
||||||
uuid employee_id FK
|
|
||||||
string device_fingerprint
|
|
||||||
datetime expires_at
|
|
||||||
datetime revoked_at
|
|
||||||
}
|
|
||||||
|
|
||||||
PasswordResetCode {
|
|
||||||
uuid id PK
|
|
||||||
uuid employee_id FK
|
|
||||||
datetime expires_at
|
|
||||||
datetime used_at
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegationGrant {
|
|
||||||
uuid id PK
|
|
||||||
uuid principal_employee_id FK
|
|
||||||
uuid delegate_employee_id FK
|
|
||||||
datetime valid_from
|
|
||||||
datetime valid_to
|
|
||||||
datetime revoked_at
|
|
||||||
}
|
|
||||||
|
|
||||||
AuditLog {
|
|
||||||
uuid id PK
|
|
||||||
string request_id
|
|
||||||
uuid actor_id FK
|
|
||||||
string action
|
|
||||||
string target_type
|
|
||||||
uuid target_id
|
|
||||||
datetime created_at
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 报销与票据(含 OCR 缓存)
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
Employee ||--o{ ReimbursementForm : owns
|
|
||||||
Employee ||--o{ ReimbursementForm : submits
|
|
||||||
Department ||--o{ ReimbursementForm : belongs
|
|
||||||
|
|
||||||
ReimbursementForm ||--o{ ReimbursementItem : contains
|
|
||||||
ExpenseType ||--o{ ReimbursementItem : categorizes
|
|
||||||
|
|
||||||
Employee ||--o{ InvoicePool : owns
|
|
||||||
Employee ||--o{ InvoicePool : uploads
|
|
||||||
ReimbursementForm ||--o{ InvoicePool : links
|
|
||||||
ReimbursementItem ||--o{ InvoicePool : linksItem
|
|
||||||
OcrResult ||--o{ InvoicePool : cachedFor
|
|
||||||
|
|
||||||
ReimbursementForm {
|
|
||||||
uuid id PK
|
|
||||||
string form_number UK
|
|
||||||
uuid owner_id FK
|
|
||||||
uuid submitter_id FK
|
|
||||||
uuid department_id FK
|
|
||||||
string status
|
|
||||||
decimal total_amount
|
|
||||||
string payment_voucher_no
|
|
||||||
}
|
|
||||||
|
|
||||||
ReimbursementItem {
|
|
||||||
uuid id PK
|
|
||||||
uuid reimbursement_id FK
|
|
||||||
uuid expense_type_id FK
|
|
||||||
decimal amount
|
|
||||||
string description
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpenseType {
|
|
||||||
uuid id PK
|
|
||||||
string code UK
|
|
||||||
string name
|
|
||||||
string category
|
|
||||||
}
|
|
||||||
|
|
||||||
InvoicePool {
|
|
||||||
uuid id PK
|
|
||||||
uuid owner_employee_id FK
|
|
||||||
uuid uploaded_by_employee_id FK
|
|
||||||
string storage_path
|
|
||||||
string file_sha256
|
|
||||||
string status
|
|
||||||
uuid linked_reimbursement_id FK
|
|
||||||
uuid linked_item_id FK
|
|
||||||
uuid ocr_result_id FK
|
|
||||||
}
|
|
||||||
|
|
||||||
OcrResult {
|
|
||||||
uuid id PK
|
|
||||||
string file_sha256
|
|
||||||
string ocr_provider
|
|
||||||
string ocr_type
|
|
||||||
json result_json
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**关系备注(关键纠偏)**
|
|
||||||
- `InvoicePool.linked_reimbursement_id / linked_item_id / ocr_result_id` 均为 **可空字段**(票据可长期处于 available/pending_ocr 未绑定状态)。
|
|
||||||
|
|
||||||
### 3.3 工作流引擎(含转审证据)
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
WorkflowDefinition ||--o{ WorkflowInstance : creates
|
|
||||||
ReimbursementForm ||--o{ WorkflowInstance : instances
|
|
||||||
WorkflowInstance ||--o{ WorkflowTask : tasks
|
|
||||||
WorkflowInstance ||--o{ WorkflowHistory : history
|
|
||||||
WorkflowTask ||--o{ WorkflowHistory : taskHistory
|
|
||||||
|
|
||||||
Employee ||--o{ WorkflowTask : assigned
|
|
||||||
Employee ||--o{ WorkflowTask : completed
|
|
||||||
Employee ||--o{ WorkflowHistory : actions
|
|
||||||
|
|
||||||
ReimbursementForm ||--o{ ApprovalTransfer : transfers
|
|
||||||
Employee ||--o{ ApprovalTransfer : from
|
|
||||||
Employee ||--o{ ApprovalTransfer : to
|
|
||||||
|
|
||||||
WorkflowDefinition {
|
|
||||||
uuid id PK
|
|
||||||
string entity_type
|
|
||||||
int version
|
|
||||||
bool is_active
|
|
||||||
json definition_json
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkflowInstance {
|
|
||||||
uuid id PK
|
|
||||||
uuid workflow_id FK
|
|
||||||
string entity_type
|
|
||||||
uuid entity_id FK
|
|
||||||
uuid submitter_id FK
|
|
||||||
string current_status
|
|
||||||
string current_step_id
|
|
||||||
json context_data
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkflowTask {
|
|
||||||
uuid id PK
|
|
||||||
uuid instance_id FK
|
|
||||||
string step_id
|
|
||||||
string step_name
|
|
||||||
uuid assignee_id FK
|
|
||||||
string assignee_role
|
|
||||||
string status
|
|
||||||
string outcome
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkflowHistory {
|
|
||||||
uuid id PK
|
|
||||||
uuid instance_id FK
|
|
||||||
uuid task_id FK
|
|
||||||
uuid actor_id FK
|
|
||||||
string action
|
|
||||||
json payload
|
|
||||||
}
|
|
||||||
|
|
||||||
ApprovalTransfer {
|
|
||||||
uuid id PK
|
|
||||||
uuid reimbursement_id FK
|
|
||||||
uuid from_approver_id FK
|
|
||||||
uuid to_approver_id FK
|
|
||||||
string reason
|
|
||||||
string status
|
|
||||||
}
|
|
||||||
|
|
||||||
ApprovalRule {
|
|
||||||
uuid id PK
|
|
||||||
string name
|
|
||||||
decimal min_amount
|
|
||||||
decimal max_amount
|
|
||||||
string level1_role
|
|
||||||
string level2_role
|
|
||||||
string level3_role
|
|
||||||
bool enabled
|
|
||||||
int priority
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**关系备注(关键纠偏)**
|
|
||||||
- `ReimbursementForm -> WorkflowInstance` 是 **0..n**(同一报销单可能多次提交产生多个实例)。
|
|
||||||
- `WorkflowTask.assignee_id` 与 `assignee_role` 在语义上通常 **互斥**(实现允许为空/组合以支持候选/角色任务)。
|
|
||||||
- `WorkflowHistory.task_id` 在实现中为 **可空字段**(例如非 task 维度的历史事件)。
|
|
||||||
- `ApprovalRule` 是规则配置表:用于构建审批步骤,通常以 `workflow_history.payload` 等形式被引用(并非强制外键关联)。
|
|
||||||
|
|
||||||
### 3.4 单号序列(系统表)
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
DocumentSequence {
|
|
||||||
string document_type PK
|
|
||||||
string prefix
|
|
||||||
string separator
|
|
||||||
int current_number
|
|
||||||
string reset_period
|
|
||||||
datetime updated_at
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 状态机
|
## 3. 数据模型 (ER 图)
|
||||||
|
|
||||||
### 4.1 报销单状态(As‑Is)
|
### 3.1 核心业务与工作流
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
stateDiagram-v2
|
erDiagram
|
||||||
[*] --> draft: create
|
REIMBURSEMENT_FORM ||--o{ WORKFLOW_INSTANCE : instances
|
||||||
draft --> reviewing: submit
|
LEAVE_REQUEST ||--o{ WORKFLOW_INSTANCE : instances
|
||||||
reviewing --> draft: return / withdraw
|
WORKFLOW_INSTANCE ||--|{ WORKFLOW_TASK : contains
|
||||||
reviewing --> rejected: reject
|
WORKFLOW_INSTANCE ||--|{ WORKFLOW_HISTORY : logs
|
||||||
reviewing --> approved: final approve
|
|
||||||
approved --> paid: pay
|
REIMBURSEMENT_FORM {
|
||||||
rejected --> draft: edit & resubmit
|
uuid id PK
|
||||||
paid --> [*]
|
string form_number UK
|
||||||
|
string status
|
||||||
|
decimal total_amount
|
||||||
|
}
|
||||||
|
|
||||||
|
WORKFLOW_INSTANCE {
|
||||||
|
uuid id PK
|
||||||
|
uuid entity_id FK
|
||||||
|
string entity_type "reimbursement | leave"
|
||||||
|
string current_status
|
||||||
|
}
|
||||||
|
|
||||||
|
WORKFLOW_TASK {
|
||||||
|
uuid id PK
|
||||||
|
uuid instance_id FK
|
||||||
|
uuid assignee_id FK
|
||||||
|
string assignee_role
|
||||||
|
string status
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 报销单状态(To‑Be/未实现)
|
### 3.2 治理、安全与存储
|
||||||
|
|
||||||
> 预留方向:补齐“作废/取消”语义,支持运营或系统终止(例如提交后发现异常、关账等)。
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
stateDiagram-v2
|
erDiagram
|
||||||
[*] --> draft
|
EMPLOYEE ||--o{ DELEGATION_GRANT : principal
|
||||||
draft --> reviewing: submit
|
EMPLOYEE ||--o{ AUTH_SESSION : sessions
|
||||||
reviewing --> cancelled: cancel [To‑Be]
|
EMPLOYEE ||--o{ AUDIT_LOG : actor
|
||||||
approved --> cancelled: cancel [To‑Be]
|
INVOICE_POOL ||--o| OCR_RESULT : caches
|
||||||
cancelled --> [*]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.3 票据池状态(As‑Is)
|
EMPLOYEE {
|
||||||
|
uuid id PK
|
||||||
|
string bank_account "Encrypted"
|
||||||
|
string id_number "Encrypted"
|
||||||
|
int token_version
|
||||||
|
}
|
||||||
|
|
||||||
```mermaid
|
DELEGATION_GRANT {
|
||||||
stateDiagram-v2
|
uuid id PK
|
||||||
[*] --> pending_ocr: upload (OCR enabled)
|
uuid principal_id FK
|
||||||
[*] --> available: upload (OCR off/mock)
|
uuid delegate_id FK
|
||||||
pending_ocr --> available: OCR success
|
datetime valid_to
|
||||||
pending_ocr --> ocr_failed: OCR failed / duplicate
|
}
|
||||||
available --> bound: bind
|
|
||||||
bound --> archived: pay (used_complete)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.4 票据池状态(To‑Be/未实现)
|
AUDIT_LOG {
|
||||||
|
uuid id PK
|
||||||
|
string action
|
||||||
|
json extra "Context snapshot"
|
||||||
|
}
|
||||||
|
|
||||||
> 预留方向:增加“解绑、超时归档、人工纠错”能力(需要额外 API/作业/运营策略支撑)。
|
INVOICE_POOL {
|
||||||
|
uuid id PK
|
||||||
```mermaid
|
string file_sha256 UK-Index
|
||||||
stateDiagram-v2
|
string storage_path "local: or s3:"
|
||||||
bound --> available: unbind [To‑Be]
|
uuid linked_reimbursement_id FK "Nullable"
|
||||||
available --> archived: unused_timeout [To‑Be]
|
}
|
||||||
ocr_failed --> available: manual_fix [To‑Be]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. 目录结构(简化视图)
|
## 4. 目录结构
|
||||||
|
|
||||||
```
|
```
|
||||||
hyb-oa/
|
hyb-oa/
|
||||||
├── client/ # 前端 (React + Vite + Tailwind)
|
├── server/
|
||||||
│ ├── src/
|
│ ├── src/
|
||||||
│ │ ├── api/ # API 封装
|
│ │ ├── services/
|
||||||
│ │ ├── auth/ # 会话与权限映射
|
│ │ │ ├── storage/ # S3/Local 抽象
|
||||||
│ │ ├── components/ # UI 组件
|
│ │ │ ├── crypto/ # AES-256 敏感字段加密
|
||||||
│ │ ├── layouts/ # 布局
|
│ │ │ └── oidc/ # OIDC 客户端逻辑
|
||||||
│ │ ├── pages/ # 页面
|
│ │ ├── observability/ # Metrics & Logging
|
||||||
│ │ ├── query/ # React Query 统一封装
|
│ │ └── worker.js # Worker 进程入口
|
||||||
│ │ ├── router/ # 路由与守卫
|
│ └── prisma/schema.prisma # 统一模型定义
|
||||||
│ │ └── styles/ # Token/基础样式
|
├── deploy/trialrun/ # 多 Profile Compose & Runbook
|
||||||
│ └── tests/e2e/ # Playwright E2E
|
└── docs/ # 规格书与本架构图
|
||||||
│
|
|
||||||
├── server/ # 后端 (Fastify + Prisma)
|
|
||||||
│ ├── src/
|
|
||||||
│ │ ├── routes/ # API 路由
|
|
||||||
│ │ ├── services/ # 业务逻辑 (OCR 等)
|
|
||||||
│ │ ├── jobs/ # 定时任务 (OCR/委派回收)
|
|
||||||
│ │ └── utils/ # 工具函数
|
|
||||||
│ ├── prisma/ # 数据库 Schema & 迁移
|
|
||||||
│ └── scripts/ # 回归脚本
|
|
||||||
│
|
|
||||||
├── deploy/trialrun/ # 试运行部署包
|
|
||||||
├── docs/ # 文档 (需求/实施/架构)
|
|
||||||
├── issues/ # Issue CSV 执行合同
|
|
||||||
└── scripts/ # 门禁/备份恢复脚本
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 技术栈速览(As‑Is)
|
## 5. 展望 (To-Be / P7+)
|
||||||
|
|
||||||
| 层级 | 技术 | 说明 |
|
- **分布式作业调度**:从 `pg-boss` 演进到支持 Dapr 或 Temporal(针对复杂长流程)。
|
||||||
|---|---|---|
|
- **多租户/物理隔离**:支持更复杂的组织架构隔离方案。
|
||||||
| 前端 | React 18 + Vite + Tailwind | SPA;配套 React Query、Radix primitives、Playwright E2E |
|
- **前端微模块化**:针对行政、财务、业务员拆分不同的前端 Bundle。
|
||||||
| 后端 | Fastify + Prisma | Node.js;JWT + refresh cookie;RBAC 权限 |
|
|
||||||
| 数据库 | PostgreSQL 15+(试运行建议 16) | Prisma ORM;关键队列/作业依赖 SQL 锁语义 |
|
|
||||||
| OCR | 腾讯云(可 mock/off) | 异步作业 + 并发锁;失败降级不阻塞上传 |
|
|
||||||
| 部署 | Docker Compose | 单机试运行;可选反代同域 |
|
|
||||||
| CI | GitHub Actions | 最小门禁 + 回归 + E2E(按开关分层执行) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 相关文档
|
|
||||||
|
|
||||||
- [需求规格说明书](./requirements-specification.md) - 完整的 P0 需求定义
|
|
||||||
- [实施计划](./implementation_plan.md) - 里程碑与验收标准
|
|
||||||
- [技术栈报告](./tech_stack_report.md) - 选型依据
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. 对标与演进(不造轮子)
|
|
||||||
|
|
||||||
> 本节只列“可借鉴能力清单”,不代表已实现。
|
|
||||||
|
|
||||||
### 8.1 工作流(Camunda / Flowable / Activiti 思想)
|
|
||||||
- **Candidate / Claim / Unclaim**:候选人/候选组与认领机制(减少“角色任务抢占冲突”,提升可控性)。
|
|
||||||
- **SLA / Due date / Escalation**:任务期限与超时升级(试运行后期高频诉求)。
|
|
||||||
- **History & Audit 分层**:流程历史(workflow_history)与合规审计(audit_logs)职责边界清晰。
|
|
||||||
|
|
||||||
### 8.2 费用报销(Odoo / ERPNext 的单据分层)
|
|
||||||
- **业务单据 vs 付款凭证/对账**:先保持 `payment_voucher_no/payment_date/channel` 的轻量字段;后续可演进独立 `payment_transactions` 与对账状态,不影响现有 API。
|
|
||||||
|
|
||||||
### 8.3 合规审计(企业级通用实践)
|
|
||||||
- **统一 request_id/trace_id**:贯穿 API 与作业日志(便于追踪、复盘、举证)。
|
|
||||||
- **最小可观测性**:ready/metrics/log 聚合(先最小可用,后续再引入 OpenTelemetry 等体系化方案)。
|
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户