☰
SpringBoot+Vue+Uniapp医疗预约问诊小程序全栈实战
2026/10/18 9:42:46 网站建设 项目流程

这次我们来看一个非常典型的全栈实战项目:SpringBoot + Vue + Uniapp 三端联动的医疗预约问诊小程序。这类项目在 CSDN 和 Git 上一直热度不低,原因是它不只是一个 Demo,而是把小程序端、管理后台端、服务端这三块全部串起来了。对前端转全栈的同学来说,它提供的不是单个页面的实现,而是一条完整的业务链路:用户注册登录、科室医生展示、预约挂号、在线图文问诊、处方归档、健康档案管理。

这个项目最值得关注的点有三个。第一,端侧覆盖完整,患者用 Uniapp 小程序,医生和管理员用 Vue 后台,业务接口统一由 SpringBoot 提供,一次联调就能理解前后端协作方式。第二,业务场景真实,挂号要处理号源扣减的并发问题,问诊要维护会话和消息记录,处方和健康档案要做数据关联,这些都不是简单的增删改查。第三,就业导向明确,前端简历缺后端经验、后端简历缺前端能力的,都可以拿这个项目补一块。

本文会带你从头过一遍:技术选型和系统架构、数据库表设计、SpringBoot 后端核心接口、Vue 管理后台页面、Uniapp 患者端小程序、本地联调验证、部署上线流程,最后再聊聊面试和简历上怎么把这个项目讲出亮点。

适合人群:前端想转全栈的开发者、Java 后端想补全栈能力的初学者、准备课程设计或毕业设计的学生,以及需要在简历里增加一个完整项目经验的人。

1. 核心能力速览

能力项说明
项目类型医疗预约问诊小程序,全栈实战项目
技术栈SpringBoot 后端 + Vue3 管理后台 + Uniapp 患者端
核心业务用户登录、科室医生展示、预约挂号、在线问诊、处方记录、健康档案
端侧覆盖Uniapp 患者小程序端、Vue 管理后台端、SpringBoot 服务端
推荐运行环境JDK 17 + MySQL 8 + Node 18 及以上
启动方式后端 IDE 或 jar 启动,Vue 使用 npm run dev,Uniapp 使用 HBuilderX 运行到微信开发者工具
API 能力后端提供 RESTful 接口,统一返回 JSON,支持 Postman / Apifox 调试
批量任务业务侧主要依赖分页列表和批量状态更新,接口设计预留相应能力
适合场景前端转全栈练手、Java 后端补前端、课程设计、简历项目
就业价值补齐后端接口开发、数据库设计、权限认证、联调部署经验

补充一句关于定位的问题。医疗两个字听起来很重,但本文按技术演示项目来做:预约、排班、问诊记录、健康档案都属于业务流程演示,使用脱敏测试数据,不涉及真实诊疗和真实患者隐私数据。如果要把小程序上架到微信公众平台,医疗类目需要额外资质,这个在部署章节会再提醒。

2. 适用场景与使用边界

2.1 适合谁

前端转全栈的开发者,是这个项目最主要的受众。很多前端同学的简历里只有页面,没有接口、没有数据库、没有权限控制。面试官问一句“登录状态怎么保存”“接口怎么鉴权”“数据库表你怎么设计”,项目就立不住了。这个项目把这些问题全部覆盖。

Java 后端初学者也适合。SpringBoot 单看增删改查很容易,但结合小程序端之后,就要考虑跨域、Token 传递、文件上传、事务回滚,这些才是实际工作中的常见问题。

在校学生做课设或毕设同样推荐。医疗预约问诊是常见的选题方向,而且有明确的三端结构,论文和答辩的素材都会比较完整。

2.2 能解决什么问题

它解决的是“会单端、不会整条链路”的问题。前端同学能从这里学会写接口、建表、做鉴权;后端同学能学会用 Uniapp 写跨端页面、封装请求、对接微信登录;想做简历项目的人,能获得一个可演示、可讲解、可扩展的完整系统。

业务闭环也完整。患者从浏览科室、选择医生、预约挂号,到发起问诊、收到处方、查看健康档案,整个流程都能跑通。这个闭环本身就是面试时最有说服力的部分。

