skills/mercury-api.wepieoa.com/activity-integration-doc

activity-integration-doc

SKILL.md

活动前后端联调文档生成器

根据 活动配置 + Go 代码 + 需求文档,自动生成一份面向前端开发的联调文档,减少沟通成本、避免参数填错。


适用场景

  • 活动后端开发完成,需要给前端一份接口对接文档
  • 新活动启动联调前,整理碎片 ID、接口列表和参数含义
  • 活动复用旧玩法,需确认当前活动中组件接口应填什么参数
  • 活动临近上线,补充联调文档用于测试和验收

输入参数

用户只需提供活动 ID 和区服,其余信息自动获取:

参数 说明 必须 示例
act_id 活动 ID 6553
region 区服代号 O(Jackaroo)
code_dir 活动代码目录 否(自动搜索) app/activity/2026/jk/jk_fire_song
env 配置环境 dev(默认)或 prod

飞书需求文档链接无需用户提供:从活动配置 JSON 的 doc_url 字段自动获取。

区服代号对照表

代号 区服 代号 区服
C 华语服 K 韩服
T 泰服 A 阿语服
M 马来服 Q 土语服
P 菲律宾服 O Jackaroo
V 越南服 G 德语服
U 美服 I 印度服
B 葡语服 N 巴基斯坦服
S 西语服 R 俄语服
Y 意大利服 J 日服

执行步骤

步骤 0:前置条件检查(硬门控,必须通过才能继续)

活动配置是生成联调文档的唯一数据基础,任何原因导致配置拉取失败,立即停止执行,不进入步骤 1

调用 fetch-game-config-clean skill(namespace=activity,region=用户提供的区服代号,key=活动 ID,env=dev(默认,用户未指定时使用)或 prod),根据结果分三种情况处理:

情况 A:ConfigSecret 未设置

立即停止,输出以下信息后不再继续任何步骤:

无法拉取活动配置:环境变量 ConfigSecret 未设置。

请在终端执行以下命令后重试:
  export ConfigSecret=<你的配置中心密钥>

ConfigSecret 可从团队配置文档或后端同学处获取。

情况 B:网络错误或其他拉取失败

立即停止,报告具体错误信息,提示用户检查网络后重试,不继续执行。

情况 C:配置拉取成功

保存返回的 JSON,继续执行步骤 1。

禁止跳过:不接受以本地 JSON 文件、手动粘贴配置或纯代码推导作为替代。配置数值(碎片档位、奖池类型、task_id、rank_type 等)是文档的核心内容,缺失配置会导致文档严重不完整,误导前端开发。


步骤 1:收集三方数据源

1.1 解析活动配置(步骤 0 已拉取)

从步骤 0 拉取的 JSON 中提取:

  1. doc_url → 飞书需求文档链接(用于步骤 1.3 自动获取需求文档)
  2. 组件配置:
    • collect_chip → 碎片定义
    • lottery → 抽奖配置
    • tasks → 任务配置
    • rank → 排行榜配置
    • exchange_store → 兑换商店配置
    • direct_purchase_store → 直购商店配置
    • collect_gifts → 收送礼配置
    • extra_gifts → 爆礼物配置
    • charge_coupon → 充值优惠配置
    • gift_pkg → 礼包配置
    • special_info → 活动特殊配置

1.2 定位并读取活动代码

1.2.1 定位活动代码目录

活动代码仓库路径通过以下优先级确定:

  1. 用户直接提供(最优先):用户说"代码在 app/activity/2026/jk/jk_cruise_holiday"时,直接使用该目录,跳过自动搜索
  2. 环境变量$ACTIVITY_DEV_CODE_DIR(指向 wespy-http-go 项目本地仓库,将自动切换到 dev 分支)
  3. 固定路径/usr/local/wejoy/src/wespy-http-go-dev/
  4. 相对路径兜底:当前工作目录下的 ../wespy-http-go

如果都找不到,提示用户设置环境变量或提供路径:

export ACTIVITY_DEV_CODE_DIR=/path/to/wespy-http-go

拉取最新 dev 代码(定位到仓库后、搜索活动子目录前执行)

定位到代码仓库根目录后,自动切换到 dev 分支并拉取最新代码:

