SpringBoot 医药咨询系统

概述

智慧医药系统(smart-medicine)是一个基于 SpringBoot 开发的Web 项目。整体页面简约大气,增加了AI医生问诊功能,功能设计的较为简单。

开源地址

https://gitcode.net/NVG_Haru/Java_04

界面预览

SpringBoot 医药咨询系统_第1张图片

SpringBoot 医药咨询系统_第2张图片

功能介绍

游客功能介绍

功能模块 功能描述
登录注册方面 注册成为系统用户
系统主页 浏览系统主页、疾病、药品信息搜索、详情的查看(统计浏览量)

用户功能介绍

功能模块 功能描述
登录注册方面 填写用户信息进行账号注册(邮件接收验证码)、使用账号密码进行登录
个人资料方面 修改个人资料(姓名、年龄、手机号、头像等)、修改登录密码
系统反馈方面 提交系统反馈意见
智能医生方面 与智能医生进行交流聊天

管理员功能介绍

功能模块 功能描述
登录注册方面 填写用户信息进行账号注册(邮件接收验证码)、使用账号密码进行登录
个人资料方面 修改个人资料(姓名、年龄、手机号、头像等)、修改登录密码
系统反馈方面 提交系统反馈意见
智能医生方面 与智能医生进行交流聊天
疾病管理方面 发布疾病、编辑(名称、原因、症状、分类等)、删除药品等
药品管理方面 发布药品、编辑(名称、搜索关键词、功效、用法用量、类型等)、关联疾病、删除药品等
反馈管理方面 管理用户提交的反馈信息

数据库设计

SpringBoot 医药咨询系统_第3张图片

代码讲解

AI 问诊功能

这个功能借助阿里通义千问大模型实现,调用了com.alibaba.dashscope.*sdk提供的接口,主要流程如下:

  1. 创建Generation对象
  2. 创建MessageManager对象
  3. 创建系统消息
  4. 将系统消息和用户消息添加到MessageManager中
  5. 创建QwenParam对象
  6. 调用Generation的call方法,获取GenerationResult对象
  7. 获取GenerationResult对象的输出部分
  8. 获取输出中的第一个消息并返回
    public String query(String queryMessage) {
        // 设置API key
        Constants.apiKey = apiKey;
        try {
            // 创建Generation对象
            Generation gen = new Generation();
            // 创建MessageManager对象
            MessageManager msgManager = new MessageManager(10);
            // 创建系统消息
            Message systemMsg = Message.builder().role(Role.SYSTEM.getValue())
                    .content("你是智能医生,你只回答与医疗相关的问题,不要回答其他问题!").build();
            // 创建用户消息
            Message userMsg = Message.builder().role(Role.USER.getValue()).content(queryMessage).build();
            // 将系统消息和用户消息添加到MessageManager中
            msgManager.add(systemMsg);
            msgManager.add(userMsg);
            // 创建QwenParam对象
            QwenParam param = QwenParam.builder().model(Generation.Models.QWEN_TURBO).messages(msgManager.get())
                    .resultFormat(QwenParam.ResultFormat.MESSAGE).build();
            // 调用Generation的call方法,获取GenerationResult对象
            GenerationResult result = gen.call(param);
            // 获取GenerationResult对象的输出部分
            GenerationOutput output = result.getOutput();
            // 获取输出中的第一个消息
            Message message = output.getChoices().get(0).getMessage();
            // 返回消息的内容
            return message.getContent();
        } catch (Exception e) {
            return "智能医生现在不在线,请稍后再试~";
        }
    }

反馈功能

主要讲解一下query这个查询函数,该函数接受一个Feedback实体函数,实际上这是一种偷懒的做法,最佳方案还是确定好哪些参数可以进入函数。

package world.xuewei.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import world.xuewei.dao.FeedbackDao;
import world.xuewei.entity.Feedback;
import world.xuewei.utils.Assert;
import world.xuewei.utils.BeanUtil;
import world.xuewei.utils.VariableNameUtils;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * 反馈服务类
 *
 */
@Service
public class FeedbackService extends BaseService<Feedback> {

    @Autowired
    protected FeedbackDao userDao;

