Skip to content

Commit a46adf0

Browse files
committed
✨ feat(larkmsg): 为手工管理卡添加统一操作记录折叠块
- 新增 CardActionHistoryOptions 和 CardActionHistoryRecord 结构体 - 实现 CardActionHistoryPanel 函数用于生成操作记录面板 - 在 StandardCardFooterOptions 中添加 ActionHistory 字段 - 新增 CollapsiblePanel 组件用于展示可折叠内容 - 实现操作记录加载、合并和渲染逻辑 ♻️ refactor(config): 在配置卡片中集成操作记录 - 在 ConfigCardViewOptions 和 FeatureCardViewOptions 中添加 MessageID 和 PendingHistory 字段 - 修改 BuildConfigCardWithOptions 和 BuildFeatureCardWithOptions 以支持操作记录 - 添加测试验证操作记录面板是否正确显示 ♻️ refactor(permission): 在权限卡片中集成操作记录 - 在 PermissionCardViewOptions 中添加 MessageID 和 PendingHistory 字段 - 修改 BuildPermissionCardJSONWithOptions 以支持操作记录 - 重构权限列表查询逻辑为可替换的变量 - 更新权限卡片测试用例 ♻️ refactor(schedule): 在计划卡片中集成操作记录 - 在 TaskCardViewState 中添加 MessageID 和 PendingHistory 字段 - 修改 BuildTaskListCard 以支持操作记录 - 更新计划卡片测试用例 ♻️ refactor(ratelimit): 在频控卡片中集成操作记录 - 新增 StatsCardOptions 结构体包含 MessageID 和 PendingHistory - 添加 BuildStatsCardJSONWithOptions 方法 - 修改 buildStatsRawCard 以支持操作记录 - 更新频控卡片测试用例 ♻️ refactor(cardaction): 在卡片回调中透传操作记录 - 在各类卡片回调处理中统一添加 MessageID 和 PendingHistory - 修改 handleConfigView、handleFeatureView 等函数 - 确保操作记录在卡片重建时能够正确传递 📝 docs(architecture): 更新运行时重构进度文档 - 添加 2026-03-11 里程碑 Q 的详细方案说明 - 记录所有相关代码修改点 - 说明设计决策和验证方法
1 parent 4d85700 commit a46adf0

File tree

19 files changed

+947
-21
lines changed

19 files changed

+947
-21
lines changed

docs/architecture/runtime-refactor-progress.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,3 +634,48 @@
634634
### 验证
635635