cd $ACTIVITY_DEV_CODE_DIR && git checkout dev && git pull
  • git checkout dev 失败(本地有未提交修改等),报告警告并询问用户是否继续使用当前分支代码
  • git pull 失败(网络问题等),报告警告并询问用户是否继续使用本地已有代码
  • 两步均成功后,继续搜索活动子目录

当用户未提供代码目录时,自动搜索

  1. 根据活动 ID 搜索(推荐,活动 ID 通常定义在 *_model.goconf/ 下):
grep -r "<活动ID>" $ACTIVITY_DEV_CODE_DIR/app/activity/ --include="*.go" -l
  1. 根据活动名称搜索目录
find $ACTIVITY_DEV_CODE_DIR/app/activity -type d -maxdepth 3 | grep -i "<活动关键词>"

定位确认步骤(必须执行):找到代码目录后,向用户展示定位结果并确认:

已定位到活动代码目录:
- 活动目录:app/activity/2026/jk/jk_cruise_holiday
- ActId 定义:internal/conf/lottery.go → ActIdLottery = 6601

请确认这是正确的目录,还是需要指定其他路径?

用户确认后才继续后续步骤。

1.2.2 项目代码结构

wespy-http-go/                     # 项目根目录
├── app/activity/                  # 活动服务
│   ├── {year}/                    #   按年份分目录
│   │   └── {region_or_name}/      #   按区域或活动名称分目录
│   │       ├── *_model.go         #     路由和 Hook 注册 ★
│   │       ├── register/          #     活动注册入口
│   │       ├── internal/conf/     #     活动配置常量
│   │       ├── internal/route/    #     路由处理器
│   │       ├── internal/service/  #     业务逻辑
│   │       ├── internal/store/    #     数据结构(req/rsp/redis)
│   │       ├── internal/hook/     #     Hook 实现
│   │       └── internal/event/    #     事件处理
│   ├── widget/                    #   公共 Widget 组件(80+)
│   ├── common/                    #   通用框架(actmodel、actutil 等)
│   ├── hook/                      #   全局 Hook
│   ├── event/                     #   全局事件处理
│   └── router/                    #   路由层
├── pkg/weconfig/                  # 配置中心 SDK
├── service/                       # 全局 service 层
├── store/                         # 全局 store 层
└── def/                           # 全局常量定义

注意:活动代码目录结构有两种风格:

  • 新版(2026+)internal/conf/internal/route/internal/service/internal/store/internal/hook/
  • 旧版conf/route/service/store/hook/(直接在活动目录下,不带 internal/) 两种都需要兼容。

1.2.3 重点提取文件

