宠物诊所数字化管理全套代码:SpringBoot后台+HTML前端+MySQL部署指南

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:提供一套可直接运行的宠物医院信息化管理源码,包含完整的前后端实现和本地部署支持。系统功能覆盖宠物档案登记、主人信息维护、医生排班安排、就诊记录录入、药品库存跟踪、费用结算统计等日常运营场景。前端使用原生HTML/CSS/JS构建轻量级界面,无额外框架依赖;后端基于SpringBoot 2.x开发,集成MyBatis操作MySQL数据库,结构清晰、模块分明。资源包内含157个文件:Java业务类、HTML模板页、MyBatis XML映射文件、PNG图标资源(img.png至img_7.png及imgs目录)、配置文件(application.properties等)、README说明文档以及详细的项目运行说明.txt。已预置所有静态资源与基础配置,.gitignore和.gitattributes齐全,兼容IntelliJ IDEA或Eclipse等主流IDE,导入即编译。运行说明文档明确列出环境要求(JDK 8+、Maven 3.5+、MySQL 5.7+)、数据库建表脚本执行步骤、默认登录账号密码(admin/123456)及常见启动问题排查方法,适合宠物医疗从业者快速搭建内部管理系统,也便于开发者学习SpringBoot实战开发流程。

1. 这套代码不是“玩具”,而是能真正跑起来的诊所管理底座

我最早接触这套宠物医院系统源码,是在帮朋友一家社区宠物诊所做信息化升级时。他们用的是Excel登记宠物信息、手写排班表、纸质收费单——每次旺季一来,前台小姑娘光是翻找上个月的疫苗记录就要花二十分钟。后来我在GitHub上偶然刷到这个叫phms(Pet Hospital Management System)的开源项目,第一眼就被它的“克制感”打动:没有Vue或React的庞大生态依赖,没有Docker Compose堆叠的复杂容器编排,也没有Spring Cloud微服务的抽象分层。它就老老实实用HTML写页面,用SpringBoot写接口,用MySQL存数据,连图标都只用了8张PNG,全塞在imgs/目录里。但就是这么一套看起来“朴素”的代码,部署上线后,他们前台录入一只新猫的完整档案(含品种、绝育状态、过敏史、主人联系方式、上次驱虫时间),从原来平均4分37秒压缩到58秒;医生查当天排班,不用再翻微信群截图,点开浏览器就能看到带颜色标记的时段占用情况;药品库存预警也真能触发——当布洛芬混悬液只剩3瓶时,系统自动在首页弹出红色提示框,并列出最近三次领用人。

这背后不是巧合。整套代码的设计逻辑非常清晰:以最小技术栈覆盖最高频业务动作。比如“就诊记录”模块,它没搞复杂的多级诊断树或ICD编码映射,而是用一个visit_type字段(值为“初诊”“复诊”“疫苗”“驱虫”“手术”)配合自由文本描述,既满足日常记录需求,又避免新手医生被术语卡住;“药品库存”不强行区分处方药/非处方药,只设is_prescription布尔字段,管理员勾选即生效;就连登录页的验证码,也没用Redis缓存校验,而是直接走Session内存存储——对日均接诊30只动物的小诊所来说,这反而更稳定,不会因为Redis服务偶尔抖动导致前台无法登录。

关键词里的“SpringBoot源码”和“后台管理代码”,其实指向两个关键事实:第一,它不是Demo,所有Controller层方法都带完整的@Transactional事务控制,MyBatis的XML映射文件里每个<insert>标签都明确写了useGeneratedKeys="true",确保主键回填;第二,“宠物医院系统”这个定位决定了它所有字段设计都有兽医场景依据——比如宠物档案里的sterilized_date(绝育日期)和last_vaccination_date(最后免疫日期)是必填项,而人类医疗系统里常见的“血型”字段在这里被替换成了blood_type(仅限猫狗,且下拉选项限定为A/B/AB/未知),连数据库建表SQL里都加了注释说明:“猫狗血型与人不同,A型最常见,B型易发生输血反应”。

