当前位置: 首页 > news >正文

GO实战项目:基于 `HTML/CSS/JS + Gin + Gorm + 文心一言API`AI 备忘录应用

AI 备忘录应用:完整项目目录与分步实现指南

基于 HTML/CSS/JS + Gin + Gorm + 文心一言API 构建,以下是完整项目目录结构可落地的分步实现方法,确保零基础也能跟随操作。

一、完整项目目录结构

plaintext

ai-memo/                  # 项目根目录
├── config/               # 配置文件目录
│   └── db.go             # 数据库连接配置
├── model/                # 数据模型目录(Gorm)
│   └── task.go           # 任务表模型定义
├── service/              # 业务服务目录
│   └── ai_service.go     # AI接口调用服务(文心一言)
├── static/               # 前端静态资源目录
│   ├── css/
│   │   └── style.css     # 页面样式文件
│   └── js/
│       └── main.js       # 前端交互逻辑
├── templates/            # HTML模板目录(Gin渲染)
│   └── index.html        # 首页(任务表单+列表)
├── go.mod                # Go依赖管理文件
├── go.sum                # Go依赖版本锁定文件
└── main.go               # 后端主程序(路由+入口)

二、分步实现方法(共 9 步)

步骤 1:环境准备(必做)

先安装基础工具,确保后续操作无依赖问题:

  1. 安装 Go 环境

    • 下载地址:https://go.dev/dl/(选择 Go 1.20 + 版本,适配 Gin 最新版)
    • 验证:终端输入 go version,显示 go version go1.2x.x xxx 即成功。
  2. 安装 MySQL 数据库

    • 下载地址:https://dev.mysql.com/downloads/mysql/(8.0 + 版本)
    • 记住数据库用户名(默认 root)和密码(安装时设置)。
  3. 申请文心一言 API 密钥

    • 访问:https://console.bce.baidu.com/qianfan/ (百度智能云千帆平台)
    • 注册登录后,创建 “应用”,获取 API KeySecret Key(后续配置用)。
  4. 安装代码编辑器

    (可选,推荐 VS Code)

    • 安装 Go 插件(搜索 “Go”)和 HTML/CSS 插件,方便编写代码。

步骤 2:创建项目目录(终端 / 命令行操作)

  1. 新建根目录

    ai-memo
    

    (路径可自定义,如桌面):

    bash

    # Windows(CMD)
    mkdir C:\Users\你的用户名\Desktop\ai-memo
    cd C:\Users\你的用户名\Desktop\ai-memo# Mac/Linux(终端)
    mkdir ~/Desktop/ai-memo
    cd ~/Desktop/ai-memo
    
  2. 创建子目录(按完整目录结构):

    bash

    # 批量创建子目录
    mkdir config model service static/css static/js templates
    
  3. 验证:打开项目根目录,确认 configstatic 等文件夹已创建。

步骤 3:初始化 Go 模块(管理依赖)

在项目根目录执行以下命令,初始化 Go 项目并导入核心依赖:

  1. 初始化 Go 模块(

    your-project-path
    

    可自定义,如

    github.com/your-name/ai-memo
    

    ):

    bash

    go mod init your-project-path
    
    • 执行后,根目录会生成 go.mod 文件(依赖配置文件)。
  2. 安装核心依赖(Gin、Gorm、MySQL 驱动、百度 API SDK):

    bash

    go get github.com/gin-gonic/gin@v1.10.0
    go get gorm.io/gorm@v1.25.3
    go get gorm.io/driver/mysql@v1.5.2
    go get github.com/baidu-idl/bce-sdk-go@v0.10.233
    
    • 执行后,根目录会生成 go.sum 文件(依赖版本锁定),且 go.mod 会新增依赖记录。

步骤 4:数据库配置与模型定义(后端核心)

4.1 创建数据库(MySQL 操作)

先在 MySQL 中创建项目专用数据库 ai_memo(后续 Gorm 会自动建表):

  1. 打开 MySQL 终端(或用 Navicat、DataGrip 等工具):

    bash

    # 登录MySQL(输入密码时按回车,然后输入你的MySQL密码)
    mysql -u root -p
    
  2. 创建数据库

    ai_memo
    

    sql

    CREATE DATABASE IF NOT EXISTS ai_memo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    
  3. 验证:输入 show databases;,看到 ai_memo 即成功,然后输入 exit; 退出 MySQL。

4.2 编写数据库配置(config/db.go)

config 目录下新建 db.go 文件,粘贴以下代码(需修改 MySQL 连接信息):

go

运行

