实现邮件,时间,数据库工具
很多鱼友私信我说要拓展的全部代码
好吧,那就 1, 2 , 3 上链接:https://github.com/fenghuanghuai/feng_ai_agent
只希望大家能多多支持,这是我持续更新的动力拜托了(づ ̄3 ̄)づ╭❤~
下面正式开始今天的代码分享把,同样和这个源码会上传到仓库中。
如果有错误,请大家指出,谢谢!!!
邮件发送工具
引入依赖
由于Spring自带了一个邮件服务,为了方便和不增加代码引入其他依赖的风险,选择他也能实现本功能
▼xml复制代码<!-- Spring Boot Mail --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
添加配置文件
▼yaml复制代码spring: # 邮件配置 mail: host: smtp.qq.com port: 587 username: xxx password: xxx properties: mail: smtp: auth: true starttls: enable: true
我这里选择的是QQ邮箱作为发送邮件的发送者,大家在选择邮箱时注意要选择有 SMTP 服务的厂家
创建密钥
注意密钥只能生成时看到,注意保存,如果忘记,需要重新生成一个
代码实现
▼java复制代码/** * @version v1.0.0 * @belongsProject: feng-ai-agent * @belongsPackage: com.qcdfz.fengaiagent.tools * @author: fgh * @description: 邮件发送工具类 * @createTime: 2025-05-17 18:49 */ @Slf4j @Component public class EmailTool { @Resource private JavaMailSender mailSender; @Value("${spring.mail.username}") private String from; /** * 邮件发送结果 */ @Data @Builder public static class EmailResult { private boolean success; private String message; private String recipient; } /** * 发送简单文本邮件 * @param to 收件人 * @param subject 主题 * @param content 内容 * @return 发送结果 */ @Tool(description = "Send a simple text email") public String sendSimpleEmail( @ToolParam(description = "Recipient email address") String to, @ToolParam(description = "Email subject") String subject, @ToolParam(description = "Email content") String content) { EmailResult result = sendEmail(to, subject, content, false); return result.getMessage(); } /** * 发送HTML格式邮件 * @param to 收件人 * @param subject 主题 * @param htmlContent HTML内容 * @return 发送结果 */ @Tool(description = "Send an HTML email") public String sendHtmlEmail( @ToolParam(description = "Recipient email address") String to, @ToolParam(description = "Email subject") String subject, @ToolParam(description = "HTML content") String htmlContent) { EmailResult result = sendEmail(to, subject, htmlContent, true); return result.getMessage(); } /** * 批量发送邮件 * @param toList 收件人列表 * @param subject 主题 * @param content 内容 * @return 发送结果 */ @Tool(description = "Send emails to multiple recipients") public String sendBatchEmails( @ToolParam(description = "List of recipient email addresses") List<String> toList, @ToolParam(description = "Email subject") String subject, @ToolParam(description = "Email content") String content) { StringBuilder resultMessage = new StringBuilder(); int successCount = 0; for (String to : toList) { EmailResult result = sendEmail(to, subject, content, false); if (result.isSuccess()) { successCount++; } resultMessage.append(result.getMessage()).append("\n"); } return String.format("批量发送完成,成功:%d,失败:%d\n%s", successCount, toList.size() - successCount, resultMessage); } /** * 发送邮件的核心方法 * @param to 收件人 * @param subject 主题 * @param content 内容 * @param isHtml 是否为HTML格式 * @return 发送结果 */ private EmailResult sendEmail(String to, String subject, String content, boolean isHtml) { try { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); // 设置邮件基本信息 // 这个只是基本思路,后续可根据当前登录用户的信息使用用户账号发送邮件 helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, isHtml); // 发送邮件 mailSender.send(message); return EmailResult.builder() .success(true) .message(String.format("邮件发送成功:%s", to)) .recipient(to) .build(); } catch (MessagingException e) { log.error("邮件发送失败: {}", e.getMessage(), e); return EmailResult.builder() .success(false) .message(String.format("邮件发送失败:%s,原因:%s", to, e.getMessage())) .recipient(to) .build(); } } }
测试代码
▼java复制代码@Test void sendHtmlEmail() { String to = "qcdfz12345@2925.com"; String subject = "HTML测试邮件"; String htmlContent = "<h1>这是一封HTML测试邮件</h1><p>Hello World!</p>"; String result = emailTool.sendHtmlEmail(to, subject, htmlContent); assertNotNull(result); }


SpringAi工具调用



时间工具
由于后面会用到mcp,所以本次时间工具就不在调用外部api,直接简单获取本地时间意思一下,等后续在做优化
代码实现
▼java复制代码/** * @version v1.0.0 * @belongsProject: feng-ai-agent * @belongsPackage: com.qcdfz.fengaiagent.tools * @author: fgh * @description: 时间工具类 * @createTime: 2025-05-17 19:49 */ @Slf4j @Component public class TimeTool { private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; private static final String DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; /** * 获取当前时间信息 * @return 包含当前日期、时间、时间戳的Map */ @Tool(description = "Get current time information") public Map<String, String> getCurrentTime() { LocalDateTime now = LocalDateTime.now(); Map<String, String> timeInfo = new HashMap<>(); timeInfo.put("date", now.format(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))); timeInfo.put("time", now.format(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); timeInfo.put("datetime", now.format(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMAT))); timeInfo.put("timestamp", String.valueOf(now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())); return timeInfo; } /** * 计算两个日期之间的天数差 * @param startDate 开始日期 (yyyy-MM-dd) * @param endDate 结束日期 (yyyy-MM-dd) * @return 天数差 */ @Tool(description = "Calculate days between two dates") public long calculateDaysBetween( @ToolParam(description = "Start date (yyyy-MM-dd)") String startDate, @ToolParam(description = "End date (yyyy-MM-dd)") String endDate) { try { LocalDate start = LocalDate.parse(startDate, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); LocalDate end = LocalDate.parse(endDate, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); return ChronoUnit.DAYS.between(start, end); } catch (Exception e) { log.error("日期计算错误: {}", e.getMessage(), e); return -1; } } /** * 格式化时间戳 * @param timestamp 时间戳(毫秒) * @return 格式化后的日期时间字符串 */ @Tool(description = "Format timestamp to datetime string") public String formatTimestamp( @ToolParam(description = "Timestamp in milliseconds") long timestamp) { try { LocalDateTime dateTime = LocalDateTime.ofInstant( java.time.Instant.ofEpochMilli(timestamp), ZoneId.systemDefault() ); return dateTime.format(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMAT)); } catch (Exception e) { log.error("时间戳格式化错误: {}", e.getMessage(), e); return "Invalid timestamp"; } } /** * 获取指定日期的开始和结束时间 * @param date 日期 (yyyy-MM-dd) * @return 包含开始时间和结束时间的Map */ @Tool(description = "Get start and end time of a specific date") public Map<String, String> getDayStartEndTime( @ToolParam(description = "Date (yyyy-MM-dd)") String date) { try { LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); LocalDateTime startOfDay = localDate.atStartOfDay(); LocalDateTime endOfDay = localDate.atTime(LocalTime.MAX); Map<String, String> result = new HashMap<>(); result.put("startTime", startOfDay.format(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMAT))); result.put("endTime", endOfDay.format(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMAT))); return result; } catch (Exception e) { log.error("日期解析错误: {}", e.getMessage(), e); Map<String, String> error = new HashMap<>(); error.put("error", "Invalid date format"); return error; } } /** * 判断日期是否在指定范围内 * @param date 要判断的日期 (yyyy-MM-dd) * @param startDate 开始日期 (yyyy-MM-dd) * @param endDate 结束日期 (yyyy-MM-dd) * @return 是否在范围内 */ @Tool(description = "Check if a date is within a date range") public boolean isDateInRange( @ToolParam(description = "Date to check (yyyy-MM-dd)") String date, @ToolParam(description = "Start date (yyyy-MM-dd)") String startDate, @ToolParam(description = "End date (yyyy-MM-dd)") String endDate) { try { LocalDate checkDate = LocalDate.parse(date, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); LocalDate start = LocalDate.parse(startDate, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); LocalDate end = LocalDate.parse(endDate, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); return !checkDate.isBefore(start) && !checkDate.isAfter(end); } catch (Exception e) { log.error("日期范围判断错误: {}", e.getMessage(), e); return false; } } }
测试代码
▼java复制代码void getCurrentTime() { TimeTool timeTool = new TimeTool(); Map<String, String> timeInfo = timeTool.getCurrentTime(); assertNotNull(timeInfo); assertTrue(timeInfo.containsKey("date")); assertTrue(timeInfo.containsKey("time")); assertTrue(timeInfo.containsKey("datetime")); assertTrue(timeInfo.containsKey("timestamp")); }
SpringAi工具调用