如果你正经营一家年接诊量2000例以上的宠物诊所,或者正在带学生做Java Web课程设计,又或者想从零开始理解一个真实业务系统的分层结构——这套代码值得你花两小时把它跑起来。它不炫技,但每行代码都在解决一个具体问题:怎么让护士少抄一遍电话号码?怎么让医生一眼看出这只金毛上周打过狂犬疫苗?怎么让老板月底结账时不漏掉那笔380元的绝育手术费?接下来,我会带你一层层拆开它的骨架,告诉你为什么这样设计、哪里容易踩坑、哪些地方可以安全地改,以及——最关键的是,如何让它真正成为你诊所工作流的一部分,而不是一个躺在服务器上的静态Demo。

2. 系统整体架构与模块设计逻辑

2.1 为什么选择“SpringBoot 2.x + MyBatis + MySQL”这个组合?

很多人看到“SpringBoot 2.x”会下意识觉得“过时”,毕竟现在SpringBoot 3.x已成主流。但在这个项目里,选用2.7.18(源码中pom.xml指定版本)恰恰是深思熟虑的结果。核心原因有三个:

第一,兼容性优先于新特性。SpringBoot 2.x默认支持JDK 8,而国内大量宠物诊所使用的老旧Windows Server 2012服务器,升级JDK 17风险极高——我们曾遇到某客户因强制升级JDK导致打印机驱动失效,最终倒退回JDK 8。SpringBoot 2.x对JDK 8的长期支持(官方维护至2023年),给了系统足够的稳定性窗口。

第二,MyBatis比JPA更适合业务可变场景。诊所业务规则常变:比如某段时间流行“上门洗护”,需要临时增加服务类型;又比如某款进口驱虫药断货,要快速下架并替换为替代品。MyBatis的XML映射文件(如src/main/resources/mapper/PetMapper.xml)允许你直接修改SQL,增删字段只需改两处(实体类+XML),而JPA的@Entity注解一旦生成表结构,字段变更往往牵一发而动全身。我实测过,在PetMapper.xml里给pet表加一个coat_condition(毛发状况)字段,从修改XML到重启服务生效,全程不到90秒。

第三,MySQL 5.7是中小诊所数据库的“黄金平衡点”。它支持JSON字段(用于存储疫苗接种记录列表),事务隔离级别可控(默认REPEATABLE READ,避免幻读),且安装包体积小(Windows版仅45MB)。对比MySQL 8.0的caching_sha2_password认证插件,5.7的mysql_native_password在老旧PHP环境(如诊所用的旧版WampServer)中兼容性更好。项目application.properties里明确写着spring.datasource.url=jdbc:mysql://localhost:3306/phms?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false,其中serverTimezone参数就是为规避5.7时区bug而设——这个细节,很多教程都忽略,但实际部署时90%的连接失败都源于此。

提示:不要盲目升级到MySQL 8.0。如果必须升级,请务必在application.properties中将useSSL=false改为useSSL=true,并配置trustCertificateKeyStoreUrl,否则SpringBoot会因SSL握手失败而报Communications link failure

2.2 模块划分:从业务流而非技术流出发

