From 6620192322d66b5cf055549c8eec49a530ab3b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=A0=E9=97=BB=E9=A3=8E?= Date: Tue, 23 Jun 2026 21:47:16 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AD=BE=E5=88=B0=E6=A3=80=E6=9F=A5=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=A2=9E=E5=BC=BA=EF=BC=9A=E8=BF=94=E5=9B=9E=E7=AD=BE?= =?UTF-8?q?=E5=88=B0=E6=97=B6=E9=97=B4=E5=92=8C=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - 用户询问'我什么时候签到的'时,AI 说'系统只记录是否签到,没有时间戳' - 实际上数据库 signs 表有完整的 sign_time 和 sign_text 字段 解决方案: - 修改 executeCheck 方法,改用 ListSigns 查询今天的签到记录 - 返回完整的签到详情:签到时间(HH:MM:SS格式)+ 签到内容 - 未签到时仍返回简单提示 改动内容: - executeCheck 使用 ListSigns 替代 HasSignedOnDay - 构建 ListOptions 查询今天的签到记录(过滤 NodeID + 时间范围) - 返回格式:'XXX 今天已经签到过了。\n签到时间:10:30:45\n签到内容:上海-Kevin-GAT562签到' - 更新测试验证返回内容包含时间和签到文本 - 更新 mockSignStore.ListSigns 支持 NodeID 和 Limit 过滤 使用效果: - 用户:'我今天签到了吗?' → 返回是否签到 + 时间 + 内容 - 用户:'我什么时候签到的?' → 返回签到时间:10:30:45 测试: - ✅ 未签到场景测试通过 - ✅ 已签到场景测试通过(验证时间和内容) - ✅ 所有签到工具测试通过 - ✅ 项目编译成功 Co-Authored-By: Claude Fable 5 --- doc/SIGN_QUERY_FEATURE.md | 14 ++++++++---- internal/agents/sign/sign.go | 36 ++++++++++++++++++++++++------- internal/agents/sign/sign_test.go | 21 +++++++++++++++++- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/doc/SIGN_QUERY_FEATURE.md b/doc/SIGN_QUERY_FEATURE.md index 489d563..cb2ba36 100644 --- a/doc/SIGN_QUERY_FEATURE.md +++ b/doc/SIGN_QUERY_FEATURE.md @@ -32,9 +32,9 @@ ### 3. 检查操作 (action=check) **新增** 检查当前节点今天是否已签到: -- 用于回答"我今天签到了吗"之类的问题 +- 用于回答"我今天签到了吗"、"我什么时候签到的"之类的问题 - 直接查询数据库,不依赖对话历史 -- 返回明确的已签到/未签到状态 +- 返回明确的签到状态,**包括签到时间和签到内容** ## 修改内容 @@ -102,15 +102,21 @@ AI 调用: ``` 返回(未签到): -``` +```text Test Node 今天还没有签到。 ``` 返回(已签到): -``` +```text Test Node 今天已经签到过了。 +签到时间:10:30:45 +签到内容:上海闵行-Kevin-GAT562签到 ``` +**用户**:"我什么时候签到的?" + +AI 同样调用 check 操作,返回包含签到时间和内容的完整信息。 + #### 查询今天的签到情况 ```json { diff --git a/internal/agents/sign/sign.go b/internal/agents/sign/sign.go index d5ccaa2..1dd2382 100644 --- a/internal/agents/sign/sign.go +++ b/internal/agents/sign/sign.go @@ -179,7 +179,7 @@ func (t *Tool) executeSign(ctx context.Context, params signParams, runtime agent return fmt.Sprintf("签到成功!%s\n签到内容:%s", displayName(node), record.SignText), nil } -// executeCheck 检查当前节点今天是否已签到 +// executeCheck 检查当前节点今天是否已签到,并返回签到详情 func (t *Tool) executeCheck(ctx context.Context, params signParams, runtime agenttool.Runtime) (string, error) { // 节点身份来自消息上下文 node, ok := agenttool.NodeContextFromContext(ctx) @@ -192,16 +192,36 @@ func (t *Tool) executeCheck(ctx context.Context, params signParams, runtime agen now = time.Now() } - // 查询数据库检查今天是否已签到 - signed, err := t.store.HasSignedOnDay(node.NodeID, now) - if err != nil { - return "", fmt.Errorf("检查签到状态失败:%w", err) + // 构建查询选项:查询今天的签到记录 + loc := now.Location() + if loc == nil { + loc = time.Local + } + start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc) + end := start.AddDate(0, 0, 1) + + opts := storepkg.ListOptions{ + NodeID: node.NodeID, + Since: &start, + Until: &end, + Limit: 1, } - if signed { - return fmt.Sprintf("%s 今天已经签到过了。", displayName(node)), nil + // 查询数据库获取今天的签到记录 + signs, err := t.store.ListSigns(opts) + if err != nil { + return "", fmt.Errorf("查询签到记录失败:%w", err) } - return fmt.Sprintf("%s 今天还没有签到。", displayName(node)), nil + + if len(signs) == 0 { + return fmt.Sprintf("%s 今天还没有签到。", displayName(node)), nil + } + + // 返回签到详情 + sign := signs[0] + signTimeStr := sign.SignTime.Format("15:04:05") + return fmt.Sprintf("%s 今天已经签到过了。\n签到时间:%s\n签到内容:%s", + displayName(node), signTimeStr, sign.SignText), nil } // executeQuery 执行查询操作 diff --git a/internal/agents/sign/sign_test.go b/internal/agents/sign/sign_test.go index 02147cb..80cddc0 100644 --- a/internal/agents/sign/sign_test.go +++ b/internal/agents/sign/sign_test.go @@ -85,6 +85,11 @@ func (m *mockSignStore) CountSignsByDay(opts storepkg.ListOptions) ([]storepkg.S func (m *mockSignStore) ListSigns(opts storepkg.ListOptions) ([]storepkg.SignRecord, error) { var result []storepkg.SignRecord for _, sign := range m.signs { + // 过滤 NodeID + if opts.NodeID != "" && sign.NodeID != opts.NodeID { + continue + } + // 过滤时间范围 if opts.Since != nil && sign.SignTime.Before(*opts.Since) { continue } @@ -93,6 +98,10 @@ func (m *mockSignStore) ListSigns(opts storepkg.ListOptions) ([]storepkg.SignRec } result = append(result, sign) } + // 应用 Limit + if opts.Limit > 0 && len(result) > opts.Limit { + result = result[:opts.Limit] + } return result, nil } @@ -261,12 +270,13 @@ func TestSignTool_CheckAction(t *testing.T) { // 测试场景2:今天已签到 t.Run("今天已签到", func(t *testing.T) { + signTime := time.Date(2024, 6, 23, 10, 30, 45, 0, time.UTC) store := &mockSignStore{ signs: []storepkg.SignRecord{ { NodeID: "test_node_123", SignText: "上海-TestUser-TestDevice签到", - SignTime: now, + SignTime: signTime, }, }, nodeInfoMap: make(map[string]*storepkg.NodeInfoRecord), @@ -300,6 +310,15 @@ func TestSignTool_CheckAction(t *testing.T) { if !contains(result, "已经签到") { t.Errorf("Expected result to indicate already signed") } + if !contains(result, "签到时间") { + t.Errorf("Expected result to contain sign time") + } + if !contains(result, "10:30:45") { + t.Errorf("Expected result to contain the exact sign time") + } + if !contains(result, "签到内容") { + t.Errorf("Expected result to contain sign text") + } }) }