云计算百科
云计算领域专业知识百科平台

【免费】LLM大模型 基于Spring AI 2.0的RAG AI智能办公OA系统(SpringBoot4+Vue3) LLM大模型毕业设计 AI Agent 大模型课程设计 锋哥原创出品,必属精品

大家好,我是Java1234_小锋老师,分享一套锋哥原创的LLM大模型 基于Spring AI 2.0的RAG AI智能办公OA系统(SpringBoot4+Vue3) 。

项目介绍

随着大语言模型(Large Language Model,LLM)技术的快速落地,企业办公自动化(Office Automation,OA)系统正从“表单驱动”转向“对话驱动”。传统OA虽然能够完成请假、报销、审批等流程,但员工仍需记忆制度条款、手工填写大量字段,并反复在多个菜单之间查询单据进度,沟通成本高、体验割裂。针对这一问题,本文设计并实现了一套面向大学与中小企业场景的AI智能办公OA系统,题目为“LLM大模型 基于Spring AI 2.0的AI智能办公OA系统”。

系统采用前后端分离架构。后端以 Java 17 为开发语言,基于 Spring Boot 4.0.8 构建 Web 服务,持久层使用 MyBatis-Plus 访问 MySQL 8 数据库 db_ai_oa;前端采用 Vue 3 + Vite + Element Plus + Pinia 实现响应式管理界面,工作台使用 ECharts 展示统计图表。智能能力以 Spring AI 2.0.1 为核心,通过 OpenAI 兼容接口对接阿里云百炼大模型:对话模型选用 qwen3.8-27b,嵌入模型选用 text-embedding-v4(维度 2048),向量检索使用 Redis Stack 的 RediSearch 模块,索引算法为 HNSW,距离度量为余弦相似度。

在业务层面,系统实现管理员、总经理、部门经理、普通员工四类角色的权限隔离。办公流程覆盖部门与员工管理、请假与报销两级审批、公告发布和个人中心。其中请假超过 3 天、报销超过 5000 元需总经理终审;部门经理本人请假直接进入总经理节点。在智能层面,系统提供三类核心能力:其一,自然语言智能填写请假单,模型将“下周三请一天事假”解析为结构化草稿,并调用假期余额工具给出扣薪提示;其二,企业知识库 RAG 问答,管理员可上传 txt、doc、pdf、markdown 制度文件,经 Tika 解析、Token 切片后写入向量库,回答时强制标注出处;其三,多轮对话助手结合 Chat Memory 与 Tool Calling,可查询真实请假/报销审批进度,避免大模型幻觉。

测试表明,系统功能完整、角色权限正确、审批阈值规则生效,AI 填单与制度问答能够稳定返回结构化结果和可追溯出处。本文工作验证了 Spring AI 2.0 在国产办公系统中的工程可行性,对同类毕业设计与企业信息化升级具有参考价值。

源码下载

链接: https://pan.baidu.com/s/1yNt4JoR1fXml2uIDtvDm7Q?pwd=1234 提取码: 1234

系统展示

核心代码

package com.java1234.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.java1234.common.BusinessException;
import com.java1234.common.LoginUser;
import com.java1234.common.RoleEnum;
import com.java1234.common.UserContext;
import com.java1234.dto.LoginRequest;
import com.java1234.dto.LoginVO;
import com.java1234.dto.PasswordDTO;
import com.java1234.entity.Dept;
import com.java1234.entity.SysUser;
import com.java1234.mapper.DeptMapper;
import com.java1234.mapper.SysUserMapper;
import com.java1234.util.FileUtil;
import com.java1234.util.JwtUtil;
import com.java1234.util.Md5Util;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

/**
* 认证与个人中心服务
*/
@Service
public class AuthService {

private final SysUserMapper sysUserMapper;
private final DeptMapper deptMapper;
private final JwtUtil jwtUtil;
private final FileUtil fileUtil;

public AuthService(SysUserMapper sysUserMapper, DeptMapper deptMapper, JwtUtil jwtUtil, FileUtil fileUtil) {
this.sysUserMapper = sysUserMapper;
this.deptMapper = deptMapper;
this.jwtUtil = jwtUtil;
this.fileUtil = fileUtil;
}

/**
* 用户名密码登录
*/
public LoginVO login(LoginRequest request) {
if (request.getUsername() == null || request.getPassword() == null) {
throw new BusinessException("请输入用户名和密码");
}
SysUser user = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUsername, request.getUsername()));
if (user == null) {
throw new BusinessException("用户名或密码错误");
}
if (user.getStatus() != null && user.getStatus() == 0) {
throw new BusinessException("账号已停用");
}
if (!Md5Util.encrypt(request.getPassword()).equals(user.getPassword())) {
throw new BusinessException("用户名或密码错误");
}
LoginUser loginUser = toLoginUser(user);
return new LoginVO(jwtUtil.createToken(loginUser), loginUser);
}

/**
* 当前登录用户详情
*/
public SysUser currentInfo() {
SysUser user = sysUserMapper.selectById(UserContext.getUserId());
if (user == null) {
throw new BusinessException(401, "用户不存在");
}
user.setPassword(null);
user.setRoleLabel(RoleEnum.labelOf(user.getRole()));
if (user.getDeptId() != null) {
Dept dept = deptMapper.selectById(user.getDeptId());
if (dept != null) {
user.setDeptName(dept.getDeptName());
}
}
return user;
}