package configimport ("your-project-path/model"  // 替换为你的Go模块名(步骤3中init的路径)"gorm.io/driver/mysql""gorm.io/gorm"
)var DB *gorm.DB  // 全局数据库对象,供其他模块调用// InitDB 初始化数据库连接(自动建表)
func InitDB() error {// 1. 修改这里的MySQL配置:root:密码@tcp(localhost:3306)/ai_memo?xxxdsn := "root:你的MySQL密码@tcp(127.0.0.1:3306)/ai_memo?charset=utf8mb4&parseTime=True&loc=Local"// 2. 连接MySQLdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {return err  // 连接失败返回错误}// 3. 自动迁移:根据model.Task创建task表(无表则建表,有表则更新结构)err = db.AutoMigrate(&model.Task{})if err != nil {return err}DB = db  // 赋值给全局变量return nil
}
  • 关键修改:将 dsn 中的 你的MySQL密码 替换为实际 MySQL 密码(如 root:123456@tcp(...))。
4.3 编写任务模型(model/task.go)

model 目录下新建 task.go 文件,粘贴以下代码(定义任务表结构):

go

运行

package modelimport ("time""gorm.io/gorm"
)// TaskStatus 任务状态枚举(限定值:pending/doing/done)
type TaskStatus string
const (StatusPending TaskStatus = "pending"  // 待执行StatusDoing   TaskStatus = "doing"    // 执行中StatusDone    TaskStatus = "done"     // 已完成
)// Task 任务表模型(Gorm会根据此结构体创建task表)
type Task struct {gorm.Model          // 内置字段:ID(主键)、CreatedAt(创建时间)、UpdatedAt(更新时间)、DeletedAt(软删除)Title       string   `gorm:"size:100;not null" json:"title"`       // 任务标题(非空,最大100字符)Content     string   `gorm:"type:text" json:"content"`             // 任务详情(长文本)DueTime     time.Time `gorm:"not null" json:"due_time"`            // 截止时间(非空)Status      TaskStatus `gorm:"default:pending" json:"status"`      // 任务状态(默认待执行)AIPlan      string   `gorm:"type:text" json:"ai_plan"`             // AI生成的任务规划AIAdvice    string   `gorm:"type:text" json:"ai_advice"`           // AI生成的执行建议
}

步骤 5:编写 AI 服务(调用文心一言 API)

service 目录下新建 ai_service.go 文件,粘贴以下代码(需替换 API 密钥):

go

运行

package serviceimport ("context""fmt""os""strings""github.com/baidubce/bce-qianfan-sdk/go/qianfan"
)// 初始化鉴权信息(通过环境变量设置AK/SK,符合SDK鉴权逻辑)
func init() {// 替换为你的百度智能云Access Key和Secret Key// 获取路径:百度智能云控制台 → 用户账户 → 安全认证 → 访问密钥err := os.Setenv("QIANFAN_ACCESS_KEY", "")if err != nil {return} // 你的Access Keyerr = os.Setenv("QIANFAN_SECRET_KEY", "")if err != nil {return} // 你的Secret Key
}// GetAIAdvice 调用百度千帆大模型API,生成任务规划和建议
// 参数:任务标题、任务详情、截止时间;返回:AI规划、AI建议、错误信息
func GetAIAdvice(title, content string, dueTime string) (plan string, advice string, err error) {// 1. 构造AI提示词(明确输出格式,便于后续解析)prompt := fmt.Sprintf(`请基于以下任务信息,严格按照【规划】和【建议】两部分生成内容,不要额外文字:1. 【规划】:包含任务优先级(高/中/低)、分步骤时间分配(适配截止时间%s);2. 【建议】:包含执行风险点(如可能拖延的环节)、资源推荐(如工具/资料)。任务标题:%s任务详情:%s`, dueTime, title, content)// 2. 初始化千帆SDK的聊天客户端(使用SDK中定义的NewChatCompletion)client := qianfan.NewChatCompletion()// 3. 构造大模型请求参数(使用SDK中定义的ChatCompletionRequest结构体)req := &qianfan.ChatCompletionRequest{Messages: []qianfan.ChatCompletionMessage{{Role:    "user", // 角色:用户(发送提示词)Content: prompt,},},Temperature: 0.7, // 生成随机性(0-1,值越小越稳定)}// 4. 调用大模型API(使用SDK中定义的Do方法)resp, err := client.Do(context.Background(), req)if err != nil {return "", "", fmt.Errorf("大模型调用失败:%v", err)}// 5. 提取大模型生成结果(直接使用SDK响应结构体的Result字段)if resp.Result == "" {return "", "", fmt.Errorf("未获取到有效生成结果")}// 6. 分割规划和建议(按约定格式提取)parts := strings.Split(resp.Result, "【建议】")if len(parts) < 2 {return "", "", fmt.Errorf("AI返回格式不符合要求(缺少【建议】):%s", resp.Result)}plan = strings.TrimPrefix(parts[0], "【规划】") // 移除【规划】前缀advice = parts[1]                           // 提取建议内容return plan, advice, nil
}
  • 关键修改:将 APIKeySecretKey 替换为步骤 1 中申请的百度智能云密钥。