        /**
     * 查询满足指定条件的Feedback列表
     *
     * @param o 查询条件对象
     * @return 满足条件的Feedback列表
     */
    @Override
    public List<Feedback> query(Feedback o) {
        // 创建QueryWrapper对象
        QueryWrapper<Feedback> wrapper = new QueryWrapper();
        if (Assert.notEmpty(o)) {
            // 将对象转换为Map
            Map<String, Object> bean2Map = BeanUtil.bean2Map(o);
            // 遍历Map中的键值对
            for (String key : bean2Map.keySet()) {
                if (Assert.isEmpty(bean2Map.get(key))) {
                    continue;
                }
                // 根据键值对创建查询条件
                wrapper.eq(VariableNameUtils.humpToLine(key), bean2Map.get(key));
            }
        }
        // 执行查询操作
        return userDao.selectList(wrapper);
    }

    @Override
    public List<Feedback> all() {
        return query(null);
    }

    @Override
    public Feedback save(Feedback o) {
        if (Assert.isEmpty(o.getId())) {
            userDao.insert(o);
        } else {
            userDao.updateById(o);
        }
        return userDao.selectById(o.getId());
    }

    @Override
    public Feedback get(Serializable id) {
        return userDao.selectById(id);
    }

    @Override
    public int delete(Serializable id) {
        return userDao.deleteById(id);
    }
}

疾病功能

package world.xuewei.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import world.xuewei.dao.IllnessDao;
import world.xuewei.entity.*;
import world.xuewei.utils.Assert;
import world.xuewei.utils.BeanUtil;
import world.xuewei.utils.VariableNameUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 疾病服务类
 *
 * @author XUEW
 */
@Service
public class IllnessService extends BaseService<Illness> {

    @Autowired
    protected IllnessDao illnessDao;

    @Override
    public List<Illness> query(Illness o) {
        QueryWrapper<Illness> wrapper = new QueryWrapper();
        if (Assert.notEmpty(o)) {
            Map<String, Object> bean2Map = BeanUtil.bean2Map(o);
            for (String key : bean2Map.keySet()) {
                if (Assert.isEmpty(bean2Map.get(key))) {
                    continue;
                }
                wrapper.eq(VariableNameUtils.humpToLine(key), bean2Map.get(key));
            }
        }
        return illnessDao.selectList(wrapper);
    }

    @Override
    public List<Illness> all() {
        return query(null);
    }

    @Override
    public Illness save(Illness o) {
        if (Assert.isEmpty(o.getId())) {
            illnessDao.insert(o);
        } else {
            illnessDao.updateById(o);
        }
        return illnessDao.selectById(o.getId());
    }

    @Override
    public Illness get(Serializable id) {
        return illnessDao.selectById(id);
    }

    @Override
    public int delete(Serializable id) {
        return illnessDao.deleteById(id);
    }

    public Map<String, Object> findIllness(Integer kind, String illnessName, Integer page) {

        Map<String, Object> map = new HashMap<>(4);
        QueryWrapper<Illness> illnessQueryWrapper = new QueryWrapper<>();
        if (Assert.notEmpty(illnessName)) {
            illnessQueryWrapper
                    .like("illness_name", illnessName)
                    .or()
                    .like("include_reason", illnessName)
                    .or()
                    .like("illness_symptom", illnessName)
                    .or()
                    .like("special_symptom", illnessName);
        }
        if (kind != null) {
            if (Assert.notEmpty(illnessName)) {
                illnessQueryWrapper.last("and (kind_id = " + kind + ") ORDER BY create_time DESC limit " + (page - 1) * 9 + "," + page * 9);
            } else {
                illnessQueryWrapper.eq("kind_id", kind);
                illnessQueryWrapper.orderByDesc("create_time");
                illnessQueryWrapper.last("limit " + (page - 1) * 9 + "," + page * 9);
            }
        } else {
            illnessQueryWrapper.orderByDesc("create_time");
            illnessQueryWrapper.last("limit " + (page - 1) * 9 + "," + page * 9);

        }
        int size = illnessDao.selectMaps(illnessQueryWrapper).size();
        List<Map<String, Object>> list = illnessDao.selectMaps(illnessQueryWrapper);
        list.forEach(l -> {
            Integer id = MapUtil.getInt(l, "id");
            Pageview pageInfo = pageviewDao.selectOne(new QueryWrapper<Pageview>().eq("illness_id", id));
            l.put("kindName", "暂无归属类");
            l.put("create_time", MapUtil.getDate(l, "create_time"));
            l.put("pageview", pageInfo == null ? 0 : pageInfo.getPageviews());
            Integer kindId = MapUtil.getInt(l, "kind_id");
            if (Assert.notEmpty(kindId)) {
                IllnessKind illnessKind = illnessKindDao.selectById(kindId);
                if (Assert.notEmpty(illnessKind)) {
                    l.put("kindName", illnessKind.getName());
                }
            }
        });
        map.put("illness", list);
        map.put("size", size < 9 ? 1 : size / 9 + 1);
        return map;
    }