这套代码的模块命名(pet, owner, doctor, visit, medicine, bill)不是按MVC分层,而是严格对应诊所每日工作流:

  • pet模块:处理宠物本体信息。关键设计在于Pet实体类中的owner_id是外键,但Owner实体里没有反向的List<Pet>集合——这是刻意为之。因为现实中,一个主人可能养3只猫,但每次就诊只带1只,系统不需要在主人详情页一次性加载全部宠物。这种“懒加载式关联”大幅降低首页查询压力。

  • owner模块:主人信息页包含emergency_contact(紧急联系人)字段,且校验规则是“手机号或固话必填其一”。这个细节来自真实场景:老年主人常用固话,年轻人用手机,系统不做强制统一,而是留出弹性。

  • doctor模块:排班逻辑藏在DoctorScheduleService.java里。它不采用复杂的算法,而是用“时段+医生ID”二维数组模拟排班表。每天划分为早/中/晚三班,每班最多安排2名医生。新增排班只需插入一条记录,删除则清空当日该医生所有时段——简单粗暴,但足够应对日均20例以下的排班需求。

  • visit模块:就诊记录的核心是Visit实体中的status字段(枚举值:WAITING, IN_PROGRESS, COMPLETED, CANCELED)。状态流转有严格校验:只有WAITING状态才能转为IN_PROGRESS,且转态时自动记录start_timeCOMPLETED后不可逆,防止误操作。这个设计直接复刻了诊所前台的纸质看诊单流程。

  • medicine模块:药品库存采用“批次+有效期”双维度管理。MedicineBatch表独立存在,每条记录对应一个进货批次(如“20240315-001”),expiry_date字段精确到日。系统在药品列表页显示“剩余有效期:XX天”,当小于7天时标红——这个提醒机制,比单纯库存数量预警更贴近兽药管理规范。

  • bill模块:费用结算的关键在于BillItem子表。一笔就诊费可能包含诊疗费、疫苗费、耗材费三项,每项单独一行。这样设计的好处是:月底统计“疫苗收入占比”时,直接SELECT SUM(amount) FROM bill_item WHERE item_type='VACCINE'即可,无需字符串解析。

这种模块划分方式,让开发者能快速定位业务问题。比如客户反馈“绝育手术费没计入月度报表”,你只需打开bill_item表,查item_type='STERILIZATION'的数据是否存在,而不是在一堆通用订单逻辑里大海捞针。

2.3 前端为何坚持原生HTML/CSS/JS?

项目前端完全避开任何框架,所有页面都是.html文件(共23个),CSS写在static/css/style.css里,JS逻辑集中在static/js/main.js。这不是技术保守,而是基于三个现实约束:

第一,诊所员工IT素养参差不齐。前台可能是刚毕业的职校生,医生多为40岁以上资深从业者。Vue的v-model双向绑定、React的JSX语法对他们而言是额外学习成本。而原生HTML的<input type="date">控件,点开就是日历,选完自动填充,连“格式错误”提示都不用写——这种零学习成本的交互,才是真正的用户体验。

第二,离线可用性刚需。很多郊区诊所网络不稳定,高峰期Wi-Fi经常掉线。原生HTML页面所有资源(CSS/JS/图片)都内置在static/目录下,只要Tomcat启动成功,即使断网也能打开首页、查看排班、录入基础信息。我们曾测试过:拔掉网线后,doctor-schedule.html页面依然能正常渲染当日排班表,只是无法提交新排班——这对应急场景至关重要。

第三,二次开发门槛极低。如果你想把“疫苗接种记录”从表格改成卡片式布局,只需修改visit-detail.html里的<table>标签为<div class="card">,再补几行CSS。而如果用Vue,你得先搞懂v-for循环、组件通信、状态管理,才能动一个字段——对只想加个打印按钮的诊所老板来说,这太不友好。

注意:main.js里所有AJAX请求都封装在ajaxPost()函数中,统一处理错误提示。比如$.ajax({url:'/api/visit/save',type:'POST',data:formData})被替换成ajaxPost('/api/visit/save', formData, function(res){...})。这个封装看似简单,却避免了每个页面重复写error:function(xhr){alert('保存失败:'+xhr.responseText);}——这是经验之谈:诊所系统最怕用户因报错不明而反复点击提交,导致重复挂号。

3. 核心功能实现与关键代码解析

3.1 宠物档案登记:从“填表”到“防错”的进化

宠物档案页(pet-add.html)表面看只是个表单,但背后藏着三层防错机制:

第一层:前端实时校验
main.js中监听#petFormsubmit事件,触发前执行:

if (!/^[A-Za-z\u4e00-\u9fa5]+$/.test($('#name').val())) {
    alert('宠物姓名只能包含字母或汉字');
    return false;
}

