毕业设计实战:医院医疗设备管理系统设计与实现
在开发“医院医疗设备管理系统”时,曾因“设备生命周期状态流转混乱”踩过关键坑——初期设备状态只有简单的在用/闲置,未考虑采购、入库、领用、维修、报废等完整流程,导致设备溯源困难,耗费3天重构状态机才解决问题🏥。本文将系统拆解为精简实用的实施指南。
一、需求分析:聚焦设备全生命周期管理
医疗设备管理最忌“功能堆砌”。明确“设备科-临床科室-维修科-领导”四方协作流程是关键。
1. 核心业务流程
设备生命周期全流程:
采购申请 → 审批采购 → 设备入库 → 科室领用 → 日常使用 →
维修申报 → 质量检测 → 设备转科 → 设备报废 → 资产盘点
核心管理维度:
1. 按设备状态:采购中、库存中、使用中、维修中、报废中
2. 按管理环节:采购管理、库存管理、使用管理、维保管理
3. 按业务类型:设备入库、设备出库、设备转科、设备报损
2. 核心功能设计(精简版)
| 角色 | 核心功能 | 关键要点 |
|---|---|---|
| 设备科管理员 | 设备台账、库存管理、采购审批、数据统计 | 完整记录设备全生命周期 |
| 临床科室 | 设备领用、维修申报、转科申请、使用登记 | 线上审批流程,减少纸质流转 |
| 维修科 | 维修记录、质量检测、预防性维护 | 建立设备维保档案 |
| 院领导 | 数据看板、资产报表、审批流程 | 宏观掌握设备资产状况 |
3. 设备状态机设计
// 设备状态流转图(简化版)
采购申请 → 已审批 → 已采购 → 已入库 → 已领用 → 使用中
↓ ↓ ↓ ↓
已取消 已入库 已领用 维修中 → 已修复 → 使用中
↓
报废中 → 已报废
二、技术选型:稳定可靠的技术栈
| 技术 | 选型理由 | 精简说明 |
|---|---|---|
| Spring Boot 2.7 | 快速开发,配置简单 | 医疗系统要求稳定可靠 |
| MySQL 8.0 | 事务支持,数据安全 | 设备数据不能丢失 |
| MyBatis Plus | 简化数据库操作 | 减少重复CRUD代码 |
| Vue 2 + ElementUI | 组件丰富,开发快速 | 适合管理后台开发 |
| Spring Security | 权限控制,数据安全 | 医疗数据敏感,需严格权限 |
三、数据库设计:核心表结构(精简版)
-- 设备基本信息表(核心表)
CREATE TABLE medical_device (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
device_no VARCHAR(50) UNIQUE NOT NULL COMMENT '设备编号(如SB2024001)',
device_name VARCHAR(100) NOT NULL COMMENT '设备名称',
device_type VARCHAR(20) NOT NULL COMMENT '设备类型:A/B/C类',
brand VARCHAR(50) COMMENT '品牌',
model VARCHAR(50) COMMENT '型号',
serial_no VARCHAR(100) COMMENT '序列号',
-- 状态信息
current_status TINYINT NOT NULL COMMENT '当前状态:1采购中 2库存中 3使用中 4维修中 5报废中',
department_id BIGINT COMMENT '所属科室ID',
user_id BIGINT COMMENT '使用人ID',
location VARCHAR(200) COMMENT '存放位置',
-- 资产信息
purchase_price DECIMAL(12,2) COMMENT '采购价格',
purchase_date DATE COMMENT '采购日期',
warranty_period INT COMMENT '保修期(月)',
useful_life INT COMMENT '使用年限',
-- 管理信息
last_maintenance_date DATE COMMENT '上次保养日期',
next_maintenance_date DATE COMMENT '下次保养日期',
total_usage_hours INT DEFAULT 0 COMMENT '累计使用小时数',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_device_no (device_no),
INDEX idx_status (current_status),
INDEX idx_department (department_id)
) COMMENT='医疗设备基本信息表';
-- 设备库存表(简化库存管理)
CREATE TABLE device_inventory (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
device_id BIGINT NOT NULL COMMENT '设备ID',
storage_id BIGINT NOT NULL COMMENT '库房ID',
quantity INT NOT NULL DEFAULT 0 COMMENT '库存数量',
min_quantity INT DEFAULT 5 COMMENT '最低库存预警',
max_quantity INT DEFAULT 100 COMMENT '最大库存量',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_device_storage (device_id, storage_id),
FOREIGN KEY (device_id) REFERENCES medical_device(id)
) COMMENT='设备库存表';
-- 设备流转记录表(核心业务表)
CREATE TABLE device_flow_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
record_no VARCHAR(50) UNIQUE NOT NULL COMMENT '流转单号',
device_id BIGINT NOT NULL COMMENT '设备ID',
-- 流转信息
flow_type TINYINT NOT NULL COMMENT '流转类型:1入库 2出库 3转科 4维修 5报废',
from_department_id BIGINT COMMENT '来源科室',
to_department_id BIGINT COMMENT '目标科室',
from_user_id BIGINT COMMENT '原使用人',
to_user_id BIGINT COMMENT '新使用人',
quantity INT DEFAULT 1 COMMENT '数量',
-- 审批信息
apply_user_id BIGINT NOT NULL COMMENT '申请人',
apply_reason VARCHAR(500) COMMENT '申请事由',
status TINYINT DEFAULT 1 COMMENT '状态:1待审批 2已通过 3已驳回',
approver_id BIGINT COMMENT '审批人',
approval_time DATETIME COMMENT '审批时间',
approval_remark VARCHAR(200) COMMENT '审批意见',
-- 流转详情
flow_date DATE NOT NULL COMMENT '流转日期',
remark VARCHAR(500) COMMENT '备注',
attachment VARCHAR(500) COMMENT '附件路径',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES medical_device(id),
INDEX idx_flow_type (flow_type),
INDEX idx_status_date (status, flow_date)
) COMMENT='设备流转记录表';
-- 设备维修记录表
CREATE TABLE device_maintenance (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
maintenance_no VARCHAR(50) UNIQUE NOT NULL COMMENT '维修单号',
device_id BIGINT NOT NULL COMMENT '设备ID',
-- 维修信息
fault_description TEXT NOT NULL COMMENT '故障描述',
maintenance_type TINYINT COMMENT '维修类型:1日常维修 2预防性维护 3大修',
maintenance_date DATE NOT NULL COMMENT '维修日期',
completion_date DATE COMMENT '完成日期',
cost DECIMAL(10,2) COMMENT '维修费用',
maintenance_result TEXT COMMENT '维修结果',
-- 责任信息
reporter_id BIGINT COMMENT '报修人',
maintainer_id BIGINT COMMENT '维修人',
department_id BIGINT COMMENT '报修科室',
-- 状态
status TINYINT DEFAULT 1 COMMENT '状态:1待处理 2维修中 3已完成 4已取消',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES medical_device(id),
INDEX idx_device_status (device_id, status)
) COMMENT='设备维修记录表';
-- 设备采购申请表
CREATE TABLE device_purchase_apply (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
apply_no VARCHAR(50) UNIQUE NOT NULL COMMENT '申请单号',
-- 申请信息
device_name VARCHAR(100) NOT NULL COMMENT '设备名称',
device_type VARCHAR(20) NOT NULL COMMENT '设备类型',
brand VARCHAR(50) COMMENT '品牌',
model VARCHAR(50) COMMENT '型号',
quantity INT NOT NULL COMMENT '申请数量',
estimated_price DECIMAL(12,2) COMMENT '预估价格',
apply_reason TEXT NOT NULL COMMENT '申请理由',
-- 审批信息
apply_user_id BIGINT NOT NULL COMMENT '申请人',
department_id BIGINT NOT NULL COMMENT '申请科室',
status TINYINT DEFAULT 1 COMMENT '状态:1待审批 2已审批 3已采购 4已完成 5已驳回',
approver_id BIGINT COMMENT '审批人',
approval_time DATETIME COMMENT '审批时间',
approval_remark VARCHAR(200) COMMENT '审批意见',
-- 采购信息
actual_price DECIMAL(12,2) COMMENT '实际价格',
purchase_date DATE COMMENT '采购日期',
supplier VARCHAR(100) COMMENT '供应商',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_status (status),
INDEX idx_department (department_id)
) COMMENT='设备采购申请表';
四、核心业务实现(精简代码)
1. 设备状态流转服务
@Service
@Transactional
public class DeviceStatusService {
@Autowired
private MedicalDeviceMapper deviceMapper;
@Autowired
private DeviceFlowRecordMapper flowMapper;
/**
* 设备状态流转(核心方法)
*/
public ApiResult changeDeviceStatus(Long deviceId, Integer targetStatus,
DeviceFlowRecord flowRecord) {
MedicalDevice device = deviceMapper.selectById(deviceId);
if (device == null) {
return ApiResult.error("设备不存在");
}
// 校验状态流转是否合法
if (!isValidStatusTransition(device.getCurrentStatus(), targetStatus)) {
return ApiResult.error("状态流转不合法");
}
// 记录流转历史
DeviceFlowRecord record = new DeviceFlowRecord();
BeanUtils.copyProperties(flowRecord, record);
record.setDeviceId(deviceId);
record.setRecordNo(generateFlowNo());
record.setCreatedTime(new Date());
flowMapper.insert(record);
// 更新设备状态
MedicalDevice updateDevice = new MedicalDevice();
updateDevice.setId(deviceId);
updateDevice.setCurrentStatus(targetStatus);
// 根据流转类型更新其他字段
switch (flowRecord.getFlowType()) {
case 2: // 出库领用
updateDevice.setDepartmentId(flowRecord.getToDepartmentId());
updateDevice.setUserId(flowRecord.getToUserId());
break;
case 3: // 转科
updateDevice.setDepartmentId(flowRecord.getToDepartmentId());
updateDevice.setUserId(flowRecord.getToUserId());
break;
case 5: // 报废
updateDevice.setScrapDate(new Date());
break;
}
deviceMapper.updateById(updateDevice);
return ApiResult.success("状态更新成功");
}
/**
* 校验状态流转是否合法
*/
private boolean isValidStatusTransition(Integer fromStatus, Integer toStatus) {
// 定义合法的状态流转规则
Map<Integer, List<Integer>> transitionRules = new HashMap<>();
// 库存中 → 使用中、维修中、报废中
transitionRules.put(2, Arrays.asList(3, 4, 5));
// 使用中 → 维修中、报废中、库存中(退回)
transitionRules.put(3, Arrays.asList(4, 5, 2));
// 维修中 → 使用中、报废中
transitionRules.put(4, Arrays.asList(3, 5));
List<Integer> allowedStatus = transitionRules.get(fromStatus);
return allowedStatus != null && allowedStatus.contains(toStatus);
}
/**
* 生成流转单号
*/
private String generateFlowNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dateStr = sdf.format(new Date());
String random = String.format("%04d", new Random().nextInt(10000));
return "FLOW" + dateStr + random;
}
}
2. 设备维修管理服务
@Service
public class DeviceMaintenanceService {
@Autowired
private DeviceMaintenanceMapper maintenanceMapper;
@Autowired
private DeviceStatusService statusService;
/**
* 创建维修申请
*/
@Transactional
public ApiResult createMaintenance(DeviceMaintenance maintenance) {
// 生成维修单号
maintenance.setMaintenanceNo(generateMaintenanceNo());
maintenance.setStatus(1); // 待处理
maintenance.setCreatedTime(new Date());
maintenanceMapper.insert(maintenance);
// 设备状态改为维修中
DeviceFlowRecord flowRecord = new DeviceFlowRecord();
flowRecord.setFlowType(4); // 维修流转
flowRecord.setApplyUserId(maintenance.getReporterId());
flowRecord.setApplyReason("设备维修:" + maintenance.getFaultDescription());
statusService.changeDeviceStatus(
maintenance.getDeviceId(),
4, // 维修中状态
flowRecord
);
return ApiResult.success("维修申请已提交", maintenance.getId());
}
/**
* 完成维修
*/
@Transactional
public ApiResult completeMaintenance(Long maintenanceId, String result,
BigDecimal cost, Long maintainerId) {
DeviceMaintenance maintenance = maintenanceMapper.selectById(maintenanceId);
if (maintenance == null) {
return ApiResult.error("维修记录不存在");
}
if (maintenance.getStatus() != 2) { // 不是维修中状态
return ApiResult.error("当前状态不能完成维修");
}
// 更新维修记录
maintenance.setStatus(3); // 已完成
maintenance.setMaintenanceResult(result);
maintenance.setCost(cost);
maintenance.setMaintainerId(maintainerId);
maintenance.setCompletionDate(new Date());
maintenanceMapper.updateById(maintenance);
// 设备状态改为使用中
DeviceFlowRecord flowRecord = new DeviceFlowRecord();
flowRecord.setFlowType(3); // 状态恢复
flowRecord.setApplyUserId(maintainerId);
flowRecord.setApplyReason("维修完成:" + result);
statusService.changeDeviceStatus(
maintenance.getDeviceId(),
3, // 使用中状态
flowRecord
);
return ApiResult.success("维修完成");
}
private String generateMaintenanceNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dateStr = sdf.format(new Date());
String random = String.format("%04d", new Random().nextInt(10000));
return "MNT" + dateStr + random;
}
}
3. 设备采购审批服务
@Service
public class DevicePurchaseService {
@Autowired
private DevicePurchaseApplyMapper applyMapper;
/**
* 审批采购申请
*/
@Transactional
public ApiResult approvePurchase(Long applyId, Long approverId,
boolean approved, String remark) {
DevicePurchaseApply apply = applyMapper.selectById(applyId);
if (apply == null) {
return ApiResult.error("采购申请不存在");
}
if (apply.getStatus() != 1) { // 不是待审批状态
return ApiResult.error("当前状态不能审批");
}
apply.setApproverId(approverId);
apply.setApprovalTime(new Date());
apply.setApprovalRemark(remark);
apply.setStatus(approved ? 2 : 5); // 已审批或已驳回
applyMapper.updateById(apply);
// 记录审批日志
logService.addPurchaseApproveLog(apply, approverId, approved);
return ApiResult.success(approved ? "审批通过" : "审批驳回");
}
/**
* 完成采购(创建设备)
*/
@Transactional
public ApiResult completePurchase(Long applyId, BigDecimal actualPrice,
Date purchaseDate, String supplier) {
DevicePurchaseApply apply = applyMapper.selectById(applyId);
if (apply == null || apply.getStatus() != 2) {
return ApiResult.error("采购申请不存在或未审批");
}
// 更新采购信息
apply.setActualPrice(actualPrice);
apply.setPurchaseDate(purchaseDate);
apply.setSupplier(supplier);
apply.setStatus(3); // 已采购
applyMapper.updateById(apply);
// 创建设备记录
for (int i = 0; i < apply.getQuantity(); i++) {
MedicalDevice device = new MedicalDevice();
device.setDeviceNo(generateDeviceNo());
device.setDeviceName(apply.getDeviceName());
device.setDeviceType(apply.getDeviceType());
device.setBrand(apply.getBrand());
device.setModel(apply.getModel());
device.setPurchasePrice(actualPrice);
device.setPurchaseDate(purchaseDate);
device.setCurrentStatus(1); // 采购中状态
deviceMapper.insert(device);
// 记录设备创建日志
logService.addDeviceCreateLog(device, applyId);
}
return ApiResult.success("设备已创建");
}
private String generateDeviceNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dateStr = sdf.format(new Date());
String seq = String.format("%03d", getTodayDeviceCount() + 1);
return "SB" + dateStr + seq;
}
}
五、前端核心页面(Vue精简版)
1. 设备台账管理页面
<template>
<div class="device-manage">
<!-- 搜索条件 -->
<el-card class="filter-card">
<el-form :inline="true">
<el-form-item label="设备编号">
<el-input v-model="query.deviceNo" placeholder="输入设备编号" />
</el-form-item>
<el-form-item label="设备状态">
<el-select v-model="query.status" placeholder="全部状态">
<el-option label="全部" :value="null" />
<el-option label="库存中" :value="2" />
<el-option label="使用中" :value="3" />
<el-option label="维修中" :value="4" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">查询</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 设备表格 -->
<el-table :data="deviceList" border v-loading="loading">
<el-table-column prop="deviceNo" label="设备编号" width="120" fixed />
<el-table-column prop="deviceName" label="设备名称" width="150" />
<el-table-column prop="brand" label="品牌" width="100" />
<el-table-column prop="model" label="型号" width="120" />
<el-table-column label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.currentStatus)">
{{ getStatusText(scope.row.currentStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="departmentName" label="所属科室" width="120" />
<el-table-column prop="userName" label="使用人" width="100" />
<el-table-column prop="location" label="存放位置" width="150" />
<el-table-column label="操作" width="200" fixed="right">
<template slot-scope="scope">
<el-button size="mini" @click="viewDetail(scope.row)">查看</el-button>
<el-button
size="mini"
type="primary"
v-if="scope.row.currentStatus === 2"
@click="showOutbound(scope.row)">
出库
</el-button>
<el-button
size="mini"
type="warning"
v-if="scope.row.currentStatus === 3"
@click="showMaintenance(scope.row)">
维修
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
@current-change="handlePageChange"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"
layout="total, prev, pager, next">
</el-pagination>
</div>
</template>
<script>
export default {
data() {
return {
query: {
deviceNo: '',
status: null,
departmentId: null
},
deviceList: [],
loading: false,
pagination: {
current: 1,
size: 15,
total: 0
}
}
},
methods: {
getStatusType(status) {
const types = {
1: 'info', // 采购中
2: '', // 库存中
3: 'success', // 使用中
4: 'warning', // 维修中
5: 'danger' // 报废中
}
return types[status] || ''
},
getStatusText(status) {
const texts = {
1: '采购中',
2: '库存中',
3: '使用中',
4: '维修中',
5: '报废中'
}
return texts[status] || '未知'
},
async search() {
this.loading = true
try {
const params = {
...this.query,
page: this.pagination.current,
size: this.pagination.size
}
const res = await this.$api.device.getDeviceList(params)
this.deviceList = res.data.records
this.pagination.total = res.data.total
} catch (error) {
this.$message.error('查询失败')
} finally {
this.loading = false
}
},
showOutbound(device) {
this.$router.push({
path: '/device/outbound',
query: { deviceId: device.id }
})
},
showMaintenance(device) {
this.$router.push({
path: '/maintenance/create',
query: { deviceId: device.id }
})
}
}
}
</script>
2. 设备出库申请页面
<template>
<div class="outbound-apply">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item label="设备信息" prop="deviceId">
<el-input :value="deviceInfo" disabled />
</el-form-item>
<el-form-item label="领用科室" prop="toDepartmentId">
<el-select v-model="form.toDepartmentId" placeholder="请选择科室">
<el-option
v-for="dept in departmentList"
:key="dept.id"
:label="dept.name"
:value="dept.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="使用人" prop="toUserId">
<el-select v-model="form.toUserId" placeholder="请选择使用人">
<el-option
v-for="user in userList"
:key="user.id"
:label="user.name"
:value="user.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="领用事由" prop="applyReason">
<el-input
v-model="form.applyReason"
type="textarea"
:rows="3"
placeholder="请填写领用事由" />
</el-form-item>
<el-form-item label="存放位置" prop="location">
<el-input v-model="form.location" placeholder="请输入存放位置" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">提交申请</el-button>
<el-button @click="cancel">取消</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
form: {
deviceId: null,
toDepartmentId: null,
toUserId: null,
applyReason: '',
location: ''
},
rules: {
toDepartmentId: [
{ required: true, message: '请选择领用科室' }
],
applyReason: [
{ required: true, message: '请填写领用事由' }
]
},
deviceInfo: '',
departmentList: [],
userList: []
}
},
created() {
this.form.deviceId = this.$route.query.deviceId
this.loadDeviceInfo()
this.loadDepartments()
},
methods: {
async loadDeviceInfo() {
const res = await this.$api.device.getDeviceDetail(this.form.deviceId)
this.deviceInfo = `${res.data.deviceNo} - ${res.data.deviceName}`
},
async loadDepartments() {
const res = await this.$api.department.getAll()
this.departmentList = res.data
},
async submit() {
try {
await this.$refs.formRef.validate()
const loading = this.$loading({ lock: true, text: '提交中...' })
await this.$api.device.applyOutbound(this.form)
loading.close()
this.$message.success('出库申请已提交')
this.$router.push('/device/manage')
} catch (error) {
this.$message.error('提交失败')
}
},
cancel() {
this.$router.go(-1)
}
}
}
</script>
六、系统测试要点
1. 核心功能测试
| 测试场景 | 测试要点 | 预期结果 |
|---|---|---|
| 设备状态流转 | 测试各种状态流转路径 | 状态流转符合业务规则 |
| 审批流程 | 测试采购、领用、转科审批 | 审批流程完整,状态正确更新 |
| 库存管理 | 测试入库、出库、库存预警 | 库存数量准确,预警及时 |
| 维修管理 | 测试维修申请、维修完成 | 维修记录完整,设备状态更新 |
2. 数据一致性验证
- 设备状态一致性:设备状态与流转记录保持一致
- 库存数量一致性:出入库操作后库存数量准确
- 审批流程一致性:审批状态与实际操作保持一致
七、答辩准备要点
1. 演示流程(5分钟)
-
设备采购流程(1分钟)
- 科室提交采购申请
- 设备科审批采购
- 创建设备台账
-
设备领用流程(2分钟)
- 科室申请设备领用
- 设备科审批出库
- 设备状态更新为使用中
-
设备维修流程(2分钟)
- 科室提交维修申请
- 维修科处理维修
- 维修完成设备恢复使用
2. 重点问题准备
Q: 如何保证设备数据的准确性? A: 三重保障机制:
- 业务流程闭环:每个操作都有记录和审批
- 状态机控制:限制非法状态流转
- 定期盘点:定期与实际设备核对
Q: 系统如何处理设备维保提醒? A: 智能提醒机制:
- 定期保养提醒:基于上次保养日期自动计算
- 保修期提醒:提前30天提醒保修到期
- 使用年限提醒:接近使用年限时提醒
Q: 系统如何支持设备溯源? A: 完整生命周期记录:
- 采购源头:记录供应商、采购信息
- 使用历史:记录所有使用科室和使用人
- 维保记录:记录所有维修和保养历史
3. 创新点展示
- 全生命周期管理:覆盖采购、使用、维保、报废全过程
- 智能状态机:防止非法状态流转
- 移动端支持:支持手机扫码快速盘点
结语
医院医疗设备管理系统的核心在于全生命周期管理和业务流程规范化。开发时需重点关注:
- 状态流转设计:设计合理的状态机模型
- 审批流程设计:支持多级审批和退回机制
- 数据追溯能力:完整记录设备所有操作历史
避坑提醒:
- 前期必须设计完整的状态流转图
- 审批流程要考虑退回和重新提交场景
- 设备编号规则要支持医院实际需求
按照这个指南开发,你的医院医疗设备管理系统毕设一定能顺利通过!