分享一套锋哥原创的微信小程序个人健康管理系统(带AI智能客服功能)(Spring AI 2.0+SpringBoot4+Vue3)

1 阅读4分钟

大家好,我是java1234_小锋老师,分享一套微信小程序个人健康管理系统(带AI智能客服功能)(Spring AI 2.0+SpringBoot4+Vue3)  。

1.jpg

项目简介

随着国民生活水平的不断提高和健康意识的逐步增强,人们对个人健康的日常管理需求日益迫切。传统的健康记录方式多以纸质笔记或单机软件为主,存在数据分散、难以长期追踪、缺乏专业指导等问题。与此同时,微信小程序凭借“无需安装、即用即走”的特性,已成为移动互联网时代轻量级应用的主流载体;而以大语言模型为核心的人工智能技术的成熟,则为健康咨询的智能化、个性化提供了全新的可能。在此背景下,本文设计并实现了一套微信小程序个人健康管理系统,并集成基于Spring AI 2.0的AI智能客服功能。

本系统采用前后端分离架构。用户端基于微信小程序原生框架开发,后台管理端采用Vue3构建,服务端基于SpringBoot4搭建,数据持久化采用MySQL数据库,并通过MyBatis-Plus实现数据访问。系统主要实现了用户管理、健康数据记录与可视化、饮食与运动记录、健康计划管理、健康资讯浏览以及AI智能客服等核心功能。其中,AI智能客服模块借助Spring AI 2.0统一的ChatClient抽象对接大语言模型,结合用户的健康画像构建提示词(Prompt),实现了流式输出的个性化健康问答与建议。

经过系统测试,本系统功能完整、运行稳定、交互友好,能够帮助用户便捷地记录和管理个人健康数据,并通过AI智能客服获得即时的健康咨询服务,具有较好的实用价值和推广意义。

源码下载

链接: pan.baidu.com/s/16V9LEpyu… 提取码: 1234

相关截图

2.jpg

3.jpg

4.jpg

5.jpg

6.jpg

7.jpg

8.jpg

9.jpg

10.jpg

核心代码


package com.java1234.health.controller;

import com.java1234.health.common.PageResult;
import com.java1234.health.common.Result;
import com.java1234.health.entity.HealthMetric;
import com.java1234.health.service.HealthMetricService;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 健康指标控制器
 *
 * @author Java1234
 */
@RestController
public class HealthMetricController {

    private final HealthMetricService healthMetricService;

    public HealthMetricController(HealthMetricService healthMetricService) {
        this.healthMetricService = healthMetricService;
    }

    /** 后台分页查询 */
    @GetMapping("/admin/metric/page")
    public Result<PageResult<HealthMetric>> adminPage(
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            @RequestParam(required = false) Long userId,
            @RequestParam(required = false) String type) {
        return Result.success(healthMetricService.page(pageNum, pageSize, userId, type));
    }

    /** 小程序分页查询 */
    @GetMapping("/wx/metric/page")
    public Result<PageResult<HealthMetric>> wxPage(
            @RequestAttribute Long userId,
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            @RequestParam(required = false) String type) {
        return Result.success(healthMetricService.page(pageNum, pageSize, userId, type));
    }

    /** 指标趋势 */
    @GetMapping("/wx/metric/trend")
    public Result<List<HealthMetric>> trend(
            @RequestAttribute Long userId,
            @RequestParam String type) {
        return Result.success(healthMetricService.trend(userId, type));
    }

    /** 最新指标 */
    @GetMapping("/wx/metric/latest")
    public Result<List<HealthMetric>> latest(@RequestAttribute Long userId) {
        return Result.success(healthMetricService.latest(userId));
    }

    @PostMapping({"/admin/metric", "/wx/metric"})
    public Result<Void> add(@RequestBody HealthMetric metric, @RequestAttribute(required = false) Long userId) {
        if (userId != null) metric.setUserId(userId);
        healthMetricService.add(metric);
        return Result.success();
    }

    @PutMapping({"/admin/metric", "/wx/metric"})
    public Result<Void> update(@RequestBody HealthMetric metric) {
        healthMetricService.update(metric);
        return Result.success();
    }