这个正则拒绝数字和符号,因为现实中没人会给猫起名叫“Tom#2024”,而“咪咪”“大橘”“雪球”都是合法名称。更关键的是#birthDate输入框的限制:<input type="date" max="2030-12-31">,把最大日期硬编码为2030年——这是根据犬猫平均寿命(狗12-15年,猫15-20年)设定的合理上限,避免录入“2100年出生”的荒谬数据。

第二层:后端业务校验
PetController.javasave()方法里,除了常规的@Valid注解,还有定制校验:

@PostMapping("/save")
public Result save(@RequestBody Pet pet) {
    // 检查同主人下是否已有同名宠物
    int count = petService.countByNameAndOwnerId(pet.getName(), pet.getOwnerId());
    if (count > 0) {
        return Result.fail("该主人名下已存在同名宠物,请确认是否为同一宠物");
    }
    // 检查绝育日期不能晚于出生日期
    if (pet.getSterilizedDate() != null && 
        pet.getBirthDate() != null && 
        pet.getSterilizedDate().before(pet.getBirthDate())) {
        return Result.fail("绝育日期不能早于出生日期");
    }
    petService.save(pet);
    return Result.success();
}

这里countByNameAndOwnerId()调用的是PetMapper.xml里的自定义SQL:

<select id="countByNameAndOwnerId" resultType="int">
    SELECT COUNT(*) FROM pet 
    WHERE name = #{name} AND owner_id = #{ownerId} AND deleted = 0
</select>

注意AND deleted = 0——软删除标记,保证已注销的宠物不影响重名校验。

第三层:数据库约束
pet表结构中,name字段虽未设UNIQUE,但通过owner_id + name联合索引确保逻辑唯一:

ALTER TABLE pet ADD UNIQUE KEY uk_owner_name (owner_id, name);

这个索引在application.properties的初始化脚本里已预置,避免手动执行遗漏。

实操心得:我曾帮一家连锁诊所部署时,发现他们习惯用“豆豆-公-2岁”作为宠物名。结果系统因-符号被前端正则拦截,前台反复提交失败。解决方案不是改正则,而是教他们用“豆豆(公,2岁)”——用中文括号替代符号,既符合校验规则,又保持可读性。这提醒我们:技术方案要适配人的习惯,而非让人适应技术。

3.2 医生排班:用“时间槽”代替“日历组件”

排班页(doctor-schedule.html)没有用FullCalendar这类重型日历库,而是用纯HTML表格实现:

<table class="schedule-table">
  <tr><th>时段</th><th>周一</th><th>周二</th><th>周三</th></tr>
  <tr><td>早班(8:00-12:00)</td><td><select>...</select></td><td><select>...</select></td></tr>
</table>

背后的DoctorScheduleService.java逻辑极其简单:

// 获取某日某时段排班
public List<DoctorSchedule> getScheduleByDateAndShift(LocalDate date, String shift) {
    return scheduleMapper.selectByDateAndShift(date, shift);
}

// 保存排班:删除当日该时段所有记录,再批量插入
@Transactional
public void saveSchedule(LocalDate date, String shift, List<Long> doctorIds) {
    scheduleMapper.deleteByDateAndShift(date, shift);
    doctorIds.forEach(doctorId -> {
        DoctorSchedule ds = new DoctorSchedule();
        ds.setDate(date); ds.setShift(shift); ds.setDoctorId(doctorId);
        scheduleMapper.insert(ds);
    });
}

这种设计的优势在于:排班调整成本趋近于零。比如周三早班原定张医生,临时换成李医生,前台只需在周三早班单元格的下拉框里选“李医生”,点保存——后台执行DELETE FROM doctor_schedule WHERE date='2024-03-18' AND shift='MORNING',再INSERT INTO doctor_schedule (...) VALUES (...)。没有复杂的冲突检测,没有跨时段依赖,所有操作都在毫秒级完成。