/**
* 修改个人资料
*/
public void updateProfile(SysUser form) {
SysUser db = sysUserMapper.selectById(UserContext.getUserId());
db.setRealName(form.getRealName());
db.setGender(form.getGender() == null ? "男" : form.getGender());
db.setPhone(form.getPhone());
db.setEmail(form.getEmail());
sysUserMapper.updateById(db);
}

/**
* 修改密码(需确认密码)
*/
public void updatePassword(PasswordDTO dto) {
if (dto.getNewPassword() == null || !dto.getNewPassword().equals(dto.getConfirmPassword())) {
throw new BusinessException("两次输入的新密码不一致");
}
SysUser db = sysUserMapper.selectById(UserContext.getUserId());
if (!Md5Util.encrypt(dto.getOldPassword()).equals(db.getPassword())) {
throw new BusinessException("原密码不正确");
}
db.setPassword(Md5Util.encrypt(dto.getNewPassword()));
sysUserMapper.updateById(db);
}

/**
* 上传头像
*/
public String uploadAvatar(MultipartFile file) {
String url = fileUtil.save(file, "avatar");
SysUser db = sysUserMapper.selectById(UserContext.getUserId());
db.setAvatar(url);
sysUserMapper.updateById(db);
return url;
}

/**
* 实体转登录用户
*/
public LoginUser toLoginUser(SysUser user) {
LoginUser loginUser = new LoginUser();
loginUser.setId(user.getId());
loginUser.setUsername(user.getUsername());
loginUser.setRealName(user.getRealName());
loginUser.setRole(user.getRole());
loginUser.setDeptId(user.getDeptId());
loginUser.setAvatar(user.getAvatar());
return loginUser;
}
}

<template>
<div class="page-card">
<div class="toolbar">
<div class="search-form">
<el-input v-model="keyword" placeholder="部门名称" clearable style="width: 220px;" @keyup.enter="load" />
<el-button type="primary" @click="load">查询</el-button>
</div>
<el-button type="primary" @click="open()">新增部门</el-button>
</div>
<div class="table-wrap">
<el-table :data="table.records" stripe>
<el-table-column prop="deptName" label="部门名称" min-width="140" />
<el-table-column prop="deptCode" label="编码" min-width="100" />
<el-table-column prop="managerName" label="部门经理" min-width="120" />
<el-table-column prop="employeeCount" label="人数" min-width="80" />
<el-table-column prop="remark" label="备注" min-width="180" />
<el-table-column label="创建时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
</el-table-column>
<el-table-column label="操作" min-width="140" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="open(row)">编辑</el-button>
<el-button link type="danger" @click="remove(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pager"><el-pagination background layout="total, prev, pager, next" :total="table.total" v-model:current-page="pageNum" :page-size="pageSize" @current-change="load" /></div>
<el-dialog v-model="visible" :title="form.id ? '编辑部门' : '新增部门'" width="520px">
<el-form :model="form" label-width="90px">
<el-form-item label="名称"><el-input v-model="form.deptName" /></el-form-item>
<el-form-item label="编码"><el-input v-model="form.deptCode" /></el-form-item>
<el-form-item label="经理">
<el-select v-model="form.managerId" clearable filterable style="width: 100%;">
<el-option v-for="u in managers" :key="u.id" :label="u.realName" :value="u.id" />
</el-select>
</el-form-item>
<el-form-item label="排序"><el-input-number v-model="form.sortNum" /></el-form-item>
<el-form-item label="备注"><el-input v-model="form.remark" type="textarea" /></el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="save">保存</el-button>
</template>
</el-dialog>
</div>
</template>

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

const keyword = ref('')
const pageNum = ref(1)
const pageSize = 10
const table = reactive({ total: 0, records: [] })
const visible = ref(false)
const form = reactive({})
const managers = ref([])

const load = async () => {
const res = await request.get('/api/dept/page', { params: { pageNum: pageNum.value, pageSize, keyword: keyword.value } })
table.total = res.data.total
table.records = res.data.records
}
const open = (row) => {
Object.keys(form).forEach((k) => delete form[k])
Object.assign(form, row ? { …row } : { sortNum: 1, parentId: 0 })
visible.value = true
}
const save = async () => {
if (form.id) await request.put('/api/dept', form)
else await request.post('/api/dept', form)
visible.value = false
ElMessage.success('保存成功')
load()
}
const remove = async (row) => {
await ElMessageBox.confirm('确认删除该部门?')
await request.delete(`/api/dept/${row.id}`)
ElMessage.success('已删除')
load()
}
onMounted(async () => {
const res = await request.get('/api/employee/managers')
managers.value = res.data || []
load()
})
</script>

赞(0)
未经允许不得转载:网硕互联帮助中心 » 【免费】LLM大模型 基于Spring AI 2.0的RAG AI智能办公OA系统(SpringBoot4+Vue3) LLM大模型毕业设计 AI Agent 大模型课程设计 锋哥原创出品,必属精品
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!