确认目录后,读取以下关键文件:

  1. *_model.go(如 sale_model.golottery_model.go)→ GetRouter() 获取 Domain 和 SubRouters,GetHookers() 获取 Hook 注册。文件名不固定,搜索包含 GetRouter().go 文件
  2. route/*.goc.ShouldBind / c.ShouldBindJSON 获取请求参数结构体
  3. store/req.go → 请求参数的 form/json tag 和 binding 约束
  4. store/rsp.go 或 route 中的 SuccessResponse → 响应结构
  5. conf/conf.go → ActId、SpecialInfo 解析结构体、常量定义(如 GiftId、ChipId)
  6. hook/*.go → Hooker 接口实现
  7. register/register.go → 活动注册入口

1.3 获取需求文档(飞书凭证为强配置)

从步骤 0 拉取的活动配置中读取 doc_url 字段,该字段即飞书需求文档链接。

情况一:doc_url 存在

调用 feishu2stdout skill 读取文档内容。FEISHU_APP_IDFEISHU_APP_SECRET 是此时的必须条件

  • 若飞书凭证未设置,立即停止执行,向用户输出以下引导后不再继续任何步骤:
    活动配置中存在飞书需求文档(doc_url 已配置),必须读取后才能生成联调文档。
    
    请设置飞书凭证后重试:
      export FEISHU_APP_ID=your_app_id
      export FEISHU_APP_SECRET=your_app_secret
    
  • 若凭证已设置但读取失败(权限不足、网络等),同样停止执行,报告具体错误并引导用户解决后重试。
  • 读取成功后,从文档中提取:活动玩法说明、数值设计(概率、保底、阈值等)、各玩法的业务流程和触发条件。

情况二:doc_url 为空或不存在

向用户输出以下提示并等待确认,不得自行跳过:

活动配置中未配置 doc_url(飞书需求文档链接)。

需求文档包含玩法说明和数值设计,对联调文档质量有重要影响。

建议在配置中心补充 doc_url 字段,否则联调文档中的活动概览和业务流程将无法填写。

请选择:
  A) 我已在配置中心补充了 doc_url,重新执行
  B) 确认该活动没有飞书需求文档,跳过此步骤,继续生成
  • 用户选 A → 重新执行步骤 0 拉取配置,再进入本步骤。
  • 用户选 B(或明确表示无文档)→ 跳过需求文档,继续执行后续步骤,文档中活动概览标注"无需求文档,请手动补充"。

feishu2stdout 输出说明:

  • stdout:Markdown 内容(直接使用)
  • stderr:调试信息(文档标题、块数量、转换进度)
  • 支持 docx 和 wiki 类型链接

步骤 2:分析与整合

2.1 碎片分析

从活动配置的 collect_chip 数组中提取每个碎片的:

  • chip_id — 碎片 ID(前端调用接口时需传入)
  • name — 碎片名称
  • price — 单价(金币)
  • sell_conf — 售卖档位(number、price、extra_number)
  • chip_discount — 折扣配置
  • record_total — 是否记录累计值

分析碎片的用途(结合代码和配置):

  • 被哪个 lottery 消耗(cost_chip_meta_list 中引用了哪个 chip_id)
  • 被哪个 exchange_store 消耗(cost_mapcost_chip_meta 中引用了哪个 chip_id)
  • 被哪个 task 监听(Trigger 类型为 collect_chip 且 rules 匹配 chip_id)
  • 是否作为标志位/中转使用(price=0 或无 sell_conf)

2.2 组件接口分析

核心规则

  1. Widget 组件接口注册在各自独立的路由 Group 下,不在活动的 Domain 路径下
  2. 活动的 Domain 路径(如 /activity_v2/jk_cruise_holiday/)下仅有 GetRouter().SubRouters 中定义的自定义接口
  3. 前端通过 act_id 参数区分调用的是哪个活动
  4. 目录名 ≠ 路由组名,必须以代码中 Group("xxx") 的实际值为准

动态发现步骤(禁止硬编码路径):

  1. 判断活动用了哪些组件:检查活动配置 JSON 中哪些组件字段非空
  2. 读取 Widget 路由代码:对每个用到的组件,读取 app/activity/widget/{组件}/route/route.go(或 router.go),从 Group("xxx").POST("yyy"...) 提取真实的路由组名和接口 Action
  3. 输出真实路径/activity_v2/{路由组名}/{action}

配置字段与 Widget 目录映射表(参考 act-config-review,完整 70+ 项):

配置字段 Widget 目录 说明
collect_chip widget/collect_chip/ 碎片收集
lottery widget/lottery/ 抽奖
tasks widget/task/ 任务
task_groups widget/task_group/ 任务分组
rank widget/rank/ 榜单
reward_pool widget/reward_pool/ 奖池
exchange_store widget/exchange_store/ 兑换商店
direct_purchase_store widget/direct_purchase_store/ 直购商店
gift_pkg widget/gift_pkg/ 礼包
coin_pkg widget/coin_pkg/ 金币礼包
charge_coupon widget/charge_coupon/ 充值优惠
collect_gifts widget/collect_gift/ 礼物收集
extra_gifts widget/extra_gift/ 爆礼物
sign widget/sign/ 签到
sign_discontinue widget/sign_discontinue/ 不连续签到
mining widget/mining/ 储值返利
level_task widget/level_task/ 多层级任务
daily_collect_chip widget/daily_collect_chip/ 每日碎片
daily_task_repeat widget/daily_task_repeat/ 每日重复任务
team widget/team/ 组队
family widget/family/ 家族
invite_user widget/invite_user/ 邀请用户
blind_box widget/blind_box_lucky_value/ 盲盒幸运值
flip_card widget/flip_card/ 翻牌
claw_machine widget/claw_machine/ 抓娃娃机
chess widget/chess/ 棋盘
farm widget/farm/ 农场
sea_bingo widget/sea_bingo/ Bingo
bingo widget/bingo/ Bingo(旧版)
red_packet widget/redpacket/ 红包雨
gift_charge widget/gift_charge/ 礼物充值
gift_unlock widget/gift_unlock/ 礼物解锁
gift_slot_machines widget/gift_slot_machines/ 礼物老虎机
gift_box_star widget/gift_box_star/ 礼盒星级
gift_love_cube widget/gift_love_cube/ 爱心魔方
vip_discount widget/vip_discount/ VIP 折扣
renew_vip widget/renew_vip/ VIP 续费
coin_pool widget/coin_pool/ 金币池
prop_rain widget/prop_rain/ 道具雨
workshop widget/workshop/ 工坊
knock_glass widget/knock_glass/ 敲杯子
treasure_pavilion widget/treasure_pavilion/ 藏宝阁
simple_vote widget/simple_vote/ 简单投票
act_vote widget/act_vote/ 活动投票
act_discover widget/act_discover/ 活动发现页
act_cdk widget/act_cdk/ CDK 兑换码
act_ai_chat widget/act_ai_chat/ AI 聊天
npc widget/npc/ NPC 互动
question widget/question/ 答题
room_gift_top widget/room_gift_top/ 房间礼物排行
voice_statistics widget/voice_statistics/ 语音统计
use_ring widget/use_ring/ 使用戒指

注意:配置字段名和 Widget 目录名大部分一致,少数有差异(如 taskswidget/task/red_packetwidget/redpacket/)。 遇到未在表中的字段,直接在 app/activity/widget/ 下搜索匹配的目录。

获取 Widget 接口路径的步骤(禁止硬编码,必须动态读取):

  1. 根据上表定位 Widget 目录
  2. 读取该目录下的 route 文件(route/route.gorouter.goroute/router.gorouter/router.go
  3. Group("xxx") 提取路由组名,从 .POST("yyy"...) 提取接口 Action
  4. 拼接真实路径:/activity_v2/{路由组名}/{action}

特别注意目录名与路由组名不同的组件

  • direct_purchase_store → 组名 direct_store
  • family → 组名 family_widget
  • act_vote → 组名 vote
  • act_ai_chat → 组名 ai_chat
  • gift_pkg 额外注册了 chain_gift_pkg 子组

对于每个组件接口,说明在当前活动中应该填什么参数值:

  • act_id — 填 conf.ActId(从 conf.go 读取)
  • type(lottery)— 填 lottery 配置中的 type
  • chip_id — 填具体的碎片 ID
  • type(rank)— 填 rank 配置中的 type
  • task_id — 填 task 配置中的 id
  • store_name — 填 exchange_store 配置中的 store_name
  • 其他参数根据具体 Widget 的请求结构体确定

2.3 活动特定接口分析

*_model.goGetRouter().SubRouters 提取活动自定义接口,结合 route 和 store 代码分析每个接口的:

  • 完整路径:/activity_v2/{domain}/{action}
  • HTTP 方法
  • 请求参数(从 ShouldBind 绑定的结构体提取)
  • 响应结构(从 SuccessResponse 返回的数据提取)
  • 业务说明(从 service 层逻辑推导)

2.4 Hook 影响分析

GetHookers() 中识别注册的 Hook,说明它们对标准组件接口的影响:

  • LotteryHookerBeforeSendRewards → 可能替换/拦截抽奖奖励
  • TaskHookerCanReceiveReward → 可能限制任务领取条件
  • SendGiftHooker → 送特定礼物时的额外处理逻辑
  • 其他 Hook 对前端展示或流程的影响

步骤 3:生成联调文档

输出一份结构化的 Markdown 文档,保存到活动代码目录下:

<code_dir>/INTEGRATION.md

文档结构如下:

# {活动名称} 前后端联调文档

> 自动生成时间:{datetime}
> 活动 ID:{act_id}
> Domain:{domain}
> 区服:{region}
> 活动时间:{start_time} ~ {end_time}

---

## 一、活动概览

{从需求文档或配置中提取的活动简要说明}

---

## 二、碎片(Collect Chip)定义

{从活动配置 `collect_chip` 数组提取,如果无法获取配置则从代码常量推导}

| chip_id | 名称 | 单价(金币) | 用途 | 备注 |
|---------|------|-----------|------|------|
| {从配置/代码提取} | {name} | {price} | {分析用途} | {sell_conf 为空则标注"不可购买"} |

### 碎片购买档位(从 sell_conf.sell_infos 提取,无 sell_conf 则省略本节)

| 数量 | 价格(金币) | 额外赠送 |
|------|-----------|---------|
| {number} | {price} | {extra_number} |

### 碎片折扣(从 chip_discount 提取,无折扣则省略本节)

| discount_id | 折扣率 | 有效期(秒) | 首次折扣 | 抽奖可用 |
|------------|--------|-----------|---------|---------|
| {discount_id} | {discount_rate} | {expire_time} | {is_first_rate} | {lottery_can_enjoy} |

---

## 三、组件接口(Widget API)参数速查

> **Widget 接口不在活动 Domain 路径下**,它们注册在各自独立的路由 Group 下。
> 前端通过 `act_id` 参数区分调用的是哪个活动。
> 以下仅列出**本活动实际用到的组件**及其接口路径和参数。
> 接口路径通过读取各 Widget 的 route 文件中 `Group("xxx")``.POST("yyy")` 的真实注册值获得。
>
> **根据活动配置动态判断用了哪些组件**(配置字段非空即表示使用了该组件),
> 然后对每个用到的组件,读取 `app/activity/widget/{组件目录}/route/route.go` 获取真实接口列表。
> **禁止猜测接口路径**,目录名 ≠ 路由组名的情况很常见。

{根据活动实际使用的组件,从「步骤 2.2 组件接口分析」的完整对照表中选取对应组件,
 读取 route 文件后填入真实路径和参数。
 如果活动自定义路由覆盖了组件同名 action,需标注说明。}

---

## 四、活动特定接口

> 以下为本活动自定义的接口(非通用组件接口)。

### 4.1 {接口名称}

- **路径**`POST /activity_v2/{domain}/{action}`
- **说明**:{业务说明}

**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| act_id | int || 活动 ID |
| uid | int || 用户 ID |
| {字段} | {类型} | {是/否} | {说明} |

**响应结构**
```json
{
  "code": 200,
  "msg": "ok",
  "result": {
    // 响应字段说明
  }
}
```

{... 其他自定义接口 ...}

---

## 五、Hook 对接口的影响

{列出注册的 Hook 及其对标准接口行为的影响}

| Hook 类型 | 影响的接口 | 影响说明 |
|----------|----------|---------|
| LotteryHooker.BeforeSendRewards | do_lottery | 每日限额控制,超出后替换为空奖励 |
| TaskHooker.CanReceiveReward | recv_task_reward | 同设备限制3个用户领取 |

---

## 六、关键业务流程

{从需求文档和代码中梳理的核心用户操作流程}

```
用户充值 → 购买碎片(buy_chip) → 执行抽奖(do_lottery) → 获得奖励
                                              获得兑换币(chip_id=2)
                                              兑换商店兑换(do_exchange)
```

---

## 七、注意事项

{从配置和代码中发现的需要前端注意的事项}

- {时间窗口限制}
- {特殊状态处理}
- {错误码说明}
- {并发/频率限制}

---

## 八、配置速查

### SpecialInfo 关键字段

| 字段 || 说明 |
|------|---|------|
| {key} | {value} | {说明} |

### 关键常量

| 常量 || 说明 |
|------|---|------|
| ActId | {act_id} | 活动 ID |
| GiftId | {gift_id} | 活动礼物 ID |
| {其他常量} | {值} | {说明} |

步骤 3.5:生成 OpenAPI 文档

基于步骤 2.2 中已分析出的 Widget 组件接口 和步骤 2.3 中已分析出的活动特定接口GetRouter().SubRouters 中的自定义接口),按照 activity-openapi-from-code skill 的规范生成 OpenAPI 3.0 文档。

范围说明:此 OpenAPI 文档覆盖两类接口

  1. Widget 组件接口:本活动实际使用的所有 Widget 组件的接口(路径为 /activity_v2/{路由组名}/{action}
  2. 活动自定义接口GetRouter().SubRouters 中定义的活动专属接口(路径为 /activity_v2/{Domain}/{action}

Part A:活动自定义接口 — 代码溯源顺序(与 activity-openapi-from-code 一致)

步骤 文件/位置 提取内容
1 <活动>/*_model.go GetActId()GetRouter()DomainSubRouters[].Action
2 SubRouters[].Handler 指向的 internal/route/*.go HTTP 方法、绑定类型 c.ShouldBind、成功/失败响应封装
3 internal/store/req.go(及同包其它 req) form/json/uri 标签 → OpenAPI 属性名;binding 与嵌入的 actutil.CommonReq
4 internal/store/rsp.go 或 route 中 SuccessResponse 的变量类型 响应 result 的结构与 json 标签
5 internal/service/*.go 中对应函数 summary/description 的补充语义(状态机、时间窗、错误分支、枚举含义)
6 internal/conf/conf.go ActID、域名等客观配置
7 公共层 app/activity/common/actutil/http_req.goCommonReq)、http_rsp.go 统一请求/响应外层

Part B:Widget 组件接口 — 代码溯源顺序

对步骤 2.2 中已识别的每个 Widget 组件,按以下顺序读取代码:

步骤 文件/位置 提取内容
1 app/activity/widget/{组件}/route/route.go(或 router.go Group("xxx") → 路由组名;.POST("yyy", handler) → action 列表;handler 函数名
2 handler 函数体中 c.ShouldBind / c.ShouldBindJSON 绑定的结构体类型 绑定方式(form/json)及请求参数结构体名
3 app/activity/widget/{组件}/store/req.go form/json 标签 → 属性名;binding:"required" → required 字段
4 app/activity/widget/{组件}/store/rsp.go 或 handler 中 SuccessResponse 的变量类型 响应 result 结构
5 app/activity/widget/{组件}/service/*.go 对应函数(可选) summary/description 语义补充

在 OpenAPI 路径中,Widget 接口格式为 /activity_v2/{路由组名}/{action},并在 description 中注明:

  • 该组件的用途(如"抽奖组件")
  • 本活动对应的 act_id 参数值(从 conf.ActId 读取)
  • 活动特有的参数枚举值(如 typechip_idtask_id 等,来自活动配置)

核心原则

  1. 一切有据:每个 path、每个字段都能在代码中找到定义或推导来源
  2. 不 Mock:不写虚构的 example 值;未覆盖的逻辑写「以代码为准」并指向文件
  3. 路径与网关一致:Widget 接口使用 /activity_v2/{路由组名}/{action};自定义接口使用 /activity_v2/{Domain}/{action}
  4. 分组标注:OpenAPI tags 中使用 widget 标记组件接口,使用 custom 标记活动自定义接口,便于前端区分

请求体 Content-Type

  • ShouldBind + form 标签 → application/x-www-form-urlencoded
  • ShouldBindJSONapplication/json
  • 同一活动混用时按接口分别声明

输出文件

保存到 <code_dir>/openapi.yaml,格式参考 activity-openapi-from-code skill 中定义的 OpenAPI 文件结构。


步骤 4:输出与确认

  1. 将生成的联调文档保存到 <code_dir>/INTEGRATION.md
  2. 将生成的 OpenAPI 文档保存到 <code_dir>/openapi.yaml
  3. 向用户展示文档摘要(碎片数量、Widget 组件数、Widget 接口总数、自定义接口数、OpenAPI 接口总数)
  4. 询问是否需要调整或补充

步骤 5:飞书文档分享

文档生成完成后,无需询问用户,直接执行以下全部操作。任何步骤失败则报告错误并继续尝试后续步骤,不阻塞整体流程。

5.1 导入飞书在线文档

  1. 读取生成的 INTEGRATION.md 文件内容
  2. 调用 lark-cli 创建飞书文档:
    lark-cli docs +create --title "{活动名称} 联调文档" --markdown "<INTEGRATION.md 内容>"
    
  3. 记录返回的 doc_url(飞书在线文档链接)

5.2 获取当前应用下的 open_id

重要:飞书 open_id 是应用级别隔离的,同一用户在不同应用下 open_id 不同。 消息中的 sender_idou_xxx)属于消息来源应用,不可直接用于 lark-cli 配置的应用发送消息(会报 open_id cross app)。 必须通过 user_id(全局唯一,非 ou_ 开头,如 cf122be1)转换为当前应用下的 open_id。

从对话消息中提取用户的 user_id(非 ou_ 开头的全局用户 ID),然后查询当前应用下的 open_id:

lark-cli contact +get-user --user-id <user_id> --user-id-type user_id

从返回 JSON 的 data.open_id 字段提取当前应用可用的 open_id。若查询失败则跳过私聊发送步骤。

5.3 私聊发送文件和链接

  1. 若已创建飞书在线文档,发送链接和摘要:
    lark-cli im +messages-send --user-id <open_id> --markdown "**{活动名称} 联调文档**\n\n在线文档:{doc_url}\n\n碎片数:{N},Widget 组件数:{N}(接口 {N} 条),自定义接口数:{N}"
    
  2. 发送本地文件(注意须使用相对路径,先 cd 到文件所在目录):
    cd <code_dir> && lark-cli im +messages-send --user-id <open_id> --file ./INTEGRATION.md
    cd <code_dir> && lark-cli im +messages-send --user-id <open_id> --file ./openapi.yaml
    

每个 lark-cli 命令独立执行,某一步失败不影响后续步骤。全部失败时报告错误,不影响已生成的文档。


输出质量要求

必须满足的条件

  • 每个碎片的 chip_id 和用途必须列出
  • 组件接口的参数值必须来自实际配置(不可编造)
  • 自定义接口的请求/响应必须来自代码中的结构体定义(不可编造)
  • 所有 {domain} 必须替换为实际的 Domain 值
  • 所有 {act_id} 必须替换为实际的活动 ID
  • Hook 影响说明必须基于代码实际注册的 Hook
  • OpenAPI 的每个 paths 条目均能在 GetRouter + route 中找到 Handler 与 Action
  • OpenAPI Request schema 字段与 form/json 标签名一致(含下划线命名)
  • OpenAPI Response resultSuccessResponse(c, result) 的类型一致
  • OpenAPI 中的 Widget 组件接口:每个配置中启用的组件必须对应至少一条 paths 条目
  • OpenAPI 中的 Widget 接口路径由 Widget route.goGroup("xxx") + .POST("yyy") 动态读取,禁止猜测
  • OpenAPI 中的 Widget 接口请求 schema 来自该 Widget 的 store/req.go,禁止凭印象填写
  • OpenAPI Widget 接口用 tags: [widget] 标注,自定义接口用 tags: [custom] 标注

禁止行为

  • 禁止编造不存在的接口或参数
  • 禁止编造 example 值(可以标注"需确认")
  • 禁止遗漏 sale_model.go 中定义的自定义接口
  • 禁止遗漏配置中启用的组件
  • 禁止在 OpenAPI 中添加未在代码中出现的枚举值或字段
  • 禁止在不读取 Widget route 文件的情况下填写 Widget 接口路径(目录名 ≠ 路由组名)

依赖说明

Skill 依赖

依赖 Skill 路径 用途 必须
fetch-game-config-clean skills/activity/backend/fetch-game-config-clean 拉取活动配置 JSON
feishu2stdout skills/activity/backend/feishu2stdout 读取飞书需求文档(输出到 stdout) 否(提供需求文档时需要)
activity-components skills/activity/backend/activity-components 组件知识库(辅助理解组件用法) 否(内置知识)
activity-openapi-from-code skills/activity/backend/activity-openapi-from-code OpenAPI 文档生成规范 是(生成 openapi.yaml 时需要)
lark-cli 全局安装(npm install -g @larksuite/cli 飞书文档分享(导入在线文档 + 私聊发送) 否(未安装则跳过分享步骤)

环境变量

变量 用途 必须
ConfigSecret 配置中心鉴权密钥(fetch-game-config-clean 需要)
FEISHU_APP_ID 飞书应用 ID 条件必须(活动配置有 doc_url 时必须设置)
FEISHU_APP_SECRET 飞书应用密钥 条件必须(活动配置有 doc_url 时必须设置)
ACTIVITY_DEV_CODE_DIR 业务代码仓库路径(wespy-http-go),自动切换 dev 分支并拉取最新代码 否(未提供时使用 ../wespy-http-go 兜底)

Python 依赖

fetch-game-config-clean 和 feishu2stdout 各自有 requirements.txt,安装方式见 README.md。


典型使用场景

场景 1:最简用法(只需活动 ID + 区服)

用户:帮我生成联调文档,活动 ID 6601,Jk 区服

AI 执行:

  1. 拉取 act_id=6601, region=O 的活动配置
  2. 从配置中读取 doc_url,自动获取飞书需求文档
  3. 根据活动 ID 自动搜索代码目录,定位后确认
  4. 整合三方数据,生成 INTEGRATION.md

场景 2:指定代码目录

用户:生成联调文档,活动 6601,O 服,代码在 app/activity/2026/jk/jk_cruise_holiday

AI 执行:

  1. 拉取配置 + 从 doc_url 获取需求文档
  2. 直接使用用户指定的代码目录
  3. 生成文档

场景 3:配置中有 doc_url 但飞书凭证未设置

用户:生成联调文档,活动 6601,O 服

AI 执行:

  1. 拉取配置,发现 doc_url 非空
  2. 尝试调用 feishu2stdout,发现 FEISHU_APP_ID / FEISHU_APP_SECRET 未设置
  3. 立即停止,提示用户设置凭证后重试,不继续生成文档

场景 4:配置中无 doc_url,用户确认没有文档

用户:生成联调文档,活动 6601,O 服

AI 执行:

  1. 拉取配置,发现 doc_url 为空
  2. 提示用户:"未配置 doc_url,请确认是否真的没有需求文档?"
  3. 用户回复"确认没有"→ 跳过需求文档,继续生成(活动概览标注"无需求文档,请手动补充")
  4. 用户回复"有,我去补充"→ 等待用户补充后重新执行

场景 6:更新已有文档

用户:更新一下 jk_cruise_holiday 的联调文档,新增了一个接口

AI 执行:

  1. 读取已有的 INTEGRATION.md
  2. 重新扫描代码,发现新增接口
  3. 增量更新文档

常见问题

Q: 配置拉取失败怎么办? A: ConfigSecret 是必须的前置条件。若提示未设置,执行 export ConfigSecret=<你的密钥> 后重试。网络问题则检查网络并重试。不接受以本地 JSON 文件或纯代码推导作为替代,配置数值必须从配置中心获取。

Q: 飞书凭证未设置怎么办? A: 若活动配置有 doc_url,飞书凭证是必须的。执行 export FEISHU_APP_ID=xxxexport FEISHU_APP_SECRET=xxx 后重试。若活动配置无 doc_url,skill 会主动询问用户是否真的没有需求文档,用户确认后可跳过。

Q: 代码目录找不到怎么办? A: 让用户提供准确的活动代码目录路径。通常在 app/activity/{year}/{region_group}/{activity_name} 下。

Q: 组件接口路径不确定? A: 禁止猜测路径。必须读取对应 Widget 目录下的 route 文件,从 Group("xxx") 获取路由组名,从 .POST("yyy") 获取接口 Action。组件接口路径是 /activity_v2/{路由组名}/{action},其中路由组名由 Widget 的 route 代码决定,不是活动的 Domain。特别注意以下目录名 ≠ 路由组名的情况:direct_purchase_store → direct_storefamily → family_widgetact_vote → voteact_ai_chat → ai_chat

Q: 如何判断活动用了哪些组件? A: 检查活动配置中哪些组件字段非空:

  • collect_chip 非空 → 碎片组件(路由组:collect_chip
  • lottery 非空 → 抽奖组件(路由组:lottery
  • tasks 非空 → 任务组件(路由组:task_group
  • rank 非空 → 排行榜组件(路由组:rank
  • exchange_store 非空 → 兑换商店(路由组:exchange_store
  • direct_purchase_store 非空 → 直购商店(路由组:direct_store
  • gift_pkg 非空 → 礼包(路由组:gift_pkg,另有 chain_gift_pkg
  • coin_pkg 非空 → 金币礼包(路由组:coin_pkg
  • charge_coupon 非空 → 充值优惠(路由组:charge_coupon
  • collect_gifts 非空 → 收送礼(路由组:collect_gift
  • extra_gifts 非空 → 爆礼物(路由组:extra_gift
  • sign 相关 → 签到(路由组:sign)/ 不连续签到(路由组:sign_discontinue
  • level_tasks 非空 → 多层级任务(路由组:level_task
  • 此外还需检查代码中是否引用了 bingo、farm、mining、team、family、invite_user 等游戏/社交类组件
Installs
6
First Seen
Apr 13, 2026