    public Map<String, Object> findIllnessOne(Integer id) {
        Illness illness = illnessDao.selectOne(new QueryWrapper<Illness>().eq("id", id));
        List<IllnessMedicine> illnessMedicines = illnessMedicineDao.selectList(new QueryWrapper<IllnessMedicine>().eq("illness_id", id));
        List<Medicine> list = new ArrayList<>(4);
        Map<String, Object> map = new HashMap<>(4);
        Pageview illness_id = pageviewDao.selectOne(new QueryWrapper<Pageview>().eq("illness_id", id));
        if (Assert.isEmpty(illness_id)) {
            illness_id = new Pageview();
            illness_id.setIllnessId(id);
            illness_id.setPageviews(1);
            pageviewDao.insert(illness_id);
        } else {
            illness_id.setPageviews(illness_id.getPageviews() + 1);
            pageviewDao.updateById(illness_id);
        }
        map.put("illness", illness);

        if (CollUtil.isNotEmpty(illnessMedicines)) {
            illnessMedicines.forEach(illnessMedicine -> {
                Medicine medicine = medicineDao.selectOne(new QueryWrapper<Medicine>().eq("id", illnessMedicine.getMedicineId()));
                if (ObjectUtil.isNotNull(medicine)) {
                    list.add(medicine);
                }
            });
            map.put("medicine", list);

        }

        return map;
    }

    public Illness getOne(QueryWrapper<Illness> queryWrapper) {
        return illnessDao.selectOne(queryWrapper);
    }
}

讲一讲findIllnessOne,这个函数是一个用于查找疾病信息的方法。它接受三个参数:kind表示疾病类型,illnessName表示疾病名称,page表示页码。函数首先创建一个HashMap用于存储结果。然后创建一个QueryWrapper对象用于构建查询条件。如果illnessName不为空,则通过like操作符模糊匹配illness_name、include_reason、illness_symptom和special_symptom字段中的任意一个包含illnessName的内容。接下来根据kind和illnessName的值,构建查询条件,包括对kind_id和create_time的筛选和排序。然后通过调用illnessDao.selectMaps方法获取查询结果的列表。接着遍历列表,对每个疾病对象获取id,并通过pageviewDao.selectOne方法查询对应的pageview信息。然后将一些字段放入疾病对象的HashMap中,包括kindName(默认值为"暂无归属类")、create_time、pageview(如果查询失败则为0)和kindName(如果有)。接下来根据疾病列表的大小计算总页数,并将疾病列表和总页数放入HashMap中。最后将HashMap作为结果返回。

主要问题有:

  • 重复代码:在kind !=
    null和else部分,关于orderByDesc和last的查询条件重复了分页逻辑。可以简化这两个部分的代码。

  • 查询条件构造:like操作的字段和值应该使用占位符,这样可以避免SQL注入。

  • 断言Assert.notEmpty的使用:代码中使用了Assert.notEmpty,这通常用于校验条件,但在这个上下文中,这个校验似乎是多余的,因为如果illnessName为空,之前的like条件就无法匹配任何结果。

  • 使用更合适的数据容器:List和Map已经足够表达结果,可以考虑直接返回一个Page对象,而不是Map,其中包含数据列表和分页信息。

  • 代码可读性:一些变量的命名可以更加清晰,例如map变量可以命名为pageResult,以更清楚地表示它包含的是分页结果。

  • 避免硬编码:分页大小(即每页9条记录)被硬编码在查询中,可以作为常量提取出来。

  • 异常处理:在查询过程中,应该有适当的异常处理机制,以处理潜在的数据库访问错误。

  • 数据映射:数据映射和处理逻辑可以封装到单独的方法中,以提高代码的可读性和可维护性。

你可能感兴趣的:(Spring,项目实战,甄选,spring,boot,后端,java)