2.3 不适合什么场景

不要在真实医院或诊所环境直接使用。系统没有经过医疗软件资质认证,也没有对接真实 HIS 系统,只能作为技术练习。

不要使用真实患者数据。开发环境和演示环境一律使用模拟数据,包括姓名、手机号、身份证号、病历内容。

不要在小程序里做“AI 自动诊断”“自动开药”等涉及医疗器械资质判断的功能。技术演示可以聊算法,业务上线必须遵守监管要求。

2.4 合规边界

医疗健康数据属于敏感个人信息。这个项目的正确用法是:所有数据脱敏、所有账号用测试数据、所有图片素材使用无版权风险的内容。

涉及到用户上传的头像、检查报告图片,开发阶段建议保存到本地临时目录或私有测试存储,上线前清理。如果后续要商用,需要走完整的隐私协议、用户授权、数据加密存储和合规审查流程。

小程序上架前,需要确认微信公众平台的类目资质。医疗类目通常要求提供相关许可证。如果只是本地运行、真机体验版、或作为个人学习项目,不受影响。

3. 全栈架构与数据库设计

3.1 端侧分工与技术栈

整个系统的架构可以从三个端来理解。

端侧技术栈核心职责
患者小程序端Uniapp + Vue3 + Pinia + uni-ui科室浏览、医生列表、预约挂号、在线问诊、健康档案
管理后台端Vue3 + Vite + Element Plus + Pinia登录、科室管理、医生管理、挂号列表、问诊处理、处方管理
服务端SpringBoot + MyBatis-Plus + MySQL登录认证、业务接口、号源控制、问诊会话、文件上传

数据流也很清晰。Uniapp 小程序通过封装后的 uni.request 访问后端接口,Vue 后台通过 axios 访问后端接口,后端统一返回{ code, message, data }结构。管理后台和患者端共用一套业务接口,但通过角色权限区分数据范围。

使用 MyBatis-Plus 的原因是开发效率高,单表 CRUD 不需要写 XML,分页插件也能直接复用。业务稍微复杂一点的查询再用自定义 SQL 补充。

3.2 核心业务表设计

数据库是这个项目的骨架。很多人写代码不先设计表,导致后面关联查询非常痛苦。这里建议至少规划这些核心表:

  • user:普通用户,也就是患者
  • doctor:医生
  • department:科室
  • appointment:预约挂号单
  • consultation:问诊会话
  • consultation_message:问诊消息
  • prescription:处方记录
  • health_record:健康档案
  • sys_user:后台账号,对应管理员和医生账号

表关系如下:

  • department 一对多 doctor
  • doctor 一对多 appointment
  • user 一对多 appointment
  • user 一对多 consultation
  • consultation 一对多 consultation_message
  • consultation 一对一 prescription
  • user 一对多 health_record

这里特别说明一个设计点:患者账号和后台账号不要混在同一张表里。患者端走的是微信登录体系,医生和管理员走的是账号密码体系,两者的字段、权限、使用场景都不同,分开建表更干净。

3.3 核心建表 SQL 示例

下面是医生表的简化建表语句,实际开发可以在此基础上加排班、评分、擅长领域等字段。

CREATE TABLE `doctor` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `dept_id` bigint NOT NULL COMMENT '科室ID', `doctor_name` varchar(50) NOT NULL COMMENT '医生姓名', `title` varchar(50) DEFAULT NULL COMMENT '职称', `intro` text COMMENT '医生简介', `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL', `reg_fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '挂号费', `total_slots` int NOT NULL DEFAULT '20' COMMENT '每日总号源', `left_slots` int NOT NULL DEFAULT '20' COMMENT '剩余号源', `status` tinyint NOT NULL DEFAULT '1' COMMENT '1启用 0停诊', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医生表';

预约挂号表是这个项目的核心业务表。

