文章目录
基于SpringBoot+VUE的宠物医院后台管理系统【源码开源】【建议收藏】
今天给大家开源一个基于SpringBoot+VUE的宠物医院后台管理系统,系统基于脚手架工程,花了大概1周时间做出来的。
该系统完全开源。
系统完美运行,无任何的bug,技术较多,可以当做
面试的项目或者作为毕设的项目。
通过本项目你可以学到:
- 项目是怎样前后端分离的
- vue 是如何用于后台管理的
- 如何用MyBaitsPlus 代码生成器生成代码的
- 单表增删改查(包括分页模糊查询)
- 批量删除
- 基于RBAC的权限管理是如何设计的
- 菜单管理又是如何实现的
- 文件上传的实现
- 导入导出
- 主从表的数据是如何在前端呈现的
- …
获取源码的方式见文章底部。
为防止刷着刷着找不到,大家点赞、收藏文章。
具体的介绍如下所示。
1.技术介绍
核心技术:SpringBoot+MyBatis-Plus;
前端:vue+elementui;
开发工具:Idea,VS Code, Git;
数据库:mysql5.7;
2.系统设计
本系统考虑的应用场景是在宠物店或宠物医院里, 主要有预约管理、药品管理、订单管理、充值管理等功能。
主要分了用户、医生和管理员三个角色。每个角色登录系统会有不同的菜单功能呈现,页面中不同的角色有不同的权限。各角色可访问的菜单都由管理员进行配置。
2.1系统功能设计
2.2预约流程
2.3就诊流程
2.4商品流程
2.5充值流程
3.功能介绍
3.1登录或注册
没有账号可以注册账号,当前只开放 普通用户的注册。对于医生和管理员的注册,由系统管理员在管理段注册。(默认管理员的账号和密码都是 admin)
登录之后,后台会菜单和权限的配置生成相应数据告知前端,前端进入相应的模块。
4.用户端
4.1就诊预约
用户可以查看所有医生信息,并根据宠物病情选择预约对应的医生预约排队(即添加预约信息)。
4.2预约管理
用户查看自己的预约信息,查看预约审核状态;并实现预约信息的添加,修改,删除,查询功能。
4.3药品管理
用户可以查询治疗宠物需要用到的所有药品或食品,针对自己宠物需要用到的商品可以进行订购(不能对商品实现添加,修改,删除功能)。
4.4订单管理
用户可以查看自己的所有商品订单,并实现修改,删除,查询功能,针未支付的订单可以进行支付,收到商品以后进行确认收货。
4.5充值中心
用户针对自己的账户进行余额充值。每笔充值记录及消费记录都可查询到。
4.6个人中心
用户可以查看自己的个人信息,并实现修改功能(例如:修改账号,密码等)。
5.医生端
医生的注册目前只能由管理员在管理员端进行注册,暂不开放自己注册。
5.1预约管理
医生可以查询预约了自己的所有预约信息,实现预约信息的添加,修改,删除,查询功能。
5.2诊断管理
针对每一个自己诊断的宠物进行记录,方便下次诊断时查询,实现就诊记录的添加,修改,删除,查询功能。
5.3.个人中心
医生可以查看自己的个人信息,并实现修改功能(例如:修改账号,密码等)。
(截图可参见前面用户端的)
6.管理员端
6.1.用户管理
查询所有普通用户信息,实现普通用户的添加,修改,删除,查询功能。
6.2.医生管理
查询所有医生信息,实现医生的添加,修改,删除,查询功能。
6.3.药品管理
查询所有治疗宠物的药品或食品信息,实现商品的添加,修改,删除,查询功能。
6.4.订单管理
查询用户的所有订单,针对已经付款的订单进行发货处理,针对已经完成的订单,以及未支付的订单可以进行删除处理,不能修改用户订单。
6.5.科室管理
查询科室信息,实现科室的添加,修改,删除,查询功能。
6.6.宠物种类管理
查询宠物种类信息,实现科室的添加,修改,删除,查询功能。
6.7.个人中心
管理员查询自己的个人信息,并可以实现修改功能(例如:修改账号,密码等)。
(截图可参见前面用户端的)
7.核心代码
/**
* <p>
* 订单管理控制器
* </p>
*
* @author
* @since 2023-04-24
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Resource
private IOrderBaseService orderBaseService;
@Resource
private IOrderItemService orderItemService;
@Resource
private IDrugService drugService;
@Resource
private IUserService userService;
private final String now = DateUtil.now();
// 新增或者更新
@PostMapping
public Result save(@RequestBody OrderDTO orderDTO) {
if (orderDTO.getItems() == null || orderDTO.getItems().size()<1) {
return Result.error(Constants.CODE_400, "参数错误.订单缺少项目信息");
}
if (StringUtils.isEmpty(orderDTO.getReceiverPhone())) {
return Result.error(Constants.CODE_400, "参数错误.请提供收货人电话");
}
if (StringUtils.isEmpty(orderDTO.getReceiverAddress())) {
return Result.error(Constants.CODE_400, "参数错误.请提供收货人地址");
}
//判断用户是否存在
QueryWrapper<User> queryWrapperCustom = new QueryWrapper<>();
queryWrapperCustom.eq("username", orderDTO.getUserName());
queryWrapperCustom.eq("role", RoleEnum.ROLE_USER.toString());
User findUser = userService.getOne(queryWrapperCustom);
if (null == findUser) {
return Result.error(Constants.CODE_404, "用户不存在");
}
if (orderDTO.getRemark() == null) {
orderDTO.setRemark("");
}
BigDecimal totalAmount = new BigDecimal(0);
OrderBase orderBase = new OrderBase();
orderBase.setStatus(OrderBase.OrderStatus.UN_PAY);
orderBase.setPayMode(OrderBase.PayMode.UNKNOWN);
orderBase.setPayVoucherNo("");
orderBase.setUserId(findUser.getId());
orderBase.setUserName(findUser.getUsername());
orderBase.setReceiverPhone(orderDTO.getReceiverPhone());
orderBase.setReceiverAddress(orderDTO.getReceiverAddress());
orderBase.setRemark(orderDTO.getRemark());
long oid = 0;
if (orderDTO.getId() == null) {
//添加操作
OrderIdGenerator orderIdGenerator = new OrderIdGenerator();
oid = orderIdGenerator.nextId(this);
orderDTO.setId(oid);
User currentUser = TokenUtils.getCurrentUser();
orderBase.setCreateName(currentUser.getUsername());
orderBase.setCreateBy(String.valueOf(currentUser.getId()));
orderBase.setCreateRole(currentUser.getRole());
orderBase.setCreateTime(new Date());
}else {
//更新操作
oid = orderDTO.getId();
//找出原订单信息
OrderBase oldOrder = orderBaseService.getById(oid);
if (null == oldOrder) {
return Result.error(Constants.CODE_404, "订单记录不存在.id=" + oid);
}
//只有待支付状态的订单才 可以 修改
if (oldOrder.getStatus() != OrderBase.OrderStatus.UN_PAY) {
return Result.error(Constants.CODE_600, "只有待支付状态的订单才可以修改 .id=" + oid);
}
orderBase.setCreateName(oldOrder.getCreateName());
orderBase.setCreateBy(oldOrder.getCreateBy());
orderBase.setCreateRole(oldOrder.getCreateRole());
orderBase.setCreateTime(oldOrder.getCreateTime());
//删除原来项目 表的内容
orderItemService.delByOrderId(oid);
}
//先保存项目表
int seq = 0;
String orderOverview = "" ; // 订单项目简要
for (OrderItem orderItem: orderDTO.getItems() ) {
orderItem.setOrderId(oid);
//处理 序号
seq++;
orderItem.setSeq(seq);
if (orderItem.getItemId() == null ) {
return Result.error(Constants.CODE_400, "参数错误. 订单项目id为Null");
};
if (orderItem.getQty() == null ) {
return Result.error(Constants.CODE_400, "参数错误. 订单项目数量为Null");
}
if (orderItem.getQty() <= 0 ) {
return Result.error(Constants.CODE_400, "参数错误. 订单项目数量必须大于0");
}
//数据库中查找商品是否存在
Drug drug = drugService.getById(orderItem.getItemId());
if (null == drug) {
return Result.error(Constants.CODE_400, "参数错误. 商品不存在. 项目id=" + orderItem.getItemId());
}
orderItem.setPrice(drug.getRetailPrice());
orderItem.setName(drug.getName() + " " + drug.getProductName());
orderItem.setUnit(drug.getUnit());
orderOverview += "|";
orderOverview += orderItem.getName();
//计算合计
totalAmount = totalAmount.add(orderItem.getPrice().multiply(new BigDecimal(orderItem.getQty())));
if (!orderItemService.save(orderItem)) {
return Result.error(Constants.CODE_600, "业务异常.无法保存某订单的项目.项目id=" + orderItem.getItemId());
};
}
orderOverview += "|";
if (orderOverview.length()>128) {
orderOverview = orderOverview.substring(0,127);
}
orderBase.setId(oid);
orderBase.setOverview(orderOverview);
orderBase.setAmount(totalAmount);
//再保存主表的
if (orderBaseService.saveOrUpdate(orderBase)) {
return Result.success();
}else {
return Result.error(Constants.CODE_600, "业务异常.保存订单主表失败" );
}
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Long id) {
OrderBase orderBase = orderBaseService.getById(id);
if (null == orderBase ){
return Result.error(Constants.CODE_404, "订单不存在. 订单id=" + id );
}
//只能删除未支付或已签收的订单
if (orderBase.getStatus()==OrderBase.OrderStatus.UN_PAY ||
orderBase.getStatus()==OrderBase.OrderStatus.SIGN ) {
;
}else {
return Result.error(Constants.CODE_600, "无效订单状态.只能删除未支付或已签收的订单");
}
//先删除项目表的记录
orderItemService.delByOrderId(id);
orderBaseService.removeById(id);
return Result.success();
}
@PostMapping("/del/batch")
public Result deleteBatch(@RequestBody List<Long> ids) {
//先删除项目表的记录
for (Long id: ids) {
orderItemService.delByOrderId(id);
}
orderBaseService.removeByIds(ids);
return Result.success();
}
@GetMapping
public Result findAll() {
return Result.success(orderBaseService.list());
}
@GetMapping("/{id}")
public Result findOne(@PathVariable Long id) {
return Result.success(orderBaseService.getById(id));
}
@GetMapping("/page")
public Result findPage(@RequestParam(defaultValue = "") String userName,
@RequestParam Integer pageNum,
@RequestParam Integer pageSize) {
QueryWrapper<OrderBase> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("create_time");
if (!"".equals(userName)) {
queryWrapper.like("user_name", userName);
}
User currentUser = TokenUtils.getCurrentUser();
if (currentUser.getRole().equals(RoleEnum.ROLE_USER.toString())) {
//普通用户只能查看自己的预约记录
queryWrapper.eq("user_id", currentUser.getId());
}
Object object = orderBaseService.page(new Page<>(pageNum, pageSize), queryWrapper);
return Result.success(object);
//return Result.success(orderBaseService.page(new Page<>(pageNum, pageSize), queryWrapper));
}
@GetMapping("/items/{id}")
public Result findOrderItems(@PathVariable Long id) {
//查找订单项目列表
QueryWrapper<OrderItem> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("seq");
queryWrapper.eq("order_id", id);
return Result.success(orderItemService.list(queryWrapper));
}
/**
* 导出接口
*/
@GetMapping("/export")
public void export(HttpServletResponse response) throws Exception {
// 从数据库查询出所有的数据
List<OrderBase> list = orderBaseService.list();
// 在内存操作,写出到浏览器
ExcelWriter writer = ExcelUtil.getWriter(true);
// 一次性写出list内的对象到excel,使用默认样式,强制输出标题
writer.write(list, true);
// 设置浏览器响应的格式
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
String fileName = URLEncoder.encode("OrderBase信息表", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
out.close();
writer.close();
}
/**
* excel 导入
* @param file
* @throws Exception
*/
@PostMapping("/import")
public Result imp(MultipartFile file) throws Exception {
InputStream inputStream = file.getInputStream();
ExcelReader reader = ExcelUtil.getReader(inputStream);
// 通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来
List<OrderBase> list = reader.readAll(OrderBase.class);
orderBaseService.saveBatch(list);
return Result.success();
}
@PostMapping("/delivery/{id}")
public Result delivery(@PathVariable Long id) {
//发货
//判断订单是否存在
OrderBase orderBase = orderBaseService.getById(id);
if (null == orderBase) {
return Result.error(Constants.CODE_404, "订单不存在.订单id=" + id);
}
//判断订单是否已经支付
if (orderBase.getStatus() == OrderBase.OrderStatus.PAYED){
}else {
return Result.error(Constants.CODE_600, "订单不是已支付状态, 不允许发货.");
}
if (orderBaseService.updateDeliveryInfo(id, OrderBase.OrderStatus.DELIVERED, new Date()) >0) {
return Result.success(orderBaseService.getById(id));
}else {
return Result.error(Constants.CODE_600, "业务异常.更新订单发货信息失败" );
}
}
@PostMapping("/sign/{id}")
public Result sign(@PathVariable Long id) {
//签收,
//判断订单是否存在
OrderBase orderBase = orderBaseService.getById(id);
if (null == orderBase) {
return Result.error(Constants.CODE_404, "订单不存在.订单id=" + id);
}
//判断订单是否已经支付
if (orderBase.getStatus() == OrderBase.OrderStatus.DELIVERED){
;
}else {
return Result.error(Constants.CODE_600, "订单不是已发货状态, 不允许签收.");
}
if (orderBaseService.updateSignInfo(id, OrderBase.OrderStatus.SIGN, new Date())>0) {
return Result.success(orderBaseService.getById(id));
}else {
return Result.error(Constants.CODE_600, "业务异常.更新订单签收信息失败" );
}
}
private User getUser() {
return TokenUtils.getCurrentUser();
}
}
/**
* <p>
* 预约管理控制器
* </p>
*
* @author
* @since 2023-04-23
*/
@RestController
@RequestMapping("/appointment")
public class AppointmentController {
@Resource
private IAppointmentService appointmentService;
@Resource
private IUserService userService;
@Resource
private IDoctorService doctorService;
private final String now = DateUtil.now();
// 新增或者更新
@PostMapping
public Result save(@RequestBody Appointment appointment) {
if (appointment.getAppointmentTime() == null) {
return Result.error(Constants.CODE_400, "参数错误:请提供预约时间");
}
//查找顾客信息
QueryWrapper<User> queryWrapperCustom = new QueryWrapper<>();
queryWrapperCustom.eq("username", appointment.getCustomName());
queryWrapperCustom.eq("role", RoleEnum.ROLE_USER.toString());
User findCustom = userService.getOne(queryWrapperCustom);
if (null == findCustom) {
return Result.error(Constants.CODE_404, "顾客不存在");
}
appointment.setCustomId(findCustom.getId());
appointment.setCustomName(findCustom.getUsername());
appointment.setCustomPhone(findCustom.getPhone());
//查找医生信息
QueryWrapper<User> queryWrapperDoctor = new QueryWrapper<>();
queryWrapperDoctor.eq("username", appointment.getDoctorName());
queryWrapperDoctor.eq("role", RoleEnum.ROLE_DOCTOR.toString());
User findDoctor = userService.getOne(queryWrapperDoctor);
if (null == findDoctor) {
return Result.error(Constants.CODE_404, "医生不存在");
}
appointment.setDoctorId(findDoctor.getId());
appointment.setDoctorName(findDoctor.getUsername());
appointment.setDoctorPhone(findDoctor.getPhone());
if (appointment.getRemark() == null) {
appointment.setRemark("");
}
if (appointment.getId() == null) {
//添加操作
appointment.setStatus(Appointment.AppointmentStatus.WAIT_AUDIT);
User currentUser = TokenUtils.getCurrentUser();
appointment.setCreateName(currentUser.getUsername());
appointment.setCreateBy(String.valueOf(currentUser.getId()));
appointment.setCreateRole(currentUser.getRole());
appointment.setCreateTime(new Date());
}else {
//更新操作
if (appointment.getStatus() == null) {
return Result.error(Constants.CODE_400, "参数错误:请提供预约状态");
}
//不更新原 创建人信息
Appointment oldAppointment = appointmentService.getById(appointment.getId());
if (null==oldAppointment) {
return Result.error(Constants.CODE_404, "预约记录不存在");
}
appointment.setCreateName(oldAppointment.getCreateName());
appointment.setCreateBy(oldAppointment.getCreateBy());
appointment.setCreateRole(oldAppointment.getCreateRole());
appointment.setCreateTime(oldAppointment.getCreateTime());
}
appointmentService.saveOrUpdate(appointment);
return Result.success();
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Long id) {
appointmentService.removeById(id);
return Result.success();
}
@PostMapping("/del/batch")
public Result deleteBatch(@RequestBody List<Long> ids) {
appointmentService.removeByIds(ids);
return Result.success();
}
// @GetMapping
// public Result findAll() {
// return Result.success(appointmentService.list());
// }
@GetMapping("/{id}")
public Result findOne(@PathVariable Long id) {
return Result.success(appointmentService.getById(id));
}
@GetMapping("/page")
public Result findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam(defaultValue = "") String customName,
@RequestParam(defaultValue = "") String doctorName) {
QueryWrapper<Appointment> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
if (!"".equals(customName)) {
queryWrapper.like("custom_name", customName);
}
if (!"".equals(doctorName)) {
queryWrapper.like("doctor_name", doctorName);
}
User currentUser = TokenUtils.getCurrentUser();
if (currentUser.getRole().equals(RoleEnum.ROLE_USER.toString())) {
//普通用户只能查看自己的预约记录
queryWrapper.eq("create_by", currentUser.getId());
}else if (currentUser.getRole().equals(RoleEnum.ROLE_DOCTOR.toString())) {
//医生可以查看自己的记录和 别人约自己的
queryWrapper.eq("create_by", currentUser.getId()).or().eq("doctor_id", currentUser.getId());
}
return Result.success(appointmentService.page(new Page<>(pageNum, pageSize), queryWrapper));
}
/**
* 导出接口
*/
@GetMapping("/export")
public void export(HttpServletResponse response) throws Exception {
// 从数据库查询出所有的数据
List<Appointment> list = appointmentService.list();
// 在内存操作,写出到浏览器
ExcelWriter writer = ExcelUtil.getWriter(true);
// 一次性写出list内的对象到excel,使用默认样式,强制输出标题
writer.write(list, true);
// 设置浏览器响应的格式
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
String fileName = URLEncoder.encode("Appointment信息表", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
out.close();
writer.close();
}
/**
* excel 导入
* @param file
* @throws Exception
*/
@PostMapping("/import")
public Result imp(MultipartFile file) throws Exception {
InputStream inputStream = file.getInputStream();
ExcelReader reader = ExcelUtil.getReader(inputStream);
// 通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来
List<Appointment> list = reader.readAll(Appointment.class);
appointmentService.saveBatch(list);
return Result.success();
}
private User getUser() {
return TokenUtils.getCurrentUser();
}
}
8.设计文档
提供需求说明和关键的业务数据库表字典,很难得喔。
9.源码获取
如果你喜欢这个项目的话, 给个一键三连,点赞越多,往后提高更多的开源项目, 谢谢大家,gitee链接附上。
后端:https://gitee.com/madifu/petHis-backend
前端:https://gitee.com/madifu/petHis-fontend-admin
10.后记
在开发这个系统时,遇到过几个坑,包括 VUE的,还有js 的,它们是怎么绕过去的,将另行文章做相关介绍。