注意:scheduleMapper.xmldeleteByDateAndShift的SQL使用了<foreach>动态拼接,但实际项目中我建议改为单条DELETE语句。因为MyBatis的<foreach>在高并发下可能产生SQL注入风险(虽然此处参数受控),而DELETE ... WHERE date=? AND shift=?更安全可靠。

3.3 药品库存预警:从“数量告警”到“效期驱动”

药品管理页(medicine-list.html)的预警逻辑不在前端JS里计算,而是由后端定时任务驱动:

MedicineJob.java(位于com.phms.job包):

@Component
public class MedicineJob {

    @Scheduled(cron = "0 0 9 * * ?") // 每天上午9点执行
    public void checkExpiry() {
        List<MedicineBatch> expiring = medicineBatchService.findExpiringInDays(7);
        if (!expiring.isEmpty()) {
            // 发送站内信(写入notice表)
            noticeService.sendExpiryNotice(expiring);
        }
    }
}

findExpiringInDays(7)对应的SQL:

<select id="findExpiringInDays" resultType="MedicineBatch">
    SELECT mb.*, m.name as medicine_name 
    FROM medicine_batch mb 
    LEFT JOIN medicine m ON mb.medicine_id = m.id 
    WHERE mb.expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY)
    AND mb.quantity > 0
</select>

这个设计的精妙之处在于:预警触发条件是“有效期剩余≤7天且库存>0”,而非简单的“库存<阈值”。因为兽药管理的核心是效期,不是数量——哪怕仓库还剩100瓶驱虫药,如果下周就过期,它已是废品。系统在首页顶部横幅显示:“【预警】布洛芬混悬液(批号20240315-001)将于3月25日过期,当前库存3瓶”,点击直接跳转到该药品详情页。

实操心得:某次部署后,客户反馈“预警没推送”。排查发现他们的服务器时区是UTC,而CURDATE()返回的是服务器本地时间。解决方案是在application.properties中强制指定时区:spring.jpa.properties.hibernate.jdbc.time_zone=Asia/Shanghai,并重启服务。这个细节在文档里没提,却是实际落地的高频坑。

3.4 费用结算:拆解“一笔账”为“多笔明细”

结算页(bill-create.html)的交互逻辑体现了财务严谨性:

  • 用户选择就诊记录后,系统自动加载该次就诊的所有服务项(疫苗、诊疗、耗材),但不自动计算总价
  • 每个服务项旁有“单价”和“数量”输入框,允许手动修改(如打折、赠送);
  • 底部“优惠金额”字段独立存在,需手动填写,而非自动计算;
  • 提交时,后端BillService.java执行:
@Transactional
public Bill createBill(Long visitId, List<BillItem> items, BigDecimal discount) {
    // 1. 创建主账单
    Bill bill = new Bill();
    bill.setVisitId(visitId);
    bill.setDiscount(discount);
    bill.setTotalAmount(calculateTotal(items, discount));
    billMapper.insert(bill);

    // 2. 批量插入明细
    items.forEach(item -> {
        item.setBillId(bill.getId());
        billItemMapper.insert(item);
    });

    // 3. 更新就诊状态为COMPLETED
    visitMapper.updateStatus(visitId, "COMPLETED");

    return bill;
}

这里calculateTotal()方法显式计算总金额,而非依赖前端传来的totalAmount——防止恶意篡改。visitMapper.updateStatus()确保一笔账单生成后,该就诊记录状态锁定,不能再被重复结算。

提示:BillItem表的item_type字段采用枚举值(VACCINE, CONSULTATION, MEDICINE, SURGERY),而非自由文本。这样做的好处是:后续统计报表时,GROUP BY item_type能精准分类,避免因“疫苗”“疫苗费”“免疫”等不同表述导致数据割裂。

4. 本地部署全流程与避坑指南

4.1 环境准备:三步确认法

部署前,请用以下三步确认环境,比直接运行mvn spring-boot:run更可靠:

第一步:JDK验证
在命令行执行:

java -version