CREATE TABLE `appointment` ( `id` bigint NOT NULL AUTO_INCREMENT, `patient_id` bigint NOT NULL COMMENT '患者用户ID', `doctor_id` bigint NOT NULL COMMENT '医生ID', `appointment_date` varchar(20) NOT NULL COMMENT '就诊日期 yyyy-MM-dd', `time_slot` varchar(20) NOT NULL COMMENT '时间段,如 09:00-10:00', `status` tinyint NOT NULL DEFAULT '0' COMMENT '0待就诊 1已完成 2已取消', `order_no` varchar(32) DEFAULT NULL COMMENT '业务单号', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_doctor_date` (`doctor_id`, `appointment_date`), KEY `idx_patient` (`patient_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预约挂号表';

这里有几点值得注意。对外展示的业务单号建议单独生成,不要直接暴露自增 ID。高频查询场景,比如“某医生某天剩余号源”,要加联合索引。另外,更完整的方案是建一张独立的排班表 schedule,按日期生成医生每天可预约的时段和号源数,而不是直接在 doctor 表里留 total_slots 和 left_slots。上面的 SQL 是简化模型,先把链路跑通,后续扩展排班表会更灵活。

4. SpringBoot 后端核心模块开发

4.1 工程目录结构

后端建议按职责分层,目录结构如下:

medical-server/ ├── src/main/java/com/example/medical/ │ ├── config/ # 跨域配置、MyBatis-Plus 配置、Redis 配置 │ ├── controller/ # 接口层 │ ├── service/ # 业务层 │ ├── mapper/ # 数据访问层 │ ├── entity/ # 实体类 │ ├── dto/ # 入参对象 │ ├── vo/ # 出参对象 │ ├── common/ # 统一返回结构、异常处理、枚举 │ └── MedicalApplication.java ├── src/main/resources/ │ ├── mapper/ # MyBatis XML,复杂查询时使用 │ └── application.yml └── pom.xml

controller 只负责参数接收和结果返回,业务逻辑放在 service,数据访问放在 mapper。这样后面加功能、改需求的时候不会把所有代码堆在一个类里。

4.2 登录鉴权:JWT 方案

小程序端和后台端共用后端接口,使用 JWT 做无状态鉴权比较合适。流程是:用户登录成功后,后端生成 token 返回给前端,前端在后续请求的 Header 中携带Authorization: Bearer token,后端通过拦截器解析 token 并设置当前用户 ID。

下面是一个简化版的 JWT 拦截器示例。

@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 非接口请求直接放行 if (!(handler instanceof HandlerMethod)) { return true; } String token = request.getHeader("Authorization"); if (token == null || token.isEmpty()) { throw new BusinessException(401, "未登录"); } // 解析 token,失败时抛出 401 Long userId = JwtUtil.parseToken(token.replace("Bearer ", "")); request.setAttribute("userId", userId); return true; } }

生产环境建议使用成熟的 JWT 库,比如 jjwt,或者直接用 Spring Security + OAuth2。这里展示的是最简实现,目的是理解鉴权链路。

4.3 挂号模块:接口与业务逻辑

挂号模块是后端最核心的部分,它要处理三个问题:医生状态校验、号源扣减、挂号单创建。其中号源扣减必须考虑并发,否则会出现“多个用户同时挂同一个号”的问题。

先看 Controller 层。

@RestController @RequestMapping("/api/appointment") public class AppointmentController { @Resource private AppointmentService appointmentService; @PostMapping("/create") public Result<AppointmentVO> create(@RequestBody AppointmentCreateDTO dto, HttpServletRequest request) { Long userId = (Long) request.getAttribute("userId"); return Result.success(appointmentService.createAppointment(userId, dto)); } }

再来看 Service 层的关键逻辑。

@Service public class AppointmentServiceImpl implements AppointmentService { @Resource private DoctorMapper doctorMapper; @Resource private AppointmentMapper appointmentMapper; @Override @Transactional(rollbackFor = Exception.class) public AppointmentVO createAppointment(Long userId, AppointmentCreateDTO dto) { // 1. 校验医生状态与号源 Doctor doctor = doctorMapper.selectById(dto.getDoctorId()); if (doctor == null || doctor.getStatus() != 1) { throw new BusinessException("医生不存在或停诊"); } if (doctor.getLeftSlots() <= 0) { throw new BusinessException("号源已满"); } // 2. 乐观更新剩余号源,防止并发超卖 int updated = doctorMapper.reduceLeftSlots(doctor.getId()); if (updated == 0) { throw new BusinessException("号源已满,请选择其他时间"); } // 3. 创建挂号单 Appointment appointment = new Appointment(); appointment.setPatientId(userId); appointment.setDoctorId(doctor.getId()); appointment.setAppointmentDate(dto.getAppointmentDate()); appointment.setTimeSlot(dto.getTimeSlot()); appointment.setStatus(0); appointment.setOrderNo(generateOrderNo()); appointmentMapper.insert(appointment); return AppointmentVO.from(appointment); } }

对应的扣减 SQL 如下:

UPDATE doctor SET left_slots = left_slots - 1 WHERE id = #{doctorId} AND left_slots > 0

这种“乐观扣减 + 受影响行数判断”的方式,是解决号源超卖最简单有效的方案。多个请求同时进来时,数据库行锁保证只有一个 update 能成功,影响行数为 0 的请求直接返回号源已满。加上@Transactional,扣号源和创建挂号单要么全部成功,要么全部回滚。

4.4 问诊与处方模块

问诊模块按会话模型来设计。患者发起问诊时创建一条 consultation 记录,之后双方通过 consultation_message 表发送消息。消息内容包含消息类型 text 或 image,图片消息在发送前先走文件上传接口拿到 URL,再把 URL 存入消息体。

医生端在 Vue 后台可以查看问诊会话列表,进入会话后回复消息。问诊结束时,医生填写诊断结论和处方信息,系统创建 prescription 记录,并把诊断摘要追加到患者的 health_record 中。这样患者端“我的健康档案”就能看到历史问诊记录,业务流程闭环。

这里的关键点不是代码量,而是状态流转。建议在 consultation 表里增加 status 字段:0 进行中、1 已结束,避免已结束的会话还能继续发消息。

4.5 统一返回结构与异常处理

所有接口返回统一结构,前端处理逻辑会简单很多。

{ "code": 200, "message": "success", "data": {} }

异常处理使用@RestControllerAdvice,把业务异常、参数校验异常、系统异常分别映射到对应的 code。前端只需要判断 code 是否为 200,省去大量重复判断代码。

5. Vue 管理后台核心页面开发

5.1 后台页面结构

管理后台是医生和管理员使用的端,页面不算多,核心是围绕业务数据做展示和操作:

  • 登录页
  • 工作台
  • 科室管理
  • 医生管理
  • 挂号列表
  • 问诊会话
  • 处方管理
  • 健康档案

技术栈使用 Vue3 + Vite + Element Plus + Pinia。Vite 启动快,Element Plus 的表格、表单、弹窗组件可以直接复用,适合后台类系统快速开发。

5.2 axios 请求封装

后台所有接口请求通过 axios 统一封装。请求拦截器负责加 token,响应拦截器负责处理业务错误码和登录失效。

import axios from 'axios' import { useAuthStore } from '@/stores/auth' import { ElMessage } from 'element-plus' import router from '@/router' const service = axios.create({ baseURL: '/api', timeout: 10000 }) service.interceptors.request.use(config => { const authStore = useAuthStore() if (authStore.token) { config.headers.Authorization = `Bearer ${authStore.token}` } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response?.status === 401) { router.push('/login') } return Promise.reject(error) } ) export default service

本地开发时,Vite 配置代理把/api转发到后端服务,这样可以避免跨域问题。

server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }

5.3 医生管理列表页示例

后台最常见的页面就是“列表 + 分页 + 搜索 + 状态操作”。以医生管理为例:

<script setup> import { ref, onMounted } from 'vue' import { getDoctorList, updateDoctorStatus } from '@/api/doctor' const list = ref([]) const total = ref(0) const query = ref({ page: 1, pageSize: 10, keyword: '' }) async function loadList() { const res = await getDoctorList(query.value) list.value = res.data.records total.value = res.data.total } async function handleStatusChange(row) { await updateDoctorStatus(row.id, row.status) ElMessage.success('状态已更新') } onMounted(loadList) </script> <template> <el-card> <el-form :inline="true"> <el-form-item label="医生姓名"> <el-input v-model="query.keyword" placeholder="输入姓名搜索" @keyup.enter="loadList" /> </el-form-item> <el-form-item> <el-button type="primary" @click="loadList">查询</el-button> </el-form-item> </el-form> <el-table :data="list" border stripe> <el-table-column prop="doctorName" label="医生姓名" /> <el-table-column prop="title" label="职称" /> <el-table-column prop="deptName" label="科室" /> <el-table-column prop="regFee" label="挂号费" /> <el-table-column prop="leftSlots" label="剩余号源" /> <el-table-column label="状态"> <template #default="{ row }"> <el-switch v-model="row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(row)" /> </template> </el-table-column> <el-table-column label="操作"> <template #default="{ row }"> <el-button link type="primary" @click="openEdit(row)">编辑</el-button> </template> </el-table-column> </el-table> <el-pagination v-model:current-page="query.page" v-model:page-size="query.pageSize" :total="total" layout="total, prev, pager, next" @current-change="loadList" /> </el-card> </template>

这个页面的逻辑很简单:进入页面加载第一页数据,搜索时重置页码,切换状态后调用更新接口。但它覆盖了后台系统最常见的套路,看完这个页面,科室管理、挂号列表、处方管理等页面可以照同样的模式快速实现。

6. Uniapp 患者端小程序开发

6.1 小程序页面结构

患者端是 C 端体验,页面按用户操作路径组织。

pages/ ├── index/index # 首页,展示科室入口、推荐医生 ├── department/list # 科室列表 ├── doctor/list # 医生列表 ├── doctor/detail # 医生详情与排班 ├── appointment/create # 创建预约 ├── appointment/list # 我的预约 ├── consultation/list # 问诊会话列表 ├── consultation/chat # 问诊聊天页 ├── health/record # 健康档案 └── mine/index # 我的页面

Uniapp 的页面路由和 Vue Router 不同,它基于pages.json配置,tabBar 页面注册在 tabBar 节点,普通页面直接放在 pages 数组里。

6.2 小程序请求封装

Uniapp 没有 axios,一般用uni.request封装一个 Promise 请求工具,统一处理 baseURL、token 和错误提示。

// utils/request.js const BASE_URL = 'https://api.example.com/api' export function request(options) { return new Promise((resolve, reject) => { const token = uni.getStorageSync('token') uni.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', Authorization: token ? `Bearer ${token}` : '' }, success: (res) => { if (res.statusCode === 200 && res.data.code === 200) { resolve(res.data.data) } else if (res.statusCode === 401) { uni.removeStorageSync('token') uni.navigateTo({ url: '/pages/login/index' }) reject(res.data) } else { uni.showToast({ title: res.data.message || '请求失败', icon: 'none' }) reject(res.data) } }, fail: (err) => { uni.showToast({ title: '网络异常', icon: 'none' }) reject(err) } }) }) }

这里的BASE_URL需要替换成实际后端地址。本地开发时可以填局域网 IP 加后端端口,真机调试时确保手机和电脑在同一网络。上线前必须改成 HTTPS 域名。

6.3 微信登录流程

微信小程序登录流程和传统账号密码登录完全不同。

前端调用uni.login获取临时 code,把 code 传给后端。后端拿着 code 调用微信接口换取 openid,然后根据 openid 查询用户表,不存在则自动注册,最后生成 token 返回前端。

async function wxLogin() { const { code } = await uni.login({ provider: 'weixin' }) const { token } = await request({ url: '/auth/wx-login', method: 'POST', data: { code } }) uni.setStorageSync('token', token) uni.switchTab({ url: '/pages/index/index' }) }

开发阶段要注意,微信开发者工具需要勾选“不校验合法域名”,否则本地请求会被拦截。上线前则必须在微信公众平台配置合法的 request 域名。

6.4 预约挂号页面交互

患者端的挂号流程是:科室列表进入医生列表,在医生详情页选择日期和时间段,点击预约后调createAppointment接口。页面要做按钮 loading 状态,防止用户快速连点产生重复订单。下单成功之后跳转到“我的预约”页面,状态显示为待就诊。

这个流程的体验重点是反馈要即时。点击预约后按钮立刻变 loading,接口成功后提示“预约成功”,失败时把后端的错误信息直接展示给用户,比如“号源已满,请选择其他时间”。

7. 前后端联调与本地启动验证

7.1 启动顺序与端口规划

本地联调建议按下面的顺序启动:

服务端口启动方式
MySQL3306本地服务
Redis6379本地服务,缓存科室和医生数据时使用
SpringBoot 后端8080IDE 启动或mvn spring-boot:run
Vue 后台5173npm run dev
Uniapp 小程序微信开发者工具HBuilderX 运行到微信开发者工具

端口是开发环境常见默认值,实际项目里如果端口被占用,在后端 application.yml 和 Vite 代理配置里改掉即可。

7.2 联调验证清单

前后端分开开发时各自都能跑,但真正的问题往往出在联调阶段。建议按照下面的清单做一轮功能验证:

验证项操作预期结果
小程序登录微信开发者工具里触发登录数据库 user 表生成记录,返回 token
科室列表小程序首页加载科室科室数据正常展示
医生列表选择科室进入医生列表医生按科室过滤,号源显示正确
创建预约选择医生和时间段提交医生剩余号源减一,我的预约出现记录
后台登录管理员账号登录 Vue 后台进入工作台,token 写入本地存储
医生管理后台新增或停用医生小程序端医生列表同步变化
在线问诊患者端发消息,后台医生回复消息记录两端可见
处方归档医生后台填写处方患者端健康档案出现对应记录

7.3 跨域问题处理

本地联调最常见的报错就是跨域。Vue 后台本地开发通过 Vite 代理解决,Uniapp 小程序不受 CORS 跨域限制,但微信开发者工具有合法域名校验。如果后端单独开启 CORS,注意只对测试环境开放,生产环境统一走 nginx 反向代理更安全。

8. 部署上线与小程序审核

8.1 后端打包部署

后端打包使用 Maven。

mvn clean package

打包后在 target 目录下生成 jar 文件,上传到服务器。可以用 nohup 临时启动,也可以用 systemd 做成服务。

nohup java -jar medical-server.jar --server.port=8080 > logs/app.log 2>&1 &

生产环境需要改数据库连接密码、关闭调试日志、设置 JVM 内存参数,比如:

java -Xms512m -Xmx1024m -jar medical-server.jar

8.2 Vue 管理后台部署

Vue 后台构建后是纯静态文件。

npm run build

把生成的 dist 目录上传到服务器,用 nginx 托管,并通过反向代理转发/api请求。

server { listen 80; server_name your-domain.example.com; location / { root /var/www/admin; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

注意 SPA 应用刷新页面会找不到路由,try_files是必须配置的。

8.3 小程序上传与审核注意

Uniapp 项目在 HBuilderX 里点击“发行 -> 小程序-微信”,生成微信小程序代码,然后用微信开发者工具打开并上传到微信公众平台。

小程序发布有几个硬性条件。request 合法域名必须是 HTTPS,且已经备案。医疗相关类目大概率需要资质。如果只是学习项目,建议保持“体验版”状态,让微信开发者工具里的成员扫码体验即可,不需要走正式审核上线。

9. 常见问题与排查方法

问题现象可能原因排查方式解决方案
后端启动失败,报数据库连接错误MySQL 未启动、账号密码错误、数据库不存在检查 application.yml 与 MySQL 服务状态创建数据库并导入 SQL,核对连接配置
小程序请求失败,报 url not in domain list微信公众平台未配置合法域名查看微信开发者工具控制台开发模式勾选“不校验合法域名”,上线前配置 HTTPS 域名
接口返回 401 或 token 解析失败请求头没带 token、token 过期查看请求 Header 与 JWT 过期时间前端统一在拦截器加 Authorization,过期后跳转登录
创建预约提示号源已满号源被扣完或并发争抢查看 doctor 表的 left_slots使用乐观更新 SQL,必要时增加 Redis 分布式锁
Vue 后台接口跨域Vite 代理未配置或后端 CORS 缺失浏览器 F12 查看 Network配置 Vite proxy 或后端 CORS
图片加载不出来图片地址是本地绝对路径或存储未开放访问检查返回 URL 与存储配置替换为可访问的地址,确认存储桶权限
小程序上传图片失败上传接口未实现、文件大小超限查看后端日志限制大小,使用 uni.uploadFile 调用对应接口
部署后页面白屏静态资源路径错误、nginx 配置问题查看 nginx error.log配置 SPA try_files 规则,检查 base 路径
本地能跑,服务器上接口很慢JVM 参数未设置、数据库连接池过小查看内存和慢查询日志设置 -Xms/-Xmx,调大连接池
问诊会话结束后还能发消息状态机未控制查看后端接口逻辑接口判断 consultation.status,已结束则拒绝新消息

10. 前端转全栈的就业视角与最佳实践

10.1 简历项目怎么写

同样是医疗小程序,简历上是否“会写”差别很大。建议用四段式结构,写清楚技术栈、项目职责、核心难点、量化结果。

医疗预约问诊小程序(全栈项目) 技术栈:SpringBoot + MyBatis-Plus + Redis + Vue3 + Element Plus + Uniapp 职责: - 设计用户、医生、挂号、问诊、健康档案等核心业务表,覆盖完整预约就诊闭环 - 实现 JWT 登录鉴权,区分患者端、医生端、管理员端角色权限 - 实现预约挂号号源扣减,使用乐观更新解决并发超卖问题 - 使用 Redis 缓存科室和医生基础数据,降低数据库查询压力 - 独立开发 Uniapp 患者端和 Vue 管理后台,完成前后端接口联调与部署

这段话的核心是让面试官一眼看到:这个人做过数据库设计、做过权限、处理过并发、做过多个端。这些正是全栈岗位考察的重点。

10.2 面试常问的高频问题

这个项目能引出的面试题很多,建议提前准备:

  • 为什么用 JWT 不用 Session?
  • 号源扣减怎么防止超卖?事务和乐观锁如何配合?
  • 微信小程序登录的完整流程是什么?openid 的作用是什么?
  • Redis 在这个项目里缓存了什么?缓存和数据库一致性怎么处理?
  • user 表和 sys_user 表为什么要分开?
  • 如果同一时间有大量用户抢号,服务器层面怎么优化?
  • 在线问诊的消息记录用什么数据结构存储?聊天记录如何高效查询?

能把这些讲清楚,项目价值就完全体现出来了。

10.3 从“能跑”到“有亮点”的扩展方向

基础功能跑通后,可以按下面的优先级做扩展。第一优先级是支付闭环,接入微信支付完成挂号费订单。第二优先级是排班表设计,把医生表里的总号源和剩余号源抽离成独立的 schedule 表,支持按天生成号源。第三优先级是消息队列,用 RabbitMQ 处理问诊通知和订单状态变更。第四优先级是 AI 辅助分诊,患者先描述症状,系统给出科室建议,只做分诊参考,不做诊断结论。

扩展方向不用全部做完,选一个做到生产级质量,比做一堆半成品更有说服力。

10.4 最佳实践与合规提醒

开发顺序上,先跑通最小闭环:登录、看医生、挂号。再扩展问诊、处方、健康档案。一次做完全部功能,排错成本会很高。

工程规范上,数据库密码、微信 secret 这类敏感信息不要提交到 Git 仓库,放配置文件并用.gitignore忽略。接口命名统一用/api前缀,后端接口和前端请求封装保持一致。

数据安全上,开发数据一律脱敏。假姓名、假手机号、假病历,不要图方便直接导入真实数据。图片素材注意版权,人物头像和病历截图不要随意下载使用。

发布之前检查一遍:是否还有调试接口、是否开启了 Swagger、数据库连接信息是否正确、小程序合法域名是否已配置、HTTPS 证书是否有效。医疗数据是敏感个人信息,学习项目用测试数据没有任何问题,一旦涉及真实数据和商用场景,必须走完整的合规流程。

这个项目最值得尝试的就是把三端串起来,跑通一个完整的预约问诊闭环。按照上面的顺序做一遍,你会理解为什么后端接口要这样设计、前端为什么这样封装请求、小程序登录为什么必须走后端换 openid。建议先按文章顺序复现一次,跑通之后试着改一个功能,比如加排班表、加微信支付或者优化号源并发控制。改完以后,这个项目就不再是别人的教程,而是真正属于你的项目。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询