步骤 6:编写后端主服务(路由 + 逻辑)

在项目根目录下新建 main.go 文件(后端入口文件),粘贴以下代码:

go

运行

package mainimport ("net/http""strconv""time""your-project-path/config"  // 替换为你的Go模块名"your-project-path/model"   // 替换为你的Go模块名"your-project-path/service" // 替换为你的Go模块名"github.com/gin-gonic/gin"
)func main() {// 第一步:初始化数据库(调用config中的InitDB)if err := config.InitDB(); err != nil {panic("数据库初始化失败:" + err.Error()) // 失败则退出程序}// 第二步:初始化Gin引擎(开启默认中间件:日志、恢复)r := gin.Default()// 第三步:配置Gin模板和静态文件r.LoadHTMLGlob("templates/*")          // 加载templates目录下的HTML模板r.Static("/static", "./static")         // 映射/static路径到static目录(前端加载CSS/JS)// 第四步:定义路由(接口+页面)// 1. 首页:GET请求,展示任务列表r.GET("/", func(c *gin.Context) {var tasks []model.Task// 从数据库查询所有任务(按创建时间倒序,最新的在前面)config.DB.Order("created_at desc").Find(&tasks)// 渲染templates/index.html,并传递tasks数据到前端c.HTML(http.StatusOK, "index.html", gin.H{"tasks": tasks})})// 2. 添加任务:POST请求,接收前端表单,调用AI生成规划r.POST("/task/add", func(c *gin.Context) {// 1. 接收前端表单参数(对应index.html中的表单name)title := c.PostForm("title")       // 任务标题content := c.PostForm("content")   // 任务详情dueTimeStr := c.PostForm("due_time")// 截止时间(前端传的是"2024-12-31T23:59"格式)// 2. 转换截止时间格式(前端T分隔 → 后端空格分隔)dueTime, err := time.Parse("2006-01-02T15:04", dueTimeStr)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"msg": "截止时间格式错误"})return}// 3. 调用AI服务生成规划和建议aiPlan, aiAdvice, err := service.GetAIAdvice(title, content, dueTime.Format("2006-01-02 15:04"), // 传给AI的时间格式)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"msg": "AI调用失败:" + err.Error()})return}// 4. 保存任务到数据库(创建model.Task对象)task := model.Task{Title:    title,Content:  content,DueTime:  dueTime,AIPlan:   aiPlan,AIAdvice: aiAdvice,Status:   model.StatusPending, // 默认状态:待执行}config.DB.Create(&task) // 插入数据库// 5. 返回成功响应给前端c.JSON(http.StatusOK, gin.H{"msg": "任务添加成功(AI规划已生成)"})})// 3. 更新任务状态:POST请求,接收任务ID和新状态r.POST("/task/status", func(c *gin.Context) {// 1. 接收前端参数(任务ID是字符串,需转为int)idStr := c.PostForm("id")id, err := strconv.Atoi(idStr)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"msg": "任务ID格式错误"})return}newStatus := model.TaskStatus(c.PostForm("status")) // 转换为TaskStatus类型// 2. 更新数据库中的任务状态(根据ID更新status字段)config.DB.Model(&model.Task{}).Where("id = ?", id).Update("status", newStatus)// 3. 返回成功响应c.JSON(http.StatusOK, gin.H{"msg": "任务状态更新成功"})})// 第五步:启动服务(监听8080端口,访问地址:http://localhost:8080)r.Run(":8080")
}
  • 关键修改:将 your-project-path 替换为步骤 3 中 go mod init 定义的模块名(如 github.com/your-name/ai-memo)。

步骤 7:编写前端代码(HTML/CSS/JS)

7.1 编写首页 HTML(templates/index.html)

templates 目录下新建 index.html 文件,粘贴以下代码:

html

