04-系统架构与业务流程图.md 20 KB

系统架构与业务流程图

基于项目源码自动生成,所有图表均使用 Mermaid 语法,可在支持 Mermaid 的 Markdown 编辑器中渲染。


一、系统整体架构

1.1 分层架构总览

graph TB
    subgraph 用户层
        Browser["浏览器<br/>(Chrome / Edge)"]
    end

    subgraph 前端["前端 · Vue 3 + Vite"]
        Router["Vue Router<br/>路由守卫 / 权限控制"]
        Views["页面组件<br/>Dashboard / Resource / Project ..."]
        Store["Pinia Store<br/>用户状态 / Token"]
        API["Axios API 层<br/>request.js 统一拦截"]
    end

    subgraph 反向代理["Nginx 反向代理"]
        Static["静态资源托管<br/>dist/"]
        Proxy["/api/ → :8080<br/>API 反代"]
    end

    subgraph 后端["后端 · Spring Boot 3"]
        Controller["Controller 层<br/>REST 接口"]
        Security["Spring Security<br/>JWT 认证 + 权限"]
        Service["Service 层<br/>核心业务逻辑"]
        Mapper["Mapper 层<br/>MyBatis-Plus"]
        Task["定时任务<br/>ResourceExpireTask"]
        Mail["邮件服务<br/>EmailService"]
    end

    subgraph 数据层
        MySQL["MySQL 8.0+<br/>resource_platform"]
        SMTP["SMTP 邮件服务器"]
    end

    Browser -->|HTTP| 反向代理
    反向代理 -->|静态文件| Static
    反向代理 -->|/api/*| Controller
    Router --> Views
    Views --> Store
    Views --> API
    API -->|Bearer Token| Controller
    Controller --> Security
    Security --> Service
    Service --> Mapper
    Mapper --> MySQL
    Task --> Service
    Service --> Mail
    Mail --> SMTP

1.2 后端内部分层

graph LR
    subgraph 接入层
        A["AuthController"]
        RC["ResourceController"]
        PC["ProjectController"]
        DC["DashboardController"]
        UC["UserController"]
        RTC["ResourceTypeController"]
        PRC["ProviderController"]
        LC["OperationLogController"]
    end

    subgraph 安全层
        JWT["JwtAuthenticationFilter"]
        SC["SecurityConfig<br/>CORS / 权限规则"]
        SU["SecurityUtils"]
    end

    subgraph 业务层
        RS["ResourceServiceImpl"]
        PS["ProjectServiceImpl"]
        US["SysUserServiceImpl"]
        RRS["ResourceRenewalServiceImpl"]
        DS["DashboardServiceImpl"]
        OLS["OperationLogServiceImpl"]
        ES["EmailServiceImpl"]
    end

    subgraph 数据层
        RM["ResourceMapper"]
        PM["ProjectMapper"]
        UM["SysUserMapper"]
        RRM["ResourceRenewalMapper"]
        PRM["ProjectResourceMapper"]
        OLM["OperationLogMapper"]
        ENLM["ExpireNotifyLogMapper"]
    end

    A --> US
    RC --> RS
    RC --> RRS
    PC --> PS
    DC --> DS
    UC --> US
    RTC --> RS
    PRC --> RS
    LC --> OLS

    RS --> RM
    RS --> PRM
    PS --> PM
    PS --> PRM
    US --> UM
    RRS --> RRM
    RRS --> RM
    DS --> RM
    DS --> PM
    OLS --> OLM
    RS --> OLS
    ES --> SMTP["SMTP"]

    JWT --> SC

二、数据模型关系图

2.1 ER 图

erDiagram
    sys_user ||--o{ resource : "负责"
    sys_user ||--o{ project : "负责"
    sys_user ||--o{ operation_log : "操作"

    project ||--o{ project : "父子(两层)"
    project ||--o{ project_resource : "关联"
    resource ||--o{ project_resource : "关联"
    resource ||--o{ resource_renewal : "续费"
    resource ||--o{ expire_notify_log : "提醒"
    resource }o--|| resource_type : "类型"
    resource }o--|| provider : "服务商"

    sys_user {
        int id PK
        varchar username
        varchar password
        varchar real_name
        varchar email
        int role "1=管理员 2=普通"
        int status "0=禁用 1=启用"
    }

    resource {
        int id PK
        varchar name
        int type_id FK
        int provider_id FK
        int owner_id FK
        date start_date
        date expire_date
        decimal cost
        int cost_cycle "1=月 2=年 3=一次性"
        int status "0停用 1使用中 2即将到期 3已过期"
        int purchase_duration
        varchar account_info
        varchar remark
    }

    project {
        int id PK
        varchar name
        int parent_id FK "NULL=顶层"
        int owner_id FK
        int status "0=下线 1=进行中"
        varchar remark
    }

    project_resource {
        int id PK
        int project_id FK
        int resource_id FK
        decimal allocation_percent "分摊占比"
    }

    resource_type {
        int id PK
        varchar name
        int sort
        varchar remark
    }

    provider {
        int id PK
        varchar name
        varchar remark
    }

    resource_renewal {
        int id PK
        int resource_id FK
        date old_expire_date
        date new_expire_date
        int duration_code
        decimal cost
        varchar remark
        int operator_id FK
    }

    expire_notify_log {
        int id PK
        int resource_id FK
        varchar stage "far/near/expired"
        date notify_date
        varchar send_to
    }

    operation_log {
        int id PK
        int user_id FK
        varchar module
        varchar operation
        varchar detail
    }

2.2 核心关联关系说明

关系 类型 说明
resource ↔ resource_type 多对一 每个资源属于一个类型
resource ↔ provider 多对一 每个资源属于一个服务商
resource ↔ sys_user (owner) 多对一 每个资源有一个负责人
resource ↔ project 多对多 通过 project_resource 关联,支持费用分摊
project_resource.allocation_percent 分摊占比 同一资源在不同项目间按比例分摊费用
project ↔ project (parent) 自关联 两层结构:顶层项目 + 子项目
resource ↔ resource_renewal 一对多 一个资源可有多条续费记录
resource ↔ expire_notify_log 一对多 三阶段提醒记录(far/near/expired)

三、核心业务流程图

3.1 用户认证流程

sequenceDiagram
    participant U as 浏览器
    participant F as 前端 (Vue)
    participant S as 后端 (Spring Boot)
    participant DB as MySQL

    U->>F: 输入用户名/密码
    F->>S: POST /auth/login {username, password}
    S->>DB: 查询用户信息
    DB-->>S: sys_user 记录

    alt 账号不存在 / 已禁用
        S-->>F: {code: 401, message: "账号不存在或已禁用"}
    end

    alt 连续失败 ≥ 5 次(15 分钟内)
        S-->>F: {code: 401, message: "账号已锁定,请 15 分钟后重试"}
    end

    alt 密码错误
        S->>DB: 记录失败次数
        S-->>F: {code: 401, message: "密码错误"}
    end

    alt 认证成功
        S->>S: 生成 JWT Token (HS256, 24h)
        S->>DB: 清除失败计数
        S-->>F: {code: 200, data: {token, userId, username, role}}
        F->>F: 存储 token 到 localStorage
        F->>F: Pinia store 更新用户状态
        F-->>U: 跳转至 Dashboard
    end

    Note over U,DB: 后续请求
    U->>F: 操作页面
    F->>S: 请求头 Authorization: Bearer <token>
    S->>S: JwtAuthenticationFilter 校验 Token
    alt Token 有效
        S->>S: 设置 SecurityContext
        S-->>F: 返回业务数据
    else Token 过期/无效
        S-->>F: 401 Unauthorized
        F->>F: 清除 token,跳转登录页
    end

3.2 权限控制模型

graph TD
    Login["用户登录"] --> JWT["获取 JWT Token"]
    JWT --> Filter["JwtAuthenticationFilter"]
    Filter -->|每次请求| Check{"Token 有效?"}
    Check -->|否| Reject["401 → 跳转登录"]
    Check -->|是| Load["加载 LoginUser<br/>(role, status)"]
    Load --> SecConfig["SecurityConfig 规则匹配"]

    SecConfig --> R1["/auth/login → 公开"]
    SecConfig --> R2["/user/enabled-list → 登录即可"]
    SecConfig --> R3["/user/** → ROLE_ADMIN"]
    SecConfig --> R4["POST/DELETE /resource-type/** → ROLE_ADMIN"]
    SecConfig --> R5["POST/DELETE /provider/** → ROLE_ADMIN"]
    SecConfig --> R6["其他接口 → 登录即可"]

    R1 --> Pass["放行"]
    R2 --> Pass
    R3 --> Admin{"isAdmin?"}
    R4 --> Admin
    R5 --> Admin
    R6 --> Pass
    Admin -->|是| Pass
    Admin -->|否| Deny["403 Forbidden"]

3.3 资源生命周期

stateDiagram-v2
    [*] --> 使用中: 创建资源<br/>(expire_date > 今天)
    [*] --> 已过期: 创建资源<br/>(expire_date ≤ 今天)

    使用中 --> 即将到期: 定时任务扫描<br/>(距到期 ≤ 30 天)
    即将到期 --> 即将到期: 定时任务扫描<br/>(距到期 ≤ 7 天)
    即将到期 --> 已过期: 超过到期日
    使用中 --> 已过期: 超过到期日

    已过期 --> 使用中: 续费<br/>(新到期日 > 今天)
    已过期 --> 即将到期: 续费<br/>(新到期日在 30 天内)

    使用中 --> 已停用: 手动停用
    即将到期 --> 已停用: 手动停用
    已停用 --> 使用中: 重新启用<br/>(更新到期日)

    使用中 --> [*]: 删除资源
    即将到期 --> [*]: 删除资源
    已过期 --> [*]: 删除资源
    已停用 --> [*]: 删除资源

3.4 资源续费流程

sequenceDiagram
    participant U as 用户
    participant F as 前端
    participant C as ResourceController
    participant RRS as ResourceRenewalService
    participant RS as ResourceService
    participant DB as MySQL

    U->>F: 点击"续费",填写续费表单
    Note over U,F: 续费日期、新到期日、时长、金额、备注

    F->>C: POST /resource/{id}/renew
    C->>RRS: renewResource(id, dto)

    RRS->>DB: 查询当前资源信息
    DB-->>RRS: Resource 记录

    RRS->>RRS: 校验:新到期日 > 当前到期日

    alt 校验失败
        RRS-->>C: 抛出 BusinessException
        C-->>F: {code: 400, message: "新到期日必须晚于当前到期日"}
    end

    RRS->>DB: INSERT resource_renewal
    RRS->>DB: UPDATE resource.expire_date = 新到期日
    RRS->>RS: 刷新资源状态

    RS->>DB: 根据新到期日计算 status
    Note over RS,DB: 使用中 / 即将到期

    RRS-->>C: 续费成功
    C->>C: 记录操作日志
    C-->>F: {code: 200}
    F->>F: 刷新详情页 + 续费历史列表

3.5 到期提醒定时任务

flowchart TD
    Start["定时触发<br/>每天 08:00<br/>cron: 0 0 8 * * ?"] --> Refresh["刷新所有资源状态<br/>refreshExpireStatus()"]
    Refresh --> Scan["扫描需要提醒的资源"]

    Scan --> Check30{"距到期 ≤ 30 天<br/>且 > 7 天?"}
    Scan --> Check7{"距到期 ≤ 7 天<br/>且 > 0 天?"}
    Scan --> CheckExpired{"已过期<br/>expire_date < 今天?"}

    Check30 -->|是| Log30{"已发过 far 提醒?"}
    Check30 -->|否| Skip1["跳过"]
    Check7 -->|是| Log7{"已发过 near 提醒?"}
    Check7 -->|否| Skip2["跳过"]
    CheckExpired -->|是| LogExp{"已发过 expired 提醒?"}
    CheckExpired -->|否| Skip3["跳过"]

    Log30 -->|未发| Send30["发送邮件<br/>stage=far<br/>普通提醒"]
    Log30 -->|已发| Skip1
    Log7 -->|未发| Send7["发送邮件<br/>stage=near<br/>紧急提醒"]
    Log7 -->|已发| Skip2
    LogExp -->|未发| SendExp["发送邮件<br/>stage=expired<br/>过期通知"]
    LogExp -->|已发| Skip3

    Send30 --> Record30["INSERT expire_notify_log<br/>stage=far"]
    Send7 --> Record7["INSERT expire_notify_log<br/>stage=near"]
    SendExp --> RecordExp["INSERT expire_notify_log<br/>stage=expired"]

    Record30 --> End["完成"]
    Record7 --> End
    RecordExp --> End
    Skip1 --> End
    Skip2 --> End
    Skip3 --> End

3.6 费用分摊计算流程

flowchart TD
    Start["资源关联多个项目"] --> Input["用户选择关联项目"]
    Input --> Mode{"分摊模式"}

    Mode -->|均分| Calc1["每个项目 = 100% / N<br/>尾差加到最后一个"]
    Mode -->|自定义| Calc2["用户手动输入各占比<br/>合计必须 = 100%"]

    Calc1 --> Save["保存到 project_resource<br/>allocation_percent"]
    Calc2 --> Validate{"合计 = 100%?"}
    Validate -->|否| Error["提示:分摊占比合计必须为 100%"]
    Validate -->|是| Save

    Save --> Stat["费用统计时"]
    Stat --> Formula["分摊费用 = 资源总费用 ×<br/>allocation_percent / 100"]
    Formula --> Report["生成费用报表<br/>按项目 / 按类型"]

四、项目管理流程

4.1 项目树形结构

graph TD
    subgraph 两层项目模型
        P1["顶层项目 A"]
        P1 --> C1["子项目 A-1"]
        P1 --> C2["子项目 A-2"]

        P2["顶层项目 B"]
        P2 --> C3["子项目 B-1"]

        P3["顶层项目 C<br/>(无子项目)"]
    end

    C1 --> R1["资源 1"]
    C1 --> R2["资源 2"]
    C2 --> R3["资源 3"]
    C3 --> R4["资源 4"]
    P3 --> R5["资源 5"]

    style P1 fill:#409eff,color:#fff
    style P2 fill:#409eff,color:#fff
    style P3 fill:#409eff,color:#fff
    style C1 fill:#67c23a,color:#fff
    style C2 fill:#67c23a,color:#fff
    style C3 fill:#67c23a,color:#fff

规则:资源只挂在叶子节点(子项目或无子项目的顶层项目)。父项目的资源统计 = 所有子项目资源的聚合。

4.2 项目新增/编辑流程

flowchart TD
    Start["用户点击新增/编辑"] --> Form["弹窗表单"]
    Form --> Fields["名称 / 父项目 / 负责人 / 状态 / 备注"]
    Fields --> Submit["提交"]
    Submit --> Check{"有父项目?"}

    Check -->|是| Leaf["标记为子项目<br/>parent_id = 选中值"]
    Check -->|否| Root["标记为顶层项目<br/>parent_id = NULL"]

    Leaf --> Save["保存到 project 表"]
    Root --> Save

    Save --> Edit{"编辑模式?"}
    Edit -->|是| Update["UPDATE + 保留子项目关系"]
    Edit -->|否| Insert["INSERT"]

    Update --> Done["刷新列表"]
    Insert --> Done

五、前端页面结构

5.1 页面导航树

graph TD
    App["App.vue"] --> Login["/login<br/>Login.vue"]
    App --> Layout["Layout.vue<br/>侧边栏 + 顶栏"]

    Layout --> Dashboard["/dashboard<br/>工作台"]
    Layout --> Resource["/resource<br/>资源台账"]
    Layout --> ResourceDetail["/resource/:id<br/>资源详情"]
    Layout --> Project["/project<br/>项目管理"]
    Layout --> ProjectDetail["/project/:id<br/>项目详情"]
    Layout --> CostStat["/cost-stat<br/>费用统计"]
    Layout --> ProcurementCost["/procurement-cost<br/>采购费用"]
    Layout --> Dict["/dict<br/>字典管理"]
    Layout --> User["/user<br/>用户管理 🔒"]
    Layout --> Log["/log<br/>操作日志"]

    Dashboard --> KPI["6 个 KPI 卡片"]
    Dashboard --> Chart1["项目状态环形图"]
    Dashboard --> Chart2["资源类型分布条形图"]
    Dashboard --> Table["风险清单表格"]

    CostStat --> Chart3["资源类型费用环形图"]
    CostStat --> Chart4["项目预算消耗排行"]
    CostStat --> Chart5["各类型费用条形图"]
    CostStat --> Chart6["项目资源数对比"]

    Dict --> Tab1["资源类型 Tab"]
    Dict --> Tab2["服务商 Tab"]

    style User fill:#e6a23c,color:#fff

5.2 前端组件依赖

graph LR
    subgraph 页面组件
        RL["ResourceList"]
        RF["ResourceForm"]
        RD["ResourceDetail"]
        PL["ProjectList"]
        PD["ProjectDetail"]
        DSH["Dashboard"]
        CS["CostStat"]
        PC["ProcurementCost"]
    end

    subgraph 组合式函数
        UC["useChart()<br/>ECharts 封装"]
        USR["useScrollRestore()<br/>滚动恢复"]
    end

    subgraph 工具
        CSV["exportCsv()"]
        CC["getChartColors()"]
        SC["statusClass()"]
    end

    subgraph API
        RA["api/resource"]
        PA["api/project"]
        DA["api/dashboard"]
    end

    subgraph Store
        ST["store/user"]
    end

    RL --> RF
    RL --> RA
    RL --> SC
    RD --> RA
    PL --> PA
    PD --> PA
    DSH --> DA
    DSH --> UC
    DSH --> CSV
    CS --> RA
    CS --> UC
    CS --> CSV
    PC --> RA
    PC --> CSV
    UC --> CC
    RL --> USR

六、部署架构

6.1 生产部署拓扑

graph TB
    subgraph 客户端
        Browser2["浏览器"]
    end

    subgraph Nginx["Nginx (resource.mizentech.com:80)"]
        Frontend["前端静态资源<br/>/opt/resource-platform/frontend/dist/"]
        APIProxy["/api/ 反代<br/>→ 127.0.0.1:8080"]
    end

    subgraph Backend["Spring Boot (127.0.0.1:8080)"]
        App["resource-platform.jar<br/>--spring.profiles.active=prod"]
    end

    subgraph Data["数据层"]
        RDS["MySQL 8.0+<br/>resource_platform"]
        Mail2["SMTP 邮件服务器<br/>smtp.qiye.aliyun.com:465"]
    end

    subgraph 配置
        Env[".env (chmod 600)<br/>DB_URL / JWT_SECRET<br/>MAIL_* / CORS_ORIGINS"]
        Systemd["systemd 服务<br/>自动重启 / 开机自启"]
    end

    Browser2 -->|HTTP| Nginx
    Nginx -->|/| Frontend
    Nginx -->|/api/*| APIProxy
    APIProxy --> App
    App --> RDS
    App --> Mail2
    Env --> App
    Systemd --> App

6.2 环境配置对比

graph LR
    subgraph 开发环境
        Dev["application-dev.yml"]
        DevDB["阿里云 RDS<br/>明文密码"]
        DevMail["smtp.example.com<br/>占位配置"]
        DevLog["日志级别: DEBUG"]
        DevCORS["CORS: localhost:5173"]
    end

    subgraph 生产环境
        Prod["application-prod.yml"]
        ProdDB["环境变量注入<br/>DB_URL / DB_PASSWORD"]
        ProdMail["smtp.qiye.aliyun.com<br/>企业邮箱"]
        ProdLog["日志级别: INFO"]
        ProdCORS["CORS: resource.mizentech.com"]
    end

    Dev --> DevDB
    Dev --> DevMail
    Dev --> DevLog
    Dev --> DevCORS
    Prod --> ProdDB
    Prod --> ProdMail
    Prod --> ProdLog
    Prod --> ProdCORS

七、API 接口全景

7.1 接口分组总览

graph TD
    subgraph 认证模块
        A1["POST /auth/login"]
        A2["GET /auth/me"]
        A3["PUT /auth/password"]
    end

    subgraph 资源管理
        R1["GET /resource/page"]
        R2["GET /resource/{id}"]
        R3["POST /resource"]
        R4["DELETE /resource/{id}"]
        R5["POST /resource/relate"]
        R6["DELETE /resource/unrelate"]
        R7["POST /resource/{id}/renew"]
        R8["GET /resource/{id}/renewals"]
        R9["DELETE /resource/{id}/renewals/{rid}"]
        R10["GET /resource/export"]
    end

    subgraph 费用统计
        C1["GET /resource/cost-stat/project"]
        C2["GET /resource/cost-stat/type"]
        C3["GET /resource/procurement-cost"]
        C4["GET /resource/procurement-cost/export"]
    end

    subgraph 项目管理
        P1["GET /project/page"]
        P2["GET /project/tree"]
        P3["GET /project/{id}"]
        P4["POST /project"]
        P5["DELETE /project/{id}"]
        P6["GET /project/active-list"]
        P7["GET /project/parent-options"]
    end

    subgraph 工作台
        D1["GET /dashboard"]
    end

    subgraph 系统管理 🔒
        U1["GET /user/list"]
        U2["GET /user/enabled-list"]
        U3["POST /user"]
        U4["DELETE /user/{id}"]
        T1["GET /resource-type/list"]
        T2["POST /resource-type"]
        T3["DELETE /resource-type/{id}"]
        V1["GET /provider/list"]
        V2["POST /provider"]
        V3["DELETE /provider/{id}"]
        L1["GET /operation-log/page"]
    end

八、技术栈依赖关系

graph TD
    subgraph 前端依赖
        Vue["Vue 3.4"] --> VRouter["Vue Router 4.3"]
        Vue --> Pinia["Pinia 2.1"]
        Vue --> EP["Element Plus 2.6"]
        Vue --> ECharts["ECharts 6.1"]
        Vue --> Axios["Axios 1.6"]
        Vue --> DayJS["Day.js 1.11"]
    end

    subgraph 后端依赖
        SB["Spring Boot 3.2.5"] --> SS["Spring Security 6.2"]
        SB --> MBP["MyBatis-Plus 3.5.5"]
        SB --> SM["Spring Mail"]
        SB --> SV["Spring Validation"]
        SS --> JWT["JJWT 0.11.5"]
        MBP --> MySQL["MySQL Connector 8.3"]
        SB --> EE["EasyExcel 3.3.2"]
        SB --> Lombok["Lombok 1.18.32"]
    end

    subgraph 构建工具
        Vite["Vite 5.1"]
        Maven["Maven"]
        Vitest["Vitest 4.1"]
        JUnit["JUnit 5 + Mockito"]
    end