数据库操作工具
这个功能,可能是需求理解不够,考虑到如果让大模型传输sql执行可能造成一系列安全问题,
所以本次我实现的思路是实现一个记事本的简单功能,提前写好执行语句,类似service,如果有其他实现思路,请大家留言,相互学习。
建表sql
▼sql复制代码-- 待办表 CREATE TABLE todo_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', userId BIGINT NOT NULL COMMENT '用户ID', title VARCHAR(100) NOT NULL COMMENT '待办事项标题', content TEXT COMMENT '待办事项内容', status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0-未完成,1-已完成', priority TINYINT NOT NULL DEFAULT 0 COMMENT '优先级:0-低,1-中,2-高', dueDate DATETIME COMMENT '截止日期', createTime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updateTime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', isDeleted TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除:0-未删除,1-已删除', KEY `idx_user_id` (`userId`), KEY `idx_status` (`status`), KEY `idx_priority` (`priority`), KEY `idx_due_date` (`dueDate`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='待办事项表';
增删改查就自动生成就好了,这里不在贴代码了
工具实现
▼java复制代码/** * @version v1.0.0 * @belongsProject: feng-ai-agent * @belongsPackage: com.qcdfz.fengaiagent.tools * @author: fgh * @description: 待办事项工具类 * @createTime: 2025-05-17 20:49 */ @Slf4j @Component public class TodoTool { @Resource private TodoItemService todoItemService; private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 创建待办事项 * @param title 标题 * @param content 内容 * @param priority 优先级(0-低,1-中,2-高) * @param dueDate 截止日期 * @param toolContext 工具上下文 * @return 创建结果 */ @Tool(description = "Create a new todo item") public String createTodo( @ToolParam(description = "Todo title") String title, @ToolParam(description = "Todo content") String content, @ToolParam(description = "Priority (0-Low, 1-Medium, 2-High)") Integer priority, @ToolParam(description = "Due date (yyyy-MM-dd HH:mm:ss)") String dueDate, ToolContext toolContext) { try { Long userId = (Long)toolContext.getContext().get("userId"); if (userId == null) { return "用户未登录"; } TodoItem todoItem = new TodoItem(); todoItem.setUserId(userId); todoItem.setTitle(title); todoItem.setContent(content); todoItem.setPriority(priority); todoItem.setDueDate(DATE_FORMAT.parse(dueDate)); todoItem.setCreateTime(new Date()); todoItem.setUpdateTime(new Date()); todoItem.setStatus(0); boolean success = todoItemService.save(todoItem); return success ? "待办事项创建成功" : "待办事项创建失败"; } catch (Exception e) { log.error("创建待办事项失败", e); return "创建待办事项失败:" + e.getMessage(); } } /** * 更新待办事项 * @param id 待办事项ID * @param title 标题 * @param content 内容 * @param status 状态(0-未完成,1-已完成) * @param priority 优先级(0-低,1-中,2-高) * @param dueDate 截止日期 * @param toolContext 工具上下文 * @return 更新结果 */ @Tool(description = "Update a todo item") public String updateTodo( @ToolParam(description = "Todo ID") Long id, @ToolParam(description = "Todo title") String title, @ToolParam(description = "Todo content") String content, @ToolParam(description = "Status (0-Not completed, 1-Completed)") Integer status, @ToolParam(description = "Priority (0-Low, 1-Medium, 2-High)") Integer priority, @ToolParam(description = "Due date (yyyy-MM-dd HH:mm:ss)") String dueDate, ToolContext toolContext) { try { Long userId = (Long)toolContext.getContext().get("userId"); if (userId == null) { return "用户未登录"; } // 验证待办事项是否属于当前用户 TodoItem existingTodo = todoItemService.getById(id); if (existingTodo == null || !existingTodo.getUserId().equals(userId)) { return "无权操作此待办事项"; } TodoItem todoItem = new TodoItem(); todoItem.setId(id); todoItem.setTitle(title); todoItem.setContent(content); todoItem.setStatus(status); todoItem.setPriority(priority); todoItem.setDueDate(DATE_FORMAT.parse(dueDate)); todoItem.setUpdateTime(new Date()); boolean success = todoItemService.updateById(todoItem); return success ? "待办事项更新成功" : "待办事项更新失败"; } catch (Exception e) { log.error("更新待办事项失败", e); return "更新待办事项失败:" + e.getMessage(); } } /** * 删除待办事项 * @param id 待办事项ID * @param toolContext 工具上下文 * @return 删除结果 */ @Tool(description = "Delete a todo item") public String deleteTodo( @ToolParam(description = "Todo ID") Long id, ToolContext toolContext) { try { Long userId = (Long)toolContext.getContext().get("userId"); if (userId == null) { return "用户未登录"; } // 验证待办事项是否属于当前用户 TodoItem existingTodo = todoItemService.getById(id); if (existingTodo == null || !existingTodo.getUserId().equals(userId)) { return "无权操作此待办事项"; } boolean success = todoItemService.removeById(id); return success ? "待办事项删除成功" : "待办事项删除失败"; } catch (Exception e) { log.error("删除待办事项失败", e); return "删除待办事项失败:" + e.getMessage(); } } /** * 获取待办事项列表 * @param page 页码 * @param size 每页大小 * @param status 状态(可选) * @param priority 优先级(可选) * @param toolContext 工具上下文 * @return 待办事项列表 */ @Tool(description = "Get todo items list") public Map<String, Object> getTodoList( @ToolParam(description = "Page number") Integer page, @ToolParam(description = "Page size") Integer size, @ToolParam(description = "Status (0-Not completed, 1-Completed)") Integer status, @ToolParam(description = "Priority (0-Low, 1-Medium, 2-High)") Integer priority, ToolContext toolContext) { try { Long userId = (Long)toolContext.getContext().get("userId"); if (userId == null) { return Map.of("error", "用户未登录"); } LambdaQueryWrapper<TodoItem> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(TodoItem::getIsDeleted, 0) .eq(TodoItem::getUserId, userId); if (status != null) { wrapper.eq(TodoItem::getStatus, status); } if (priority != null) { wrapper.eq(TodoItem::getPriority, priority); } wrapper.orderByDesc(TodoItem::getCreateTime); Page<TodoItem> pageResult = todoItemService.page(new Page<>(page, size), wrapper); return Map.of( "total", pageResult.getTotal(), "items", pageResult.getRecords() ); } catch (Exception e) { log.error("获取待办事项列表失败", e); return Map.of("error", "获取待办事项列表失败:" + e.getMessage()); } } /** * 更新待办事项状态 * @param id 待办事项ID * @param status 状态(0-未完成,1-已完成) * @param toolContext 工具上下文 * @return 更新结果 */ @Tool(description = "Update todo item status") public String updateTodoStatus( @ToolParam(description = "Todo ID") Long id, @ToolParam(description = "Status (0-Not completed, 1-Completed)") Integer status, ToolContext toolContext) { try { Long userId = (Long)toolContext.getContext().get("userId"); if (userId == null) { return "用户未登录"; } // 验证待办事项是否属于当前用户 TodoItem existingTodo = todoItemService.getById(id); if (existingTodo == null || !existingTodo.getUserId().equals(userId)) { return "无权操作此待办事项"; } LambdaUpdateWrapper<TodoItem> wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(TodoItem::getId, id) .set(TodoItem::getStatus, status); boolean success = todoItemService.update(wrapper); return success ? "待办事项状态更新成功" : "待办事项状态更新失败"; } catch (Exception e) { log.error("更新待办事项状态失败", e); return "更新待办事项状态失败:" + e.getMessage(); } } }
注册工具

添加模拟数据

创建代办
▼java复制代码@Test void doChatWithTools3() { // 测试代办事项 testMessage("我今天下午2:30有个很紧急的会议,关于直播教学AI超级智能体的项目"); }

修改代办
修改是成功了,但是提示词是经过精心设计的,所以还是有缺陷的,这个问题还是后面优化一下吧。
▼java复制代码@Test void doChatWithTools3() { // 测试代办事项 testMessage("先查询我今天下午紧急会议的待办,并且把今天下午2:30的会议的待办改成明天下午3:35"); }

▼java复制代码@Test void doChatWithTools3() { // 测试代办事项 testMessage("id为1 这个待办我已经已经完成了"); }

评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
作者分享
llm-wiki:把 AI 会话沉淀成可长期复用的本地知识库
7
我做了一个 AI Token Dashboard:把 Claude Code、Codex、Gemini 等 AI 工具的 Token 消耗统一看清楚
13
最近公司一直在推进AI的产品落地,我负责了一块有关于数据查询AI化的部分,大致能力就是,
实现用自然语言转换成sql进行查询,得到数据,返回给用户。我找了网上很多的资料,都是介绍
用对应的mcp和插件来实现,不能满足当前需求,因为这个能力是需要单独部署成mcp工具,给很
多部门做数据分析用。所以想请教大家有没有帖子或资源,是使用代码实现的。语言java和python都可。
3
error installing 14.21.3: open C:\Users\qcdfz\AppData\Local\Temp\nvm-npm-1073152608\npm-v6.14.18.zip
2
超级AI智能体完结篇
9