预览

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>AI智能备忘录</title><!-- 加载静态CSS(对应static/css/style.css) --><link rel="stylesheet" href="/static/css/style.css">
</head>
<body><div class="container"><!-- 页面标题 --><h1>AI智能备忘录</h1><!-- 1. 任务添加表单 --><div class="add-task"><h3>添加新任务</h3><form id="taskForm"><div class="form-item"><label>任务标题:</label><input type="text" name="title" required placeholder="例如:完成Go项目报告"></div><div class="form-item"><label>任务详情:</label><textarea name="content" rows="3" placeholder="例如:包含需求分析、代码说明、测试报告"></textarea></div><div class="form-item"><label>截止时间:</label><input type="datetime-local" name="due_time" required></div><button type="submit" id="submitBtn">添加任务(AI自动规划)</button><!-- AI加载状态(默认隐藏) --><span id="loading" style="display: none; margin-left: 10px; color: #666;">AI正在生成规划...(约3秒)</span></form></div><!-- 2. 任务列表(后端传递的tasks数据会渲染这里) --><div class="task-list"><h3>我的任务</h3>{{if len .tasks}}<ul>{{range .tasks}}<li class="task-item"><!-- 任务标题+状态选择器 --><div class="task-header"><h4>{{.Title}}</h4><select class="status-select" data-id="{{.ID}}"><option value="pending" {{if eq .Status "pending"}}selected{{end}}>待执行</option><option value="doing" {{if eq .Status "doing"}}selected{{end}}>执行中</option><option value="done" {{if eq .Status "done"}}selected{{end}}>已完成</option></select></div><!-- 任务基础信息 --><div class="task-info"><p><span>详情:</span>{{if .Content}}{{.Content}}{{else}}无详情{{end}}</p><p><span>截止时间:</span>{{.DueTime.Format "2006-01-02 15:04"}}</p><p><span>创建时间:</span>{{.CreatedAt.Format "2006-01-02 15:04"}}</p></div><!-- AI生成的规划和建议 --><div class="ai-content"><p><span>AI规划:</span>{{.AIPlan}}</p><p><span>AI建议:</span>{{.AIAdvice}}</p></div></li>{{end}}</ul>{{else}}<!-- 无任务时显示 --><p class="empty">暂无任务,点击“添加新任务”开始吧!</p>{{end}}</div></div><!-- 加载静态JS(对应static/js/main.js) --><script src="/static/js/main.js"></script>
</body>
</html>
7.2 编写 CSS 样式(static/css/style.css)

static/css 目录下新建 style.css 文件,粘贴以下代码:

css

/* 全局样式重置 */
* {margin: 0;padding: 0;box-sizing: border-box;font-family: "Microsoft YaHei", Arial, sans-serif;
}body {background-color: #f5f7fa;color: #333;
}/* 容器样式(居中+宽度限制) */
.container {max-width: 1000px;margin: 30px auto;padding: 0 20px;
}h1 {color: #2c3e50;text-align: center;margin-bottom: 40px;font-size: 28px;
}h3 {color: #2c3e50;margin-bottom: 15px;font-size: 18px;
}/* 1. 任务添加表单样式 */
.add-task {background-color: white;padding: 25px;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.05);margin-bottom: 30px;
}.form-item {margin-bottom: 20px;
}.form-item label {display: block;margin-bottom: 8px;color: #666;font-weight: 500;
}.form-item input, 
.form-item textarea {width: 100%;padding: 10px 12px;border: 1px solid #ddd;border-radius: 4px;font-size: 14px;transition: border 0.3s;
}.form-item input:focus, 
.form-item textarea:focus {outline: none;border-color: #3498db;
}.form-item textarea {resize: vertical; /* 只允许垂直拉伸 */
}button {background-color: #3498db;color: white;border: none;padding: 12px 24px;border-radius: 4px;font-size: 14px;cursor: pointer;transition: background 0.3s;
}button:hover {background-color: #2980b9;
}/* 2. 任务列表样式 */
.task-list {background-color: white;padding: 25px;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}.task-item {border-bottom: 1px solid #f0f0f0;padding: 20px 0;list-style: none;
}.task-item:last-child {border-bottom: none;
}.task-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 15px;
}.task-header h4 {color: #2c3e50;font-size: 16px;
}.status-select {padding: 6px 10px;border: 1px solid #ddd;border-radius: 4px;font-size: 13px;cursor: pointer;transition: border 0.3s;
}.status-select:focus {outline: none;border-color: #3498db;
}.task-info {margin-bottom: 15px;font-size: 14px;color: #666;line-height: 1.8;
}.task-info span {color: #999;margin-right: 8px;
}/* AI内容样式(突出显示) */
.ai-content {background-color: #f8f9fa;padding: 15px;border-radius: 4px;font-size: 14px;line-height: 1.8;color: #555;
}.ai-content span {color: #3498db;font-weight: 500;margin-right: 8px;
}/* 无任务提示样式 */
.empty {text-align: center;padding: 40px 0;color: #999;font-size: 14px;background-color: #f8f9fa;border-radius: 4px;
}
7.3 编写前端 JS(static/js/main.js)

static/js 目录下新建 main.js 文件,粘贴以下代码:

javascript

运行

