作为一名长期从事医疗信息化系统开发的工程师,我最近完成了一个基于SpringBoot+Vue3的宠物医院信息管理系统项目。这个系统的诞生源于当前宠物医疗行业数字化转型的迫切需求。
传统宠物医院普遍面临以下痛点:
我们的系统正是为了解决这些问题而设计。通过实际调研发现,一家中型宠物医院(日均接诊30-50例)在使用本系统后:
系统采用经典的前后端分离架构:
code复制[前端Vue3] ←HTTP→ [SpringBoot后端] ←JDBC→ [MySQL数据库]
↑
↓
[Redis缓存]
这种架构的优势在于:
SpringBoot 2.7.x 作为核心框架,主要考虑:
MyBatis-Plus 3.5.x 作为ORM框架:
JWT 用于认证授权:
Vue3 + 组合式API 带来:
Pinia 替代Vuex的优势:
Element Plus 组件库:
数据库设计关键表:
sql复制CREATE TABLE `pet` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`owner_id` bigint NOT NULL,
`species` varchar(20) NOT NULL,
`breed` varchar(50) DEFAULT NULL,
`birth_date` date DEFAULT NULL,
`weight` decimal(5,2) DEFAULT NULL,
`chip_number` varchar(50) DEFAULT NULL,
`medical_history` text,
PRIMARY KEY (`id`),
KEY `idx_owner` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
后端接口设计示例:
java复制@RestController
@RequestMapping("/api/pet")
public class PetController {
@Autowired
private PetService petService;
@PostMapping
public Result addPet(@RequestBody PetDTO petDTO) {
return petService.addPet(petDTO);
}
@GetMapping("/{id}")
public Result getPet(@PathVariable Long id) {
return petService.getPetById(id);
}
@GetMapping("/owner/{ownerId}")
public Result listByOwner(@PathVariable Long ownerId) {
return petService.listByOwner(ownerId);
}
}
前端实现关键点:
排班表设计考虑:
预约状态机设计:
code复制[待支付] → [已预约] → [已完成]
↓
[已取消]
关键业务逻辑:
java复制public Result makeAppointment(AppointmentDTO dto) {
// 1. 检查时段是否可约
if (!scheduleService.isAvailable(dto.getScheduleId())) {
return Result.error("该时段已约满");
}
// 2. 检查宠物是否已有未完成预约
if (appointmentService.hasUnfinished(dto.getPetId())) {
return Result.error("该宠物已有未完成预约");
}
// 3. 创建预约记录
return appointmentService.create(dto);
}
电子病历结构设计:
药品库存管理实现:
java复制@Transactional
public Result prescribeMedicine(Long recordId, List<PrescriptionItem> items) {
// 1. 扣减库存
for (PrescriptionItem item : items) {
int affected = medicineMapper.reduceStock(
item.getMedicineId(),
item.getQuantity());
if (affected == 0) {
throw new BusinessException("药品库存不足");
}
}
// 2. 保存处方记录
prescriptionService.save(recordId, items);
return Result.success();
}
缓存场景选择:
典型缓存实现:
java复制@Cacheable(value = "schedule", key = "#doctorId+'_'+#date")
public ScheduleVO getSchedule(Long doctorId, LocalDate date) {
// 数据库查询逻辑
}
@CacheEvict(value = "schedule", key = "#doctorId+'_'+#date")
public void updateSchedule(Long doctorId, LocalDate date) {
// 更新逻辑
}
javascript复制const routes = [
{
path: '/records',
component: () => import('@/views/RecordList.vue')
}
]
html复制<el-image
lazy
:src="pet.avatar"
:preview-src-list="[pet.avatar]">
</el-image>
javascript复制import { throttle } from 'lodash-es'
const search = throttle(() => {
loadData()
}, 500)
采用RBAC(基于角色的访问控制):
code复制权限 → 角色 → 用户
权限注解示例:
java复制@PreAuthorize("hasRole('DOCTOR')")
@PostMapping("/records")
public Result addRecord(@RequestBody MedicalRecordDTO dto) {
// ...
}
java复制@Column
@Convert(converter = CryptoConverter.class)
private String ownerPhone;
java复制@Aspect
@Component
public class AuditLogAspect {
@AfterReturning(
pointcut = "@annotation(com.example.audit.OperateLog)",
returning = "result")
public void afterReturning(JoinPoint jp, Object result) {
// 记录操作日志
}
}
推荐部署方案:
Dockerfile示例:
dockerfile复制FROM openjdk:11-jre
COPY target/pet-hospital.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
关键监控指标:
现象:同一时段被重复预约
解决方案:
java复制@Transactional
public synchronized Result makeAppointment(AppointmentDTO dto) {
// 添加SELECT FOR UPDATE锁
Schedule schedule = scheduleMapper.selectForUpdate(dto.getScheduleId());
if (schedule.getRemain() <= 0) {
return Result.error("该时段已约满");
}
// 后续处理...
}
常见场景:
解决方案:
javascript复制onBeforeUnmount(() => {
clearInterval(timer)
eventBus.off('someEvent', handler)
})
现象:查询结果不符合预期
解决方案:
xml复制<select id="getById" useCache="true">...</select>
<insert id="add" flushCache="true">...</insert>
java复制sqlSession.clearCache();
技术预研:
在实际开发过程中,最大的收获是认识到良好的领域建模的重要性。初期因为对宠物医疗业务流程理解不够深入,导致部分表结构设计不合理,后期不得不进行多次调整。建议开发类似系统的同行,一定要先花足够时间进行业务调研,绘制完整的业务流程图和状态机图,这能为后续开发节省大量时间。