    @DeleteMapping({"/admin/metric/{id}", "/wx/metric/{id}"})
    public Result<Void> delete(@PathVariable Long id) {
        healthMetricService.delete(id);
        return Result.success();
    }
}


<template>
  <div class="page-container">
    <div class="page-header"><h2>健康指标</h2></div>
    <div class="search-bar">
      <el-select v-model="queryType" placeholder="指标类型" clearable style="width:140px" @change="loadData">
        <el-option v-for="t in types" :key="t" :label="t" :value="t" />
      </el-select>
      <el-button type="primary" @click="loadData"><el-icon><Search /></el-icon>搜索</el-button>
      <el-button type="success" @click="openDialog()"><el-icon><Plus /></el-icon>新增</el-button>
    </div>
    <el-table :data="tableData" stripe border style="width:100%">
      <el-table-column prop="id" label="ID" min-width="60" />
      <el-table-column prop="nickname" label="用户" min-width="90" />
      <el-table-column prop="type" label="类型" min-width="80" />
      <el-table-column label="数值" min-width="120">
        <template #default="{ row }">
          {{ row.value }}{{ row.value2 ? '/' + row.value2 : '' }} {{ row.unit }}
        </template>
      </el-table-column>
      <el-table-column prop="recordTime" label="记录时间" min-width="160">
        <template #default="{ row }">{{ formatDateTime(row.recordTime) }}</template>
      </el-table-column>
      <el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
      <el-table-column label="操作" min-width="140" fixed="right">
        <template #default="{ row }">
          <el-button type="primary" link @click="openDialog(row)">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination class="pagination" v-model:current-page="pageNum" v-model:page-size="pageSize" :total="total" layout="total, prev, pager, next" @change="loadData" />

    <el-dialog v-model="dialogVisible" :title="form.id ? '编辑指标' : '新增指标'" width="500px">
      <el-form :model="form" label-width="90px">
        <el-form-item label="用户ID"><el-input-number v-model="form.userId" :min="1" /></el-form-item>
        <el-form-item label="类型">
          <el-select v-model="form.type"><el-option v-for="t in types" :key="t" :label="t" :value="t" /></el-select>
        </el-form-item>
        <el-form-item label="数值"><el-input-number v-model="form.value" :precision="2" /></el-form-item>
        <el-form-item label="辅助值" v-if="form.type === '血压'"><el-input-number v-model="form.value2" :precision="2" /></el-form-item>
        <el-form-item label="单位"><el-input v-model="form.unit" /></el-form-item>
        <el-form-item label="记录时间"><el-date-picker v-model="form.recordTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" /></el-form-item>
        <el-form-item label="备注"><el-input v-model="form.remark" /></el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSave">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/utils/request'
import { formatDateTime } from '@/utils/format'

const types = ['体重', '血压', '血糖', '心率', '体温']
const tableData = ref([])
const total = ref(0)
const pageNum = ref(1)
const pageSize = ref(10)
const queryType = ref('')
const dialogVisible = ref(false)
const form = reactive({ id: null, userId: 1, type: '体重', value: 0, value2: null, unit: 'kg', recordTime: '', remark: '' })

onMounted(() => loadData())

async function loadData() {
  const res = await request.get('/admin/metric/page', { params: { pageNum: pageNum.value, pageSize: pageSize.value, type: queryType.value } })
  tableData.value = res.data.records
  total.value = res.data.total
}

function openDialog(row) {
  Object.assign(form, row ? { ...row } : { id: null, userId: 1, type: '体重', value: 0, value2: null, unit: 'kg', recordTime: new Date().toISOString().slice(0,19).replace('T',' '), remark: '' })
  dialogVisible.value = true
}

async function handleSave() {
  if (form.id) await request.put('/admin/metric', form)
  else await request.post('/admin/metric', form)
  ElMessage.success('保存成功')
  dialogVisible.value = false
  loadData()
}

async function handleDelete(id) {
  await ElMessageBox.confirm('确定删除吗?', '提示', { type: 'warning' })
  await request.delete(`/admin/metric/${id}`)
  ElMessage.success('删除成功')
  loadData()
}
</script>

<style scoped>
.pagination { margin-top: 16px; justify-content: flex-end; }
</style>