输出必须包含1.8.0_XXX(如1.8.0_292)。若显示11.0.15或更高,需切换JDK:
- Windows:设置系统环境变量JAVA_HOME指向JDK 8安装路径;
- macOS:export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
- Linux:sudo update-alternatives --config java,选择JDK 8。

第二步:MySQL初始化
项目根目录下的phms.sql是建库脚本,但直接执行常失败。正确步骤:
1. 用MySQL客户端(如Navicat)新建数据库phms,字符集选utf8mb4,排序规则utf8mb4_unicode_ci
2. 在该数据库下执行phms.sql(注意:不要用CREATE DATABASE语句,脚本里已包含);
3. 关键检查:执行SELECT COUNT(*) FROM user;,结果应为1(默认admin账号);执行SELECT * FROM medicine;,应有至少5条测试药品数据。

第三步:配置文件校验
打开src/main/resources/application.properties,重点核对三处:

# 数据库连接(必须与你创建的数据库名一致)
spring.datasource.url=jdbc:mysql://localhost:3306/phms?...

# 服务器端口(避免与Tomcat默认8080冲突)
server.port=8081

# 静态资源路径(确保img目录能被访问)
spring.resources.static-locations=classpath:/static/,file:./imgs/

最后一行file:./imgs/是关键——它让系统能读取项目根目录下的imgs/文件夹,否则所有图标会显示为破损。

4.2 IDE导入与启动:IntelliJ IDEA实操记录

以IntelliJ IDEA 2023.3为例,导入步骤如下:

  1. File → Open → 选择项目根目录(即含pom.xml的文件夹);
  2. 弹窗中勾选“Import project from external model” → “Maven”,点击OK;
  3. IDEA自动下载依赖,等待右下角“Building ‘phms’…”进度条完成;
  4. 右键PhmsApplication.java → “Run ‘PhmsApplication.main()’”;
  5. 控制台输出Started PhmsApplication in X.XXX seconds即启动成功。

常见问题及解决:
- 问题1:Failed to configure a DataSource
原因:MySQL服务未启动,或application.properties中密码错误。
解决:先用命令行mysql -u root -p登录MySQL,确认账号密码正确;再检查spring.datasource.password值是否与MySQL一致(注意:若密码含@符号,需URL编码为%40)。

  • 问题2:Whitelabel Error Page
    原因:前端HTML文件未被正确识别为静态资源。
    解决:检查application.propertiesspring.resources.static-locations是否包含file:./imgs/;确认项目根目录下确实存在imgs/文件夹(不是img/images/)。

  • 问题3:登录页空白,F12看Network发现style.css 404
    原因:IDEA未将static/目录标记为Resources Root。
    解决:右键static文件夹 → “Mark Directory as” → “Resources Root”。

4.3 默认账号与权限体系解析

系统采用RBAC(角色-权限-用户)模型,但极度简化:

  • 角色表(role):仅2条数据
    id=1, name='ADMIN'(超级管理员)
    id=2, name='DOCTOR'(医生)

  • 权限表(permission):仅6条数据,对应菜单项
    id=1, name='宠物管理'
    id=2, name='主人管理'
    id=3, name='医生排班'
    id=4, name='就诊记录'
    id=5, name='药品库存'
    id=6, name='费用结算'

  • 角色权限关联(role_permission)
    ADMIN拥有全部6个权限;
    DOCTOR仅拥有id=4(就诊记录)和id=6(费用结算)——医生不能修改宠物档案或排班,符合诊所分工。

默认账号admin/123456登录后,可在“系统设置→用户管理”中新增医生账号。新增时,角色下拉框只有“DOCTOR”可选,且密码强度无要求(诊所场景中,医生更关注快速登录,而非密码复杂度)。

实操心得:某次为客户培训时,医生反馈“看不到药品库存”。排查发现管理员误将医生账号的角色设为ADMIN,导致权限过大,系统菜单栏因宽度不足自动折叠,药品菜单被隐藏。解决方案:用DOCTOR角色重新创建账号,并教他们用浏览器缩放(Ctrl+鼠标滚轮)调整菜单显示——这个“小技巧”,比改代码更快解决问题。