636636
- `GOCACHE=/tmp/gocache go test ./internal/infrastructure/lark_dal/larkmsg ./internal/application/permission ./internal/application/lark/schedule ./internal/application/lark/cardaction ./internal/application/config`
637+
638+
## 2026-03-11 · Milestone Q · 手工管理卡统一补操作记录折叠块
639+
640+
### 方案
641+
642+
- 给手工 schema v2 管理卡统一增加一个通用 `collapsible_panel`,用于展示当前卡片消息维度下的操作历史。
643+
- 历史数据继续复用已有的 OpenSearch 卡片操作审计索引,不再引入新的 DB 表或第二套审计链路。
644+
- 卡片回调重建时显式透传当前 `open_message_id`,并把“本次正在处理的回调动作”先以内存记录合并进面板,避免 OpenSearch 异步写入尚未完成时用户看不到刚点下去的那次操作。
645+
646+
### 修改
647+
648+
- `internal/infrastructure/lark_dal/larkmsg/*`
649+
- 新增 / 补全 `CardActionHistoryOptions``CardActionHistoryRecord``CardActionHistoryPanel(...)`
650+
- `StandardCardFooterOptions` 增加 `ActionHistory`
651+
- footer 在按钮区上方统一插入操作记录折叠块
652+
- `collapsible_panel` helper 修正为符合 Feishu Card JSON 2.0 的合法结构
653+
- 历史项渲染统一为:动作 + 参与人 + 时间
654+
- `internal/infrastructure/lark_dal/larkmsg/record.go`
655+
- 卡片操作审计补齐 `open_message_id``open_chat_id``action_name`
656+
- `internal/application/config/*`
657+
- `config / feature` 卡片 footer 接入统一操作记录
658+
- view options 增加 `MessageID / PendingHistory`
659+
- `internal/application/permission/*`
660+
- 权限卡 footer 接入统一操作记录
661+
- 权限卡内部读取 bot identity / subject list 增加测试可替换 hook
662+
- `internal/application/lark/schedule/*`
663+
- schedule 管理卡 footer 接入统一操作记录
664+
- view state 增加 `MessageID / PendingHistory`
665+
- `internal/application/lark/ratelimit/*`
666+
- ratelimit 手工卡接入统一操作记录
667+
- `internal/application/lark/cardaction/builtin.go`
668+
- config / feature / permission / schedule / ratelimit 回调重建时统一透传:
669+
- `MessageID`
670+
- `PendingHistory: []CardActionHistoryRecord{NewCardActionHistoryRecord(actionCtx.Event)}`
671+
672+
### 决策
673+
674+
- 操作记录按“当前卡片消息”聚合,而不是按 chat / user / resource 聚合;这样语义最直观,也不会把不同卡片实例的操作串在一起。
675+
- 不等待 OpenSearch 写入完成再回包,而是把当前回调动作先以内存 `PendingHistory` 合并显示;用户看到的是即时结果,后续持久化仍走原异步审计路径。
676+
- 折叠块默认收起,避免压缩正文可视区域;无消息 ID 时显示占位提示,而不是静默隐藏。
677+
- `collapsible_panel` 结构按 Feishu 官方 JSON 2.0 文档修正,避免继续发送旧式非法 `header / border` 结构。
678+
679+
### 验证
680+
681+
- `GOCACHE=/tmp/gocache go test ./internal/infrastructure/lark_dal/larkmsg ./internal/application/config ./internal/application/permission ./internal/application/lark/schedule ./internal/application/lark/ratelimit ./internal/application/lark/cardaction`

internal/application/config/card.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package config
33
import (
44
"context"
55
"strconv"
6+
7+
"github.com/BetaGoRobot/BetaGo-Redefine/internal/infrastructure/lark_dal/larkmsg"
68
)
79