// 1. 任务表单提交逻辑(调用后端/add接口,带加载状态)
const taskForm = document.getElementById('taskForm');
const submitBtn = document.getElementById('submitBtn');
const loading = document.getElementById('loading');taskForm.addEventListener('submit', async (e) => {e.preventDefault(); // 阻止表单默认刷新行为// 显示加载状态,禁用按钮(防止重复提交)submitBtn.disabled = true;loading.style.display = 'inline-block';try {// 收集表单数据(转为URL编码格式,适配后端POST接收)const formData = new FormData(taskForm);const formParams = new URLSearchParams(formData);// 调用后端添加任务接口const response = await fetch('/task/add', {method: 'POST',body: formParams, // 自动设置Content-Type为application/x-www-form-urlencoded});// 解析后端响应(JSON格式)const result = await response.json();// 提示用户并刷新页面(显示新任务)alert(result.msg);window.location.reload();} catch (error) {// 捕获错误并提示alert('添加任务失败:' + error.message);} finally {// 恢复按钮和加载状态(无论成功/失败)submitBtn.disabled = false;loading.style.display = 'none';}
});// 2. 任务状态更新逻辑(实时修改,无需刷新页面)
const statusSelects = document.querySelectorAll('.status-select');statusSelects.forEach(select => {// 记录初始状态(用于更新失败时恢复)const oldStatus = select.value;// 监听下拉框变化select.addEventListener('change', async (e) => {const taskId = e.target.dataset.id; // 获取任务ID(从HTML的data-id属性)const newStatus = e.target.value;   // 获取新状态try {// 调用后端更新状态接口await fetch('/task/status', {method: 'POST',body: new URLSearchParams({id: taskId,status: newStatus})});// 更新成功:提示用户(可选)// alert('状态已更新');} catch (error) {// 更新失败:恢复初始状态并提示e.target.value = oldStatus;alert('更新状态失败:' + error.message);}});
});

步骤 8:测试运行项目

  1. 启动 MySQL 服务

    • Windows:打开 “服务”,找到 “MySQL”,确保状态为 “正在运行”;
    • Mac:通过 “系统偏好设置”→“MySQL”,点击 “Start MySQL Server”;
    • Linux:执行 sudo systemctl start mysql
  2. 启动后端服务

    在项目根目录执行以下命令(终端 / CMD):

    bash

    go run main.go
    
    • 成功提示:[GIN-debug] Listening and serving HTTP on :8080(表示服务已启动,监听 8080 端口)。
  3. 访问应用

    打开浏览器,输入地址:http://localhost:8080,即可看到 AI 备忘录页面:

    • 功能 1:填写任务标题、详情、截止时间,点击 “添加任务”,等待 AI 生成规划(约 3 秒),成功后刷新页面显示新任务;
    • 功能 2:在任务列表的下拉框中修改状态(待执行→执行中→已完成),实时更新数据库;
    • 功能 3:查看 AI 生成的 “规划” 和 “建议”(在每个任务的下方)。

步骤 9:常见问题排查

  1. 数据库连接失败
    • 错误提示:数据库初始化失败:Error 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
    • 解决:检查 config/db.go 中的 dsn,确认 MySQL 密码正确(如 root:123456 中的 123456 是你的实际密码)。
  2. AI 调用失败
    • 错误提示:AI调用失败:AI返回错误:18-Invalid API Key
    • 解决:检查 service/ai_service.go 中的 APIKeySecretKey,确认与百度智能云的应用密钥一致(注意大小写)。
  3. 前端样式不加载
    • 现象:页面无样式,CSS/JS 路径报错;
    • 解决:确保 main.gor.Static("/static", "./static") 配置正确,且 HTML 中 linkscript 的路径是 /static/css/style.css(开头带 /)。
  4. 端口被占用
    • 错误提示:bind: address already in use
    • 解决:修改 main.gor.Run(":8080") 为其他端口(如 r.Run(":8081")),访问时用 http://localhost:8081

三、功能扩展建议(可选)

  1. 任务删除 / 编辑:在 main.go 中添加 /task/delete/task/edit 接口,前端增加删除按钮和编辑表单;
  2. AI 多轮交互:在任务详情页增加 “追问 AI” 输入框,支持用户进一步细化规划(如 “如何优化这个任务步骤?”);
  3. 到期提醒:集成 Go 的定时任务库(如 github.com/robfig/cron),每天检查到期任务,通过邮件 / 短信提醒;
  4. 数据导出:添加 “导出任务” 功能,将任务列表导出为 Excel(使用 github.com/xuri/excelize/v2 库)。

按照以上步骤操作,即可完成一个完整的 “AI 自动规划任务” 备忘录应用,前后端全链路打通,支持核心功能使用。

编辑分享

待改进V2.0

功能扩展一:任务删除功能

目标:实现 “删除任务” 按钮,点击后从数据库删除任务并刷新列表。