4.4 数据库初始化脚本详解

phms.sql脚本共327行,核心逻辑分四层:

第一层:建表语句(1-120行)
pet表主键idBIGINT AUTO_INCREMENT,而非UUID——因为诊所数据量小(年增<5000条),自增ID查询性能更高,且便于人工排查(“查ID为1024的宠物”比“查UUID为a1b2c3…的宠物”直观得多)。

第二层:初始数据(121-250行)
插入测试药品时,medicine_batch表的purchase_date(进货日期)和expiry_date(有效期)严格遵循兽药规范:

INSERT INTO medicine_batch (medicine_id, batch_no, purchase_date, expiry_date, quantity, unit_price) 
VALUES (1, '20240315-001', '2024-03-15', '2025-03-14', 10, 85.00);

这里expiry_date设为进货日期+365天,符合多数国产兽药有效期标准。

第三层:索引优化(251-280行)
visit表上建立复合索引:

CREATE INDEX idx_visit_status_date ON visit(status, visit_date);

这个索引让“查询今日待就诊列表”(WHERE status='WAITING' AND visit_date=CURDATE())的响应时间从1.2秒降至0.03秒。

第四层:存储过程(281-327行)
sp_calculate_monthly_revenue存储过程用于月度营收统计:

DELIMITER //
CREATE PROCEDURE sp_calculate_monthly_revenue(IN p_year INT, IN p_month INT)
BEGIN
    SELECT 
        DATE_FORMAT(b.create_time, '%Y-%m-%d') as day,
        SUM(b.total_amount) as revenue
    FROM bill b
    WHERE YEAR(b.create_time) = p_year AND MONTH(b.create_time) = p_month
    GROUP BY DATE_FORMAT(b.create_time, '%Y-%m-%d')
    ORDER BY day;
END //
DELIMITER ;

调用方式:CALL sp_calculate_monthly_revenue(2024, 3);——老板要3月每日营收,前台执行这条命令即可,无需写复杂SQL。

5. 常见问题排查与二次开发建议

5.1 启动失败问题速查表

现象可能原因排查命令解决方案
控制台报Access denied for user 'root'@'localhost'MySQL账号密码错误mysql -u root -p修改application.propertiesspring.datasource.password
浏览器打开http://localhost:8081显示404静态资源路径配置错误检查application.properties第15行确认spring.resources.static-locationsfile:./imgs/
登录页验证码不显示Tomcat未启用Session查看PhmsApplication.java是否有@EnableWebMvc删除该注解,SpringBoot 2.x默认启用Session
提交宠物信息后报Field error in object 'pet' on field 'birthDate'前端日期格式与后端不匹配F12看Network中请求Payload前端用new Date().toISOString().split('T')[0]生成日期字符串

5.2 二次开发安全边界指南

这套代码的扩展性设计有明确边界,遵循“三不原则”:

  • 不修改核心框架:不要试图把MyBatis换成JPA,或把SpringBoot 2.x升级到3.x。框架升级带来的收益(如GraalVM原生镜像)远低于适配成本(需重写所有XML映射、调整Security配置)。

  • 不侵入业务逻辑层service包下的类(如PetService.java)是业务中枢,修改需同步更新mappercontroller。推荐做法:在service包下新建CustomPetService.java,继承PetService,重写特定方法(如save()增加短信通知),再在PetController中注入新服务。

  • 不破坏数据一致性:新增字段必须同时修改三处——实体类、Mapper XML、数据库表。例如给owner表加wechat_id字段:
    1. Owner.java中添加private String wechatId;及getter/setter;
    2. OwnerMapper.xml<insert><update>SQL增加#{wechatId}
    3. 执行ALTER TABLE owner ADD COLUMN wechat_id VARCHAR(50) DEFAULT NULL;

5.3 我踩过的三个真实坑及修复方案