810
type configLookupCandidate struct {
@@ -15,10 +17,14 @@ type configLookupCandidate struct {
1517
type ConfigCardViewOptions struct {
1618
BypassCache bool
1719
LastModifierOpenID string
20+
MessageID string
21+
PendingHistory []larkmsg.CardActionHistoryRecord
1822
}
1923

2024
type FeatureCardViewOptions struct {
2125
LastModifierOpenID string
26+
MessageID string
27+
PendingHistory []larkmsg.CardActionHistoryRecord
2228
}
2329

2430
// ==========================================

internal/application/config/card_view.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func BuildConfigCardWithOptions(ctx context.Context, scope, chatID, openID strin
3838
return newRawCard(ctx, "配置面板", elements, larkmsg.StandardCardFooterOptions{
3939
RefreshPayload: larkmsg.StringMapToAnyMap(BuildConfigViewValueWithState(view)),
4040
LastModifierOpenID: options.LastModifierOpenID,
41+
ActionHistory: larkmsg.CardActionHistoryOptions{
42+
Enabled: true,
43+
OpenMessageID: options.MessageID,
44+
PendingRecords: options.PendingHistory,
45+
},
4146
}), nil
4247
}
4348

@@ -71,6 +76,11 @@ func BuildFeatureCardWithOptions(ctx context.Context, chatID, openID string, opt
7176
return newRawCard(ctx, "功能开关", elements, larkmsg.StandardCardFooterOptions{
7277
RefreshPayload: larkmsg.StringMapToAnyMap(BuildFeatureViewValueWithState(view)),
7378
LastModifierOpenID: options.LastModifierOpenID,
79+
ActionHistory: larkmsg.CardActionHistoryOptions{
80+
Enabled: true,
81+
OpenMessageID: options.MessageID,
82+
PendingRecords: options.PendingHistory,
83+
},
7484
}), nil
7585
}
7686

internal/application/config/card_view_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,38 @@ func TestBuildConfigScopeRowContainsViewActionPayload(t *testing.T) {
169169
}
170170
}
171171

172+
func TestBuildConfigCardAddsOperationHistoryPanel(t *testing.T) {
173+
useWorkspaceConfigPath(t)
174+
card, err := BuildConfigCardWithOptions(context.Background(), "chat", "chat-1", "user-1", ConfigCardViewOptions{})
175+
if err != nil {
176+
t.Fatalf("BuildConfigCardWithOptions() error = %v", err)
177+
}
178+
raw, err := json.Marshal(card)
179+
if err != nil {
180+
t.Fatalf("Marshal() error = %v", err)
181+
}
182+
jsonStr := string(raw)
183+
if !strings.Contains(jsonStr, `"tag":"collapsible_panel"`) || !strings.Contains(jsonStr, `操作记录`) {
184+
t.Fatalf("expected operation history panel in config card: %s", jsonStr)
185+
}
186+
}
187+
188+
func TestBuildFeatureCardAddsOperationHistoryPanel(t *testing.T) {
189+
useWorkspaceConfigPath(t)
190+
card, err := BuildFeatureCardWithOptions(context.Background(), "chat-1", "user-1", FeatureCardViewOptions{})
191+
if err != nil {
192+
t.Fatalf("BuildFeatureCardWithOptions() error = %v", err)
193+
}
194+
raw, err := json.Marshal(card)
195+
if err != nil {
196+
t.Fatalf("Marshal() error = %v", err)
197+
}
198+
jsonStr := string(raw)
199+
if !strings.Contains(jsonStr, `"tag":"collapsible_panel"`) || !strings.Contains(jsonStr, `操作记录`) {
200+
t.Fatalf("expected operation history panel in feature card: %s", jsonStr)
201+
}
202+
}
203+
172204
func TestBuildConfigCustomValueFormContainsInputAndSubmit(t *testing.T) {
173205
element := buildConfigCustomValueForm(ConfigItem{
174206
Key: string(KeyReactionDefaultRate),

internal/application/lark/cardaction/builtin.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
appratelimit "github.com/BetaGoRobot/BetaGo-Redefine/internal/application/lark/ratelimit"
1212
scheduleapp "github.com/BetaGoRobot/BetaGo-Redefine/internal/application/lark/schedule"
1313
apppermission "github.com/BetaGoRobot/BetaGo-Redefine/internal/application/permission"
14+
"github.com/BetaGoRobot/BetaGo-Redefine/internal/infrastructure/lark_dal/larkmsg"
1415
cardactionproto "github.com/BetaGoRobot/BetaGo-Redefine/pkg/cardaction"
1516
"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher/callback"
1617
)
@@ -117,6 +118,8 @@ func handleFeatureAction(ctx context.Context, actionCtx *Context) (*callback.Car
117118
}
118119
card, cardErr := appconfig.BuildFeatureCardWithOptions(ctx, actionCtx.ChatID(), actionCtx.OpenID(), appconfig.FeatureCardViewOptions{
119120
LastModifierOpenID: actionCtx.OpenID(),
121+
MessageID: actionCtx.MessageID(),
122+
PendingHistory: []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)},
120123
})
121124
if cardErr != nil {
122125
return InfoToast(resp.Message), nil
@@ -139,6 +142,8 @@ func handleFeatureView(ctx context.Context, actionCtx *Context) (*callback.CardA
139142
}
140143
card, err := appconfig.BuildFeatureCardWithOptions(ctx, chatID, openID, appconfig.FeatureCardViewOptions{
141144
LastModifierOpenID: req.LastModifierOpenID,
145+
MessageID: actionCtx.MessageID(),
146+
PendingHistory: []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)},
142147
})
143148
if err != nil {
144149
return ErrorToast(err.Error()), nil
@@ -160,6 +165,8 @@ func handleConfigAction(ctx context.Context, actionCtx *Context) (*callback.Card
160165
card, cardErr := appconfig.BuildConfigCardJSONWithOptions(ctx, req.Scope, req.ChatID, req.OpenID, appconfig.ConfigCardViewOptions{
161166
BypassCache: true,
162167
LastModifierOpenID: actionCtx.OpenID(),
168+
MessageID: actionCtx.MessageID(),
169+
PendingHistory: []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)},
163170
})
164171
if cardErr != nil {
165172
return InfoToast(resp.Message), nil
@@ -175,6 +182,8 @@ func handleConfigView(ctx context.Context, actionCtx *Context) (*callback.CardAc
175182
card, err := appconfig.BuildConfigCardJSONWithOptions(ctx, req.Scope, req.ChatID, req.OpenID, appconfig.ConfigCardViewOptions{
176183
BypassCache: true,
177184
LastModifierOpenID: req.LastModifierOpenID,
185+
MessageID: actionCtx.MessageID(),
186+
PendingHistory: []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)},
178187
})
179188
if err != nil {
180189
return ErrorToast(err.Error()), nil
@@ -195,6 +204,8 @@ func handlePermissionAction(ctx context.Context, actionCtx *Context) (*callback.
195204
}
196205
card, cardErr := apppermission.BuildPermissionCardJSONWithOptions(ctx, actionCtx.ChatID(), actionCtx.OpenID(), req.TargetOpenID, apppermission.PermissionCardViewOptions{
197206
LastModifierOpenID: actionCtx.OpenID(),
207+
MessageID: actionCtx.MessageID(),
208+
PendingHistory: []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)},
198209
})
199210
if cardErr != nil {
200211
return InfoToast(resp.Message), nil
@@ -209,6 +220,8 @@ func handlePermissionView(ctx context.Context, actionCtx *Context) (*callback.Ca
209220
}
210221
card, err := apppermission.BuildPermissionCardJSONWithOptions(ctx, actionCtx.ChatID(), actionCtx.OpenID(), req.TargetOpenID, apppermission.PermissionCardViewOptions{
211222
LastModifierOpenID: req.LastModifierOpenID,
223+
MessageID: actionCtx.MessageID(),
224+
PendingHistory: []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)},
212225
})
213226
if err != nil {
214227
return ErrorToast(err.Error()), nil
@@ -228,7 +241,10 @@ func handleRateLimitView(ctx context.Context, actionCtx *Context) (*callback.Car
228241
if chatID == "" && actionCtx.MetaData != nil {
229242
chatID = strings.TrimSpace(actionCtx.MetaData.ChatID)
230243
}
231-
card, err := appratelimit.BuildStatsCardJSON(ctx, chatID)
244+
card, err := appratelimit.BuildStatsCardJSONWithOptions(ctx, chatID, appratelimit.StatsCardOptions{
245+
MessageID: actionCtx.MessageID(),
246+
PendingHistory: []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)},
247+
})
232248
if err != nil {
233249
return ErrorToast(err.Error()), nil
234250
}
@@ -245,6 +261,8 @@ func handleScheduleView(ctx context.Context, actionCtx *Context) (*callback.Card
245261
if chatID == "" && actionCtx.MetaData != nil {
246262
chatID = strings.TrimSpace(actionCtx.MetaData.ChatID)
247263
}
264+
req.View.MessageID = actionCtx.MessageID()
265+
req.View.PendingHistory = []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)}
248266
card, err := scheduleapp.BuildTaskCardPayloadForView(ctx, chatID, req.View, true)
249267
if err != nil {
250268
return ErrorToast(err.Error()), nil
@@ -289,6 +307,8 @@ func handleScheduleAction(ctx context.Context, actionCtx *Context) (*callback.Ca
289307
}
290308

291309
req.View.LastModifierOpenID = actorOpenID
310+
req.View.MessageID = actionCtx.MessageID()
311+
req.View.PendingHistory = []larkmsg.CardActionHistoryRecord{larkmsg.NewCardActionHistoryRecord(actionCtx.Event)}
292312
card, cardErr := scheduleapp.BuildTaskCardPayloadForView(ctx, chatID, req.View, req.Action == scheduleapp.TaskActionDelete)
293313
if cardErr != nil {
294314
return InfoToast(message), nil

internal/application/lark/ratelimit/stats_card.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,17 @@ type StatsCardRecentSend struct {
3838
}
3939

4040
func BuildStatsCardJSON(ctx context.Context, chatID string) (map[string]any, error) {
41+
return BuildStatsCardJSONWithOptions(ctx, chatID, StatsCardOptions{})
42+
}
43+
44+
type StatsCardOptions struct {
45+
MessageID string
46+
PendingHistory []larkmsg.CardActionHistoryRecord
47+
}
48+
49+
func BuildStatsCardJSONWithOptions(ctx context.Context, chatID string, opts StatsCardOptions) (map[string]any, error) {
4150
data := BuildStatsCardData(ctx, chatID)
42-
card := buildStatsRawCard(ctx, data)
51+
card := buildStatsRawCardWithOptions(ctx, data, opts)
4352
return map[string]any(card), nil
4453
}
4554

@@ -165,6 +174,10 @@ func buildStatsCardData(snapshot *StatsSnapshot) *StatsCardData {
165174
}
166175

167176
func buildStatsRawCard(ctx context.Context, data *StatsCardData) larkmsg.RawCard {
177+
return buildStatsRawCardWithOptions(ctx, data, StatsCardOptions{})
178+
}
179+
180+
func buildStatsRawCardWithOptions(ctx context.Context, data *StatsCardData, opts StatsCardOptions) larkmsg.RawCard {
168181
elements := []any{
169182
buildStatsOverviewBlock(data),
170183
}
@@ -189,6 +202,11 @@ func buildStatsRawCard(ctx context.Context, data *StatsCardData) larkmsg.RawCard
189202

190203
return larkmsg.NewStandardPanelCard(ctx, "频控详情", elements, larkmsg.StandardCardFooterOptions{
191204
RefreshPayload: larkmsg.StringMapToAnyMap(BuildStatsViewValue(data.ChatID)),
205+
ActionHistory: larkmsg.CardActionHistoryOptions{
206+
Enabled: true,
207+
OpenMessageID: opts.MessageID,
208+
PendingRecords: opts.PendingHistory,
209+
},
192210
})
193211
}
194212

internal/application/lark/ratelimit/stats_card_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ func TestBuildStatsRawCardUsesSchemaV2Structure(t *testing.T) {
155155
if !strings.Contains(jsonStr, `"content":"刷新"`) || !strings.Contains(jsonStr, `"action":"ratelimit.view"`) {
156156
t.Fatalf("expected refresh footer action in json: %s", jsonStr)
157157
}
158+
if !strings.Contains(jsonStr, `"tag":"collapsible_panel"`) || !strings.Contains(jsonStr, `操作记录`) {
159+
t.Fatalf("expected operation history panel in json: %s", jsonStr)
160+
}
158161
if !strings.Contains(jsonStr, `"text_size":"heading-1"`) {
159162
t.Fatalf("expected emphasized overview text in json: %s", jsonStr)
160163
}

internal/application/lark/schedule/card_action.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/BetaGoRobot/BetaGo-Redefine/internal/infrastructure/db/model"
10+
"github.com/BetaGoRobot/BetaGo-Redefine/internal/infrastructure/lark_dal/larkmsg"
1011
cardactionproto "github.com/BetaGoRobot/BetaGo-Redefine/pkg/cardaction"
1112
)
1213

@@ -49,6 +50,8 @@ type TaskCardViewState struct {
4950
ToolName string
5051
CreatorOpenID string
5152
LastModifierOpenID string
53+
MessageID string
54+
PendingHistory []larkmsg.CardActionHistoryRecord
5255
Limit int
5356
}
5457

@@ -198,6 +201,7 @@ func normalizeTaskCardView(view TaskCardViewState) TaskCardViewState {
198201
view.ToolName = strings.TrimSpace(view.ToolName)
199202
view.CreatorOpenID = strings.TrimSpace(view.CreatorOpenID)
200203
view.LastModifierOpenID = strings.TrimSpace(view.LastModifierOpenID)
204+
view.MessageID = strings.TrimSpace(view.MessageID)
201205

202206
if view.Limit <= 0 {
203207
if view.Mode == TaskCardViewModeList {

internal/application/lark/schedule/card_view.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,14 @@ func BuildTaskListCard(ctx context.Context, title string, tasks []*model.Schedul
3131
}
3232
elements = larkmsg.AppendSectionsWithDividers(elements, taskSections...)
3333
}
34-
3534
return larkmsg.NewStandardPanelCard(ctx, title, elements, larkmsg.StandardCardFooterOptions{
3635
RefreshPayload: larkmsg.StringMapToAnyMap(BuildTaskViewValue(view)),
3736
LastModifierOpenID: view.LastModifierOpenID,
37+
ActionHistory: larkmsg.CardActionHistoryOptions{
38+
Enabled: true,
39+
OpenMessageID: view.MessageID,
40+
PendingRecords: view.PendingHistory,
41+
},
3842
})
3943
}
4044

@@ -256,7 +260,7 @@ func buildTaskCreatorPicker(view TaskCardViewState) map[string]any {
256260
Width: "default",
257261
Type: "default",
258262
Payload: larkmsg.StringMapToAnyMap(BuildTaskCreatorPickerValue(view)),
259-
ElementID: "schedule_creator_filter",
263+
ElementID: "sched_creator_pick",
260264
})
261265
}
262266

internal/application/lark/schedule/card_view_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ func TestBuildTaskListCardUsesSchemaV2AndFooter(t *testing.T) {
7474
if !strings.Contains(jsonStr, `"content":"刷新"`) || !strings.Contains(jsonStr, cardactionproto.ActionScheduleView) {
7575
t.Fatalf("expected schedule refresh action in card json: %s", jsonStr)
7676
}
77+
if !strings.Contains(jsonStr, `"tag":"collapsible_panel"`) || !strings.Contains(jsonStr, `首次发送后可在此查看操作记录`) {
78+
t.Fatalf("expected shared action history panel in card json: %s", jsonStr)
79+
}
7780
if !strings.Contains(jsonStr, cardactionproto.ActionSchedulePause) ||
7881
!strings.Contains(jsonStr, cardactionproto.ActionScheduleResume) ||
7982
!strings.Contains(jsonStr, cardactionproto.ActionScheduleDelete) {
@@ -85,10 +88,13 @@ func TestBuildTaskListCardUsesSchemaV2AndFooter(t *testing.T) {
8588
if !strings.Contains(jsonStr, "创建者") || !strings.Contains(jsonStr, "ou_creator") {
8689
t.Fatalf("expected creator info and filter row in card json: %s", jsonStr)
8790
}
88-
if !strings.Contains(jsonStr, `"tag":"select_person"`) || !strings.Contains(jsonStr, `"element_id":"schedule_creator_filter"`) {
91+
if !strings.Contains(jsonStr, `"tag":"select_person"`) || !strings.Contains(jsonStr, `"element_id":"sched_creator_pick"`) {
8992
t.Fatalf("expected select_person creator picker in card json: %s", jsonStr)
9093
}
9194
if strings.Contains(jsonStr, `"options":[`) {
9295
t.Fatalf("expected creator picker to default to current chat members, not static options: %s", jsonStr)
9396
}
97+
if !strings.Contains(jsonStr, `"tag":"collapsible_panel"`) || !strings.Contains(jsonStr, `操作记录`) {
98+
t.Fatalf("expected operation history panel in schedule card: %s", jsonStr)
99+
}
94100
}

0 commit comments

Comments
 (0)