# 药品管理系统 (MedicineManagerSystem) 基于 **Vue 3 + Spring Boot** 的全栈药品进销存管理系统,支持采购入库、库存管理、销售/领用出库、效期预警、供应商管理及 RBAC 权限控制。 --- ## 技术栈 | 类别 | 技术 | 版本 | |------|------|------| | 前端框架 | Vue 3 (Composition API) | 3.5+ | | 构建工具 | Vite | 8.x | | UI 组件库 | Element Plus | 2.x | | 路由 / 状态 | Vue Router 4 / Pinia 2 | 4.x / 2.x | | HTTP / 样式 | Axios 1.x / Sass | 1.x | | 后端框架 | Spring Boot (Web + Security + Validation) | 4.1.0 | | JDK | Java 21 | LTS | | ORM | MyBatis-Plus | 3.5.11 | | 安全认证 | Spring Security + JWT (jjwt 0.13.0) | — | | 数据库 | MySQL 8.0 (JDBC + HikariCP) | — | | 缓存 | Redis 7 + Jedis(可选,无连接自动跳过) | — | | 工具库 | Hutool 5.8 / Lombok | — | | 构建 / 部署 | Maven 3.9+ / Docker + Docker Compose | — | --- ## 系统架构 ```mermaid graph TB Client["🖥️ 浏览器
Vue 3 + Element Plus SPA"] subgraph Backend["☕ Spring Boot 4.1.0"] Security["🔐 Spring Security + JWT Filter
无状态认证"] Controller["📡 Controller Layer
REST API · 参数校验 · 统一响应格式 Result-T"] Service["⚙️ Service Layer
业务逻辑 · 事务管理"] Mapper["🗄️ MyBatis-Plus Mapper
分页 · 逻辑删除 · 自动填充"] end DB["🐬 MySQL 8.0
InnoDB · utf8mb4"] Cache["⚡ Redis 7
可选 · 自动降级"] Client -->|"HTTPS + JWT Token"| Security Service --> Cache Security --> Controller Controller --> Service Service --> Mapper Mapper -->|"JDBC"| DB ``` ### 请求响应流程 ```mermaid sequenceDiagram participant F as 🖥️ Vue 前端 participant A as Axios 拦截器 participant SF as JWT Filter participant C as Controller participant SV as Service participant M as Mapper participant DB as MySQL F->>A: 发起 API 请求 A->>A: 自动注入 JWT Token
到 Authorization Header A->>SF: HTTP Request (Bearer Token) alt Token 有效 SF->>C: 放行请求 C->>C: @Valid 参数校验 C->>SV: 调用业务方法 SV->>M: 调用数据访问 M->>DB: 执行 SQL DB-->>M: 返回结果集 M-->>SV: 返回实体对象 SV-->>C: 返回处理结果 C-->>A: "Result JSON" A->>A: 响应拦截器处理
(统一错误提示 / Token 刷新) A-->>F: 展示数据 else Token 过期或无效 SF-->>A: 401 Unauthorized A->>A: 尝试 RefreshToken alt 刷新失败 A->>F: 跳转登录页 end else 权限不足 SF-->>A: 403 Forbidden A-->>F: 提示"权限不足" end ``` --- ### RBAC 权限模型 ```mermaid graph LR U["👤 User"] -->|N:M| UR["user_role"] UR -->|N:M| R["🔑 Role"] R -->|N:M| RM["role_menu"] RM -->|N:M| M["📋 Menu"] M --> D["📁 目录 type=1
侧边栏分组"] M --> P["📄 菜单 type=2
页面路由"] M --> B["🔘 按钮 type=3
permission_code"] subgraph 三层权限 L1["路由守卫"] -.-> P L2["v-permission 指令"] -.-> B L3["@PreAuthorize"] -.-> B end ``` --- ### 认证流程 ```mermaid sequenceDiagram participant U as 👤 用户 participant F as 🖥️ Vue 前端 participant B as ☕ Spring Boot participant DB as 🐬 MySQL rect rgb(240, 248, 255) Note over U,DB: === 登录阶段 === U->>F: 输入用户名 + 密码 F->>B: POST /api/auth/login
{username, password} B->>DB: 查询用户信息 DB-->>B: 返回用户实体 B->>B: BCrypt.matches(password, hash) alt 验证成功 B->>B: 生成 AccessToken (有效期 24h) B->>B: 生成 RefreshToken (有效期 7天) B-->>F: { token, refreshToken, userInfo, menus } F->>F: localStorage.setItem('token', ...) F-->>U: 跳转首页,渲染菜单 else 验证失败 B-->>F: 401 "用户名或密码错误" F-->>U: ElMessage.error 提示 end end rect rgb(255, 248, 240) Note over U,DB: === 正常请求阶段 === F->>B: GET /api/medicines
Authorization: Bearer B->>B: JwtFilter 解析 Token
提取 userId + permissions B->>B: SecurityContextHolder 设置认证信息 B-->>F: Result > end rect rgb(255, 240, 240) Note over U,DB: === Token 过期刷新 === F->>B: POST /api/auth/refresh
{ refreshToken } B->>B: 校验 RefreshToken 有效性 alt RefreshToken 有效 B->>B: 签发新 AccessToken B-->>F: { token: "newToken" } F->>F: 更新 localStorage else RefreshToken 过期 B-->>F: 401 F->>F: 清空 Token,跳转登录页 end end ``` --- ## 数据库设计 ```mermaid erDiagram medicine_category ||--o{ medicine : "分类" medicine ||--o{ stock : "库存批次" medicine ||--o{ stock_in_detail : "" medicine ||--o{ stock_out_detail : "" warehouse ||--o{ stock : "" warehouse ||--o{ stock_in_record : "" warehouse ||--o{ stock_out_record : "" supplier ||--o{ stock_in_record : "" customer ||--o{ stock_out_record : "" stock_in_record ||--o{ stock_in_detail : "" stock_out_record ||--o{ stock_out_detail : "" user ||--o{ stock_in_record : "操作人" user ||--o{ stock_out_record : "操作人" user ||--o{ user_role : "" role ||--o{ user_role : "" role ||--o{ role_menu : "" menu ||--o{ role_menu : "" menu ||--o{ menu : "父子菜单" medicine_category { bigint id PK "分类ID" varchar name "名称" varchar code UK "编码" } medicine { bigint id PK "药品ID" varchar name "通用名称" varchar specification "规格" varchar manufacturer "厂家" varchar approval_number UK "批准文号" decimal retail_price "零售价" decimal wholesale_price "批发价" int shelf_life "有效期(月)" int warning_stock "预警阈值" } supplier { bigint id PK "供应商ID" varchar name "名称" varchar contact_person "联系人" varchar phone "电话" tinyint status "状态" } customer { bigint id PK "客户ID" varchar name "名称" varchar phone "电话" varchar type "类型" } warehouse { bigint id PK "仓库ID" varchar name "名称" varchar code UK "编码" } stock { bigint id PK "库存ID" bigint medicine_id FK "药品" bigint warehouse_id FK "仓库" varchar batch_no "批号" int quantity "数量" decimal cost_price "成本价" date expiry_date "有效期至" } stock_in_record { bigint id PK "入库单ID" varchar record_no UK "单号" bigint supplier_id FK "供应商" decimal total_amount "总金额" date in_date "入库日期" tinyint status "0待审1已入库2已取消" } stock_in_detail { bigint id PK bigint record_id FK bigint medicine_id FK varchar batch_no int quantity decimal cost_price "进价" decimal total_price "小计" date expiry_date } stock_out_record { bigint id PK "出库单ID" varchar record_no UK "单号" bigint customer_id FK "客户" decimal total_amount "总金额" date out_date "出库日期" tinyint out_type "1销售2领用3报损4退货" tinyint status "0待审1已出库2已取消" } stock_out_detail { bigint id PK bigint record_id FK bigint medicine_id FK varchar batch_no int quantity decimal unit_price "单价" decimal total_price "小计" } user { bigint id PK "用户ID" varchar username UK "用户名" varchar password "BCrypt" varchar real_name "姓名" varchar phone "手机" tinyint status "状态" } role { bigint id PK "角色ID" varchar name "名称" varchar code UK "编码" } user_role { bigint id PK bigint user_id FK bigint role_id FK } menu { bigint id PK "菜单ID" bigint parent_id "父ID" varchar name "名称" tinyint type "1目录2菜单3按钮" varchar path "路由" varchar icon "图标" varchar permission_code UK "权限编码" } role_menu { bigint id PK bigint role_id FK bigint menu_id FK } ``` --- ## 功能模块 | 模块 | 功能 | |------|------| | 🔐 认证 | 登录 / 注册 / JWT 刷新 / 退出 | | 📊 仪表盘 | 4 项统计卡片 · **效期预警**(<30天红 · 30-90天橙) · 最近出入库 | | 💊 药品管理 | CRUD · 分类树管理 · 按名称/批准文号/分类搜索 · 分页 | | 📦 库存管理 | 批次跟踪 · 效期颜色标记 · 库存盘点 | | 📥 入库管理 | 新建入库单(供应商+动态明细行) · **审核通过自动增加库存** · 驳回 | | 📤 出库管理 | 新建出库单(客户+库存批次选择) · **审核通过 FIFO 扣减库存** · 驳回 | | 🏢 供应商 | CRUD · 启用/停用开关 | | 👥 客户 | CRUD · 类型(个人/医院/药店/诊所) | | 👤 用户管理 | CRUD · 角色分配 | | 🔑 角色管理 | CRUD · 权限树分配 | | 📋 菜单管理 | 菜单树 CRUD · 三级(目录/菜单/按钮) | --- ## 核心业务流程 ### 入库审核 ```mermaid sequenceDiagram participant F as 前端 participant B as 后端 participant DB as MySQL Note over F,DB: 1. 创建入库单 F->>B: POST /api/stock-in
{ supplierId, details: [...] } B->>DB: INSERT stock_in_record (status=0 待审核) B->>DB: INSERT stock_in_detail (明细) B-->>F: 入库单已创建 Note over F,DB: 2. 审核通过 F->>B: PUT /api/stock-in/{id}/status
{ status: 1 } B->>DB: 查同药品+同批号库存 alt 库存已存在 B->>DB: UPDATE stock SET quantity += N else 新批次 B->>DB: INSERT stock (新批次) end B->>DB: UPDATE status=1 B-->>F: 审核通过 · 库存已更新 ``` ### 出库审核 ```mermaid sequenceDiagram participant F as 前端 participant B as 后端 participant DB as MySQL Note over F,DB: 1. 创建出库单 F->>B: POST /api/stock-out
{ customerId, details: [...] } B->>DB: INSERT stock_out_record (status=0 待审核) Note over F,DB: 2. 审核通过 F->>B: PUT /api/stock-out/{id}/status
{ status: 1 } B->>DB: 查可用库存 (FIFO 按效期升序) alt 库存充足 B->>DB: UPDATE stock SET quantity -= N B->>DB: UPDATE status=1 B-->>F: 审核通过 · 库存已扣减 else 库存不足 B-->>F: 错误: 库存不足,需要 X,可用 Y end ``` --- ## 快速开始 ### 方式一:Docker Compose(推荐) ```bash # 1. 配置环境变量 cd backend cp .env.example .env # 编辑 .env 设置 DB_PASSWORD 和 JWT_SECRET # 2. 一键启动(MySQL + Redis + 后端 + 前端) docker-compose up -d # 3. 访问 open http://localhost ``` > 首次启动 MySQL 会自动执行 `init.sql` 建表和数据。Redis 不可用时后端自动降级跳过缓存。 ### 方式二:手动启动 **环境要求** - JDK 21+ · Node.js 18+ · MySQL 8.0+ · Maven 3.9+ #### 1. 初始化数据库 ```bash mysql -u root -p < backend/src/main/resources/database/init.sql ``` ### 2. 配置后端环境变量 ```shell cd backend cp .env.example .env ``` 在 `backend/.env` 中填写: ```properties DB_HOST=localhost DB_PORT=3306 DB_NAME=medicine_manager DB_USERNAME=root DB_PASSWORD=你的数据库密码 JWT_SECRET=你的256位Base64密钥 ``` ### 3. 启动后端 ```bash cd backend ./mvnw spring-boot:run -DskipTests ``` > 后端运行在 `http://localhost:8080`,启动时会自动加载 `.env` ### 4. 启动前端 ```bash cd frontend npm install npm run dev ``` > 前端运行在 `http://localhost:5173`,API 请求自动代理到后端 ### 5. 登录 | 用户名 | 密码 | 角色 | 权限范围 | |--------|------|------|----------| | `admin` | `admin123` | 超级管理员 | 全部 | | `warehouse01` | `admin123` | 仓库管理员 | 出入库 + 库存 + 供应商客户查看 | | `staff01` | `admin123` | 普通员工 | 仪表盘 + 只读查看 | --- ## 项目结构 ``` MedicineManagerSystem/ ├── README.md ├── .env.example ├── docker-compose.yml # Docker 编排 ├── projectDesigning.md # 详细架构设计文档 │ ├── frontend/ # Vue 3 SPA │ ├── Dockerfile │ ├── nginx.conf │ └── src/ │ ├── api/ (11) # API 模块 │ ├── directives/ (1) # v-permission │ ├── layout/ (1) # MainLayout │ ├── router/ (1) # 路由 + 动态菜单 │ ├── stores/ (2) # Pinia │ ├── styles/ (1) # SCSS │ ├── utils/ (2) # Axios · Redis 缓存 │ └── views/ (16) # 页面组件 │ └── backend/ # Spring Boot ├── Dockerfile └── src/main/ ├── java/com/kronecker/backend/ │ ├── common/ (4) # Result · 异常 │ ├── config/ (3) # Security · MP · CORS │ ├── controller/ (12) # REST API │ ├── dto/ (4) # 请求对象 │ ├── entity/ (15) # 数据库实体 │ ├── mapper/ (15) # MP Mapper │ ├── security/ (3) # JWT │ ├── service/ (23) # 业务逻辑 │ └── utils/ (1) # Redis 缓存 └── resources/ ├── application.yml └── database/init.sql # 建表 + 种子数据 ``` --- ## API 端点 | 模块 | 方法 | 路径 | 权限 | |------|------|------|------| | 认证 | `POST` | `/api/auth/login` | 公开 | | 认证 | `POST` | `/api/auth/register` | 公开 | | 认证 | `POST` | `/api/auth/refresh` | 公开 | | 认证 | `GET` | `/api/auth/userinfo` | 登录 | | 仪表盘 | `GET` | `/api/dashboard/statistics` | 登录 | | 仪表盘 | `GET` | `/api/dashboard/expiry-warning` | 登录 | | 药品 | `GET POST PUT DELETE` | `/api/medicines[/{id}]` | medicine:* | | 分类 | `GET POST PUT DELETE` | `/api/categories[/{id}]` | category:* | | 供应商 | `GET POST PUT DELETE` | `/api/suppliers[/{id}]` | supplier:* | | 客户 | `GET POST PUT DELETE` | `/api/customers[/{id}]` | customer:* | | 库存 | `GET` | `/api/stocks` | stock:list | | 入库 | `GET POST` | `/api/stock-in` | stock-in:* | | 入库 | `PUT` | `/api/stock-in/{id}/status` | stock-in:audit | | 出库 | `GET POST` | `/api/stock-out` | stock-out:* | | 出库 | `PUT` | `/api/stock-out/{id}/status` | stock-out:audit | | 用户 | `GET POST PUT DELETE` | `/api/users[/{id}]` | user:* | | 角色 | `GET POST PUT DELETE` | `/api/roles[/{id}]` | role:* | | 菜单 | `GET POST PUT DELETE` | `/api/menus[/{id}]` | menu:* | | 菜单 | `GET` | `/api/menus/tree` | 登录 |