步骤 1:后端添加删除任务路由(修改 main.go

在现有路由后新增删除逻辑:

go

运行

// 4. 删除任务:POST请求,接收任务ID并删除
r.POST("/task/delete", func(c *gin.Context) {// 解析任务IDidStr := c.PostForm("id")id, err := strconv.Atoi(idStr)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"msg": "任务ID格式错误"})return}// GORM删除任务(根据ID)config.DB.Delete(&model.Task{}, id)c.JSON(http.StatusOK, gin.H{"msg": "任务删除成功"})
})
步骤 2:前端页面添加删除按钮(修改 templates/index.html

在任务项的「状态选择器」旁添加删除按钮:

html

预览

<div class="task-header"><h4>{{.Title}}</h4><div><select class="status-select" data-id="{{.ID}}"><option value="pending" {{if eq .Status "pending"}}selected{{end}}>待执行</option><option value="doing" {{if eq .Status "doing"}}selected{{end}}>执行中</option><option value="done" {{if eq .Status "done"}}selected{{end}}>已完成</option></select><button class="delete-btn" data-id="{{.ID}}">删除</button></div>
</div>
步骤 3:前端 JS 处理删除逻辑(修改 static/js/main.js

添加删除按钮的点击事件:

javascript

运行

// 3. 任务删除逻辑
const deleteBtns = document.querySelectorAll('.delete-btn');
deleteBtns.forEach(btn => {btn.addEventListener('click', async (e) => {const taskId = e.target.dataset.id;if (confirm('确定要删除该任务吗?')) {try {await fetch('/task/delete', {method: 'POST',body: new URLSearchParams({ id: taskId })});window.location.reload(); // 刷新页面} catch (error) {alert('删除失败:' + error.message);}}});
});
步骤 4:添加删除按钮样式(修改 static/css/style.css

css

.delete-btn {background-color: #e74c3c;color: white;border: none;padding: 6px 12px;border-radius: 4px;font-size: 13px;cursor: pointer;margin-left: 10px;transition: background 0.3s;
}
.delete-btn:hover {background-color: #c0392b;
}

功能扩展二:AI 多轮交互(任务详情追问)

目标:在任务详情页添加输入框,向 AI 追问并获取实时建议。

步骤 1:后端添加 “追问 AI” 路由(修改 main.go

新增路由处理追问请求:

go

运行

// 5. AI追问:接收任务ID和问题,生成新建议
r.POST("/task/ask", func(c *gin.Context) {taskIdStr := c.PostForm("taskId")taskId, err := strconv.Atoi(taskIdStr)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"msg": "任务ID错误"})return}question := c.PostForm("question") // 追问内容// 查询任务详情var task model.Taskif err := config.DB.First(&task, taskId).Error; err != nil {c.JSON(http.StatusNotFound, gin.H{"msg": "任务不存在"})return}// 构造包含任务上下文的提示词prompt := fmt.Sprintf(`任务标题:%s详情:%s截止时间:%sAI原规划:%sAI原建议:%s我的问题:%s`, task.Title, task.Content, task.DueTime.Format("2006-01-02 15:04"), task.AIPlan, task.AIAdvice, question)// 调用AI服务(复用GetAIAdvice,忽略title和content,用prompt传完整问题)_, aiReply, err := service.GetAIAdvice("", "", prompt)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"msg": "AI调用失败"})return}c.JSON(http.StatusOK, gin.H{"reply": aiReply})
})
步骤 2:前端页面添加追问输入框(修改 templates/index.html

在任务的「AI 内容」下方新增输入框:

html

预览

<div class="ai-ask"><input type="text" id="askInput" placeholder="向AI追问(如:如何优化步骤?)" data-id="{{.ID}}"><button class="ask-btn" data-id="{{.ID}}">追问AI</button><div id="aiReply" style="display: none; margin-top: 10px; padding: 10px; background: #f8f9fa; border-radius: 4px;"><span>AI回复:</span><span id="replyContent"></span></div>
</div>
步骤 3:前端 JS 处理追问逻辑(修改 static/js/main.js

添加追问按钮的点击事件:

javascript

运行

// 4. AI追问逻辑
const askBtns = document.querySelectorAll('.ask-btn');
askBtns.forEach(btn => {btn.addEventListener('click', async (e) => {const taskId = e.target.dataset.id;const input = document.querySelector(`#askInput[data-id="${taskId}"]`);const question = input.value.trim();if (!question) {alert('请输入追问内容');return;}// 显示加载状态const loading = document.createElement('span');loading.textContent = 'AI思考中...';loading.style.marginLeft = '10px';btn.parentNode.appendChild(loading);try {const res = await fetch('/task/ask', {method: 'POST',body: new URLSearchParams({ taskId, question })});const data = await res.json();// 显示AI回复const replyDiv = document.querySelector(`#aiReply[data-id="${taskId}"]`);const replyContent = document.querySelector(`#replyContent[data-id="${taskId}"]`);replyContent.textContent = data.reply;replyDiv.style.display = 'block';} catch (err) {alert('追问失败:' + err.message);} finally {loading.remove(); // 移除加载状态}});
});
步骤 4:添加追问样式(修改 static/css/style.css

css

.ai-ask {margin-top: 20px;
}
.ai-ask input {width: calc(100% - 120px);padding: 8px 12px;border: 1px solid #ddd;border-radius: 4px;font-size: 14px;
}
.ask-btn {background-color: #2ecc71;color: white;border: none;padding: 8px 16px;border-radius: 4px;font-size: 14px;cursor: pointer;margin-left: 5px;transition: background 0.3s;
}
.ask-btn:hover {background-color: #27ae60;
}

功能扩展三:到期提醒(定时任务 + 邮件通知)

目标:每天自动检查到期任务,通过邮件提醒用户。

步骤 1:安装依赖

bash

go get github.com/robfig/cron/v3     # 定时任务库
go get github.com/jordan-wright/email # 邮件库
步骤 2:配置邮件服务(新增 config/email.go

config 目录新建 email.go,配置邮件服务器(以 QQ 邮箱为例):

go

运行

package configimport ("github.com/jordan-wright/email""net/smtp"
)// 邮件配置(需替换为你的实际信息)
const (EmailHost     = "smtp.qq.com"      // QQ邮箱SMTP服务器EmailPort     = 587                // SMTP端口EmailUsername = "你的QQ邮箱@qq.com" // 邮箱账号EmailPassword = "你的SMTP授权码"    // QQ邮箱需生成“SMTP授权码”(非登录密码)EmailFrom     = "你的QQ邮箱@qq.com" // 发件人地址
)// SendEmail 发送邮件
func SendEmail(to, subject, body string) error {e := email.NewEmail()e.From = EmailFrome.To = []string{to}e.Subject = subjecte.Text = []byte(body)return e.Send(EmailHost+":"+string(EmailPort), smtp.PlainAuth("", EmailUsername, EmailPassword, EmailHost))
}
步骤 3:添加定时任务(修改 main.go

导入依赖并启动定时任务:

go

运行

package mainimport ("aimemo/config""aimemo/model""aimemo/service""time""github.com/gin-gonic/gin""github.com/robfig/cron/v3" // 新增:定时任务库"net/http""strconv""fmt" // 新增:用于日志打印
)func main() {// ... 原有初始化代码(数据库、Gin引擎)...// 启动定时任务cronJob := cron.New()// 每天9:00执行(Cron表达式:"0 9 * * *")_, err := cronJob.AddFunc("0 9 * * *", checkDueTasks)if err != nil {panic("定时任务创建失败:" + err.Error())}cronJob.Start()// ... 原有路由代码 ...// 启动服务r.Run(":8080")
}// checkDueTasks 检查当天到期的任务并发送邮件
func checkDueTasks() {var tasks []model.TasktodayEnd := time.Now().Truncate(24 * time.Hour).Add(23*time.Hour + 59*time.Minute + 59*time.Second)config.DB.Where("due_time <= ?", todayEnd).Find(&tasks)for _, task := range tasks {userEmail := "接收提醒的邮箱@xxx.com" // 需替换为实际用户邮箱(可扩展User模型存储)subject := "任务到期提醒:" + task.Titlebody := fmt.Sprintf(`任务标题:%s详情:%s截止时间:%sAI规划:%sAI建议:%s`, task.Title, task.Content, task.DueTime.Format("2006-01-02 15:04"), task.AIPlan, task.AIAdvice)if err := config.SendEmail(userEmail, subject, body); err != nil {fmt.Printf("邮件发送失败,任务ID:%d,错误:%v\n", task.ID, err)}}
}
步骤 4:测试定时任务(可选)

若需快速测试,临时修改 Cron 表达式为 */1 * * * *(每分钟执行一次),等待 1 分钟后查看邮箱是否收到提醒,测试后改回 0 9 * * *

功能扩展四:任务导出为 Excel

目标:添加按钮,将任务列表导出为 Excel 文件。

步骤 1:安装 Excel 依赖

bash

go get github.com/xuri/excelize/v2
步骤 2:后端添加导出路由(修改 main.go

新增导出 Excel 的路由:

go

运行

// 6. 导出任务为Excel
r.GET("/task/export", func(c *gin.Context) {var tasks []model.Taskconfig.DB.Order("created_at desc").Find(&tasks)f := excelize.NewFile()defer f.Close()// 设置表头f.SetCellValue("Sheet1", "A1", "ID")f.SetCellValue("Sheet1", "B1", "标题")f.SetCellValue("Sheet1", "C1", "详情")f.SetCellValue("Sheet1", "D1", "截止时间")f.SetCellValue("Sheet1", "E1", "状态")f.SetCellValue("Sheet1", "F1", "AI规划")f.SetCellValue("Sheet1", "G1", "AI建议")f.SetCellValue("Sheet1", "H1", "创建时间")// 填充任务数据for i, task := range tasks {row := i + 2f.SetCellValue("Sheet1", fmt.Sprintf("A%d", row), task.ID)f.SetCellValue("Sheet1", fmt.Sprintf("B%d", row), task.Title)f.SetCellValue("Sheet1", fmt.Sprintf("C%d", row), task.Content)f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), task.DueTime.Format("2006-01-02 15:04"))f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), task.Status)f.SetCellValue("Sheet1", fmt.Sprintf("F%d", row), task.AIPlan)f.SetCellValue("Sheet1", fmt.Sprintf("G%d", row), task.AIAdvice)f.SetCellValue("Sheet1", fmt.Sprintf("H%d", row), task.CreatedAt.Format("2006-01-02 15:04"))}// 设置下载响应头c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")c.Header("Content-Disposition", "attachment; filename=tasks.xlsx")if err := f.WriteTo(c.Writer); err != nil {c.JSON(http.StatusInternalServerError, gin.H{"msg": "导出失败"})}
})
步骤 3:前端添加导出按钮(修改 templates/index.html

在任务列表标题旁添加按钮:

html

预览

<div class="task-list"><h3>我的任务</h3><button id="exportBtn">导出任务为Excel</button>{{if len .tasks}}<ul>...</ul>{{else}}<p class="empty">暂无任务</p>{{end}}
</div>
步骤 4:前端 JS 处理导出逻辑(修改 static/js/main.js

添加导出按钮的点击事件:

javascript

运行

// 5. 导出任务为Excel
const exportBtn = document.getElementById('exportBtn');
if (exportBtn) {exportBtn.addEventListener('click', () => {window.location.href = '/task/export'; // 跳转到导出接口,触发文件下载});
}
步骤 5:添加导出按钮样式(修改 static/css/style.css

css

#exportBtn {background-color: #3498db;color: white;border: none;padding: 10px 16px;border-radius: 4px;font-size: 14px;cursor: pointer;margin-bottom: 20px;transition: background 0.3s;
}
#exportBtn:hover {background-color: #2980b9;
}

总结

通过以上步骤,依次实现了任务删除AI 多轮交互到期提醒Excel 导出四大扩展功能。每个功能从后端路由前端页面JS 逻辑样式美化等维度逐步修改,确保功能完整且交互友好。实际部署时,需根据自身环境替换数据库密码AI 密钥邮件服务器配置等敏感信息。

http://www.dtcms.com/a/392154.html

相关文章:

  • 数据结构【堆(⼆叉树顺序结构)和⼆叉树的链式结构】
  • 我爱学算法之—— 位运算(下)
  • LeetCode第364题_加权嵌套序列和II
  • 云计算和云手机之间的关系
  • 胡服骑射对中国传统文化的影响
  • leetcode-hot-100 (多维动态规划)
  • Chromium 138 编译指南 Ubuntu 篇:depot_tools安装与配置(三)
  • 在Ubuntu 16.04上安装openjdk-6/7/8-jdk的步骤
  • 小杰机器学习高级(four)——基于框架的逻辑回归
  • 基于AI分类得视频孪生鹰眼图像三维逆变换矫正算法
  • [Tongyi] 智能代理搜索范式 | 决策->行动->观察(循环迭代)
  • FLink:窗口分配器(Window Assigners)指定窗口的类型
  • GO实战项目:流量统计系统完整实现(Go+XORM+MySQL + 前端)
  • 零基础-动手学深度学习-13.10. 转置卷积
  • 【Math】初三第一、二单元测试卷(测试稿)
  • 2.Spring AI的聊天模型
  • 【连载6】 C# MVC 日志管理最佳实践:归档清理与多目标输出配置
  • autodl平台jupyterLab的使用
  • React学习教程,从入门到精通,React 开发环境与工具详解 —— 语法知识点、使用方法与案例代码(25)
  • 【C++】容器进阶:deque的“双端优势” vs list的“链式灵活” vs vector的“连续高效”
  • llm的ReAct
  • C++ 参数传递方式详解
  • 前端实战开发(一):从参数优化到布局通信的全流程解决方案
  • iOS 层级的生命周期按三部分(App / UIViewController / UIView)
  • 第一章 自然语言处理领域应用
  • GitHub又打不开了?
  • OpenAI回归机器人:想把大模型推向物理世界
  • QML学习笔记(五)QML新手入门其三:通过Row和Colunm进行简单布局
  • 按键检测函数
  • CTFshow系列——PHP特性Web109-112