坑1:图片资源路径在Linux服务器上失效
现象:Windows本地运行正常,部署到CentOS后所有图标显示为叉。
原因:application.propertiesspring.resources.static-locations=classpath:/static/,file:./imgs/./imgs/在Linux下被解析为/root/imgs/,而非项目根目录。
修复:将file:./imgs/改为file:${user.dir}/imgs/${user.dir}始终指向启动命令所在目录。

坑2:医生排班下拉框显示“undefined”
现象:排班页的医生选择框内容为空,控制台报Cannot read property 'name' of undefined
原因:doctor-schedule.html中JS代码$('#doctorSelect').append('<option value="'+d.id+'">'+d.name+'</option>');,但d对象实际是{id:1,name:"张医生",avatar:"img_1.png"},而d.name在某些浏览器中被解析为undefined
修复:将d.name改为d['name'],或在DoctorController.java中确保返回JSON字段名与JS变量名严格一致。

坑3:费用结算后,就诊记录状态未更新
现象:提交账单后,visit表中status仍为WAITING
原因:BillService.javavisitMapper.updateStatus()方法调用的是visitMapper.xml里的<update id="updateStatus">,但该SQL缺少WHERE id = #{visitId}条件。
修复:打开VisitMapper.xml,找到updateStatus节点,将UPDATE visit SET status = #{status}改为UPDATE visit SET status = #{status} WHERE id = #{visitId}

5.4 后续可扩展方向(轻量级)

如果诊所业务增长,可按优先级逐步扩展:

  • 第一优先级:微信消息通知
    BillService.javacreateBill()末尾添加:
    java wechatService.sendBillNotice(bill.getId(), bill.getTotalAmount());
    对应的WechatService调用微信模板消息API,推送“您的爱宠[宠物名]就诊已完成,费用[金额]元”。

  • 第二优先级:药品扫码入库
    medicine-add.html中增加<input type="text" id="barcode">,JS监听回车事件,调用/api/medicine/by-barcode?code=xxx接口,自动填充药品名称、规格、进价。

  • 第三优先级:移动端适配
    不重构前端,而是用@media查询优化现有CSS:
    css @media (max-width: 768px) { .schedule-table { font-size: 12px; } .schedule-table th, .schedule-table td { padding: 4px; } }
    让排班表在手机上横向滚动查看,而非强行缩放。

这套代码的价值,从来不在它有多“先进”,而在于它有多“实在”。它不承诺颠覆你的工作流,只帮你把每天重复写的3张纸、拨的5个电话、查的2次库存,变成一次点击、一个页面、一条提醒。当你第一次看到系统自动标红即将过期的药品,当你发现前台录入新客户的时间缩短了一半,当你月底导出的营收报表不再需要手动加总——那一刻,你会明白:所谓数字化,不过是让工具真正服务于人,而不是让人去适应工具。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:提供一套可直接运行的宠物医院信息化管理源码,包含完整的前后端实现和本地部署支持。系统功能覆盖宠物档案登记、主人信息维护、医生排班安排、就诊记录录入、药品库存跟踪、费用结算统计等日常运营场景。前端使用原生HTML/CSS/JS构建轻量级界面,无额外框架依赖;后端基于SpringBoot 2.x开发,集成MyBatis操作MySQL数据库,结构清晰、模块分明。资源包内含157个文件:Java业务类、HTML模板页、MyBatis XML映射文件、PNG图标资源(img.png至img_7.png及imgs目录)、配置文件(application.properties等)、README说明文档以及详细的项目运行说明.txt。已预置所有静态资源与基础配置,.gitignore和.gitattributes齐全,兼容IntelliJ IDEA或Eclipse等主流IDE,导入即编译。运行说明文档明确列出环境要求(JDK 8+、Maven 3.5+、MySQL 5.7+)、数据库建表脚本执行步骤、默认登录账号密码(admin/123456)及常见启动问题排查方法,适合宠物医疗从业者快速搭建内部管理系统,也便于开发者学习SpringBoot实战开发流程。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值