From 3b1b70d57102f97c4afb4b1639c77dfbf1e9a71d Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Tue, 7 Jul 2026 09:58:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=B8=BA=E5=AE=8C=E6=95=B4=E7=9A=84=E8=8D=AF=E5=93=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=B3=BB=E7=BB=9F=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重写README文件,将原来的简单项目概述更新为详细的药品管理系统文档, 包含技术栈介绍、系统架构图、数据库设计、功能模块说明、业务流程图和 快速开始指南等完整内容。 --- readme.md | 407 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 388 insertions(+), 19 deletions(-) diff --git a/readme.md b/readme.md index 0f35242..5060099 100644 --- a/readme.md +++ b/readme.md @@ -1,27 +1,396 @@ -# 医药管理系统 +# 药品管理系统 (MedicineManagerSystem) -## 一、概述 +基于 **Vue 3 + Spring Boot** 的全栈药品进销存管理系统,支持采购入库、库存管理、销售/领用出库、效期预警、供应商管理及 RBAC 权限控制。 -### (一)项目概述 +--- -#### 1.技术栈: +## 技术栈 -| 类别 | 技术栈 | 版本 | -| ---------- | ------------------------------ | ----------- | -| 前端 | Vue 3 + Vite | 3.5+ / 8.1+ | -| UI 与路由 | Element Plus + Vue Router | 2.x / 4.x | -| 状态与请求 | Pinia + Axios | 2.x / 1.x | -| 样式与图标 | Sass + @element-plus/icons-vue | 1.x / — | -| 后端框架 | Spring Boot (含 Validation) | 4.1.0 | -| 开发语言 | JDK | 21 | -| 持久层 | MyBatis-Plus + MySQL | 3.5+ / 8.0+ | -| 安全与认证 | Spring Security + JWT (jjwt) | 6.x / 0.12+ | -| 工具库 | Hutool | 5.x | -| 项目构建 | Maven | 3.9+ | +| 类别 | 技术 | 版本 | +|------|------|------| +| 前端框架 | 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) | — | +| 工具库 | Hutool 5.8 / Lombok | — | +| 构建 | Maven 3.9+ | — | -## 二、前端 +--- -## 三、后端 +## 系统架构 -## 四、快速开始 +```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"] + + Client -->|"HTTPS + JWT Token"| Security + 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 +``` + +--- + +## 快速开始 + +### 环境要求 + +- 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 +cp backend/.env.example backend/.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 +├── projectDesigning.md # 详细架构设计文档 +│ +├── frontend/ # Vue 3 SPA +│ └── src/ +│ ├── api/ (11) # API 模块 +│ ├── directives/ (1) # v-permission +│ ├── layout/ (1) # MainLayout +│ ├── router/ (1) # 路由 + 动态菜单 +│ ├── stores/ (2) # Pinia +│ ├── styles/ (1) # SCSS +│ ├── utils/ (1) # Axios 封装 +│ └── views/ (16) # 页面组件 +│ +└── backend/ # Spring Boot + └── 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) # 业务逻辑 + └── 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` | 登录 |