growork 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ # 【V1.7】五牌阵(外化)
2
+
3
+ | 日期 | 版本 | 修改人 |
4
+ | --- | --- | --- |
5
+ | 2026/1/7 | V1.0: 创建文档 | @ou_b7af96ce7aa4c1c66fc6dfe05920f483 |
6
+ | 2026/1/16 | V1.1:修改首页牌阵场景逻辑 | @ou_b7af96ce7aa4c1c66fc6dfe05920f483 |
7
+ | 2026/1/21 | V1.2:牌阵外化卡片新增字段:使用次数&点赞次数 | @ou_aee8dadd00d620825dc7d479734dc300 |
8
+
9
+ # 一、需求背景
10
+
11
+ 目前产品内容侧已有 5 个牌阵,但在用户进入解读之前的关键链路上体验偏薄:用户从输入问题到开始抽牌/解读之间,系统仅提供一段简短的问题确认文字,缺少对牌阵选择逻辑的呈现与引导,导致用户对「为什么用这个牌阵、这个牌阵能解决什么」的理解不足。因此,本期需求将现有的5牌阵外化出来,增强用户对牌阵的参与感,同时外化丰富产品内容;
12
+
13
+ # 二、需求详情
14
+
15
+ **问题确认流程**
16
+
17
+ **需求详情**
18
+
19
+ | 原型 | 说明 |
20
+ | --- | --- |
21
+ | | **解读页牌阵推荐****卡** **展示** **交互** |
22
+ | | **首页牌阵场景** **展示** **交互** |
23
+
24
+ **牌阵文字库(已找教研确认)**
25
+
26
+ | Spread Name | Spread Description |
27
+ | --- | --- |
28
+ | 时间流牌阵 (Past, Present, Future Spread) | Unfold your complete timeline to see how past influences are shaping your future outcomes. |
29
+ | 追本溯源牌阵 (Situation Analysis Spread) | Get to the bottom of 'why' by uncovering the hidden internal and external factors driving this situation. |
30
+ | 关系大十字 (Five-Card Cross Spread) | Analyze the causes and core issues, and provide analysis and action suggestions for the interaction of emotional relationships. |
31
+ | 二择一牌阵 (Two-Choose-One Spread) | Weigh two potential futures side-by-side to clarify which path leads to your desired goal. |
32
+ | 综合运势 (General Reading Spread) | Analyze past, present, and future trends to provide a comprehensive forecast for wealth, love, or career. |
33
+
34
+
35
+ [嵌入表格: JaWGsQ9zghCsfltzHB1liQTfgPb_LijZ9A]
36
+
37
+ # 调研附录
38
+
39
+ 字段补充
40
+
41
+ | Esty | Walmart | Amazon? | |
42
+ | --- | --- | --- | --- |
43
+ | | | | |
44
+
45
+ **后期修改prompt后(多场景/更具体)可参考增加牌阵介绍页**
46
+
47
+ **方案一:问题场景与牌阵一一对应**
48
+
49
+ | 原型 | 说明 |
50
+ | --- | --- |
51
+ | | **首页推荐** **展示** |
52
+ | | **牌阵介绍** **展示(以时间流示例)** 是不是正压着一块石头,不知道等待你的会是惊喜还是惊吓? 无论是那场至关重要的考试,那个正在推进的项目,还是那段刚刚萌芽的关系,未来的不确定性总是让人焦虑。如果你正时间站在岔路口,想知道当前的努力是否白费,那就抽牌张看看吧! 此牌阵会为你展开时间的卷轴,帮你理清过去的影响,透视现在的关键阻碍,更重要的是——我为你“剧透”这件事最终的大结局! 英文版 ✅ **Those anxiously waiting for results:** Whether it's a job interview, a final exam, or a visa application, and you just can't sit still. ✅ **Explorers of new beginnings:** You've just started a new project, business, or relationship and want to know if it will sail smoothly. ✅ **Those seeking reassurance:** You are facing uncertainty and desperately need to know, "Will this actually work out?" **Is the fear of the unknown weighing you down?** Are you wondering if a big surprise or a sudden setback is waiting for you around the corner? Whether it's that crucial test, a high-stakes project, or a fresh romance, the uncertainty can be overwhelming. If you are standing at a crossroads in time, draw a card to clear the fog. This spread unfolds your personal timeline to reveal how the past shapes your present, what obstacles are currently in your way, and most importantly—**I will give you a "spoiler" on the final ending!** **交互** 从牌阵介绍页进入解读页 推荐问题?不推荐 牌阵确认?不确认 使用原流程 |
53
+
54
+ **方案二:一个牌阵对应多个问题场景(增加爱情场景高频问题/问题更具体)**
55
+
56
+ 高频问题定期根据用户数据调整?
57
+
58
+ | 原型 | 说明 |
59
+ | --- | --- |
60
+ | | **首页推荐** **展示** |
61
+ | | **问题介绍** **展示(以Does he/she actually like me?为例)** 英文版 |
62
+
63
+ 调研简述:
64
+
65
+ - 可以仿照Quin,在问题确认下方增加问题解析+牌阵推荐(牌阵可选),牌阵的排布按照具体抽牌时的排布设计;在牌阵选择页给出牌阵简介
66
+ | Hellobot | | Lumi | laby | Quin |
67
+ | --- | --- | --- | --- | --- |
68
+ | | | | | 共16个牌阵,4个和感情相关 |
69
+
70
+ | Quin | 小雀幸 | 测测 | AI Tarot | Sidera | Tarot card reading and meaning | Moonly | Lumi | Tarotoo | Tarot | Trusted Tarot | Labyrinthos Tarot Cards | Tarot reading |
71
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
72
+ | | | | | | | | | | | | | |
73
+
74
+ [五牌阵prompt](https://romangic.sg.larksuite.com/wiki/RqR7w1dEgiwtFBk3vUnltS6lgWe?from=from_copylink&sheet=55f1a3)
@@ -0,0 +1,126 @@
1
+ # 测试
2
+
3
+
4
+ 需求详情
5
+
6
+
7
+ | 日期 | 版本 | 修改人 |
8
+ | ---------- | ---------- | ---- |
9
+ | 2025/11/28 | V1.0: 创建文档 | @徐子彤 |
10
+
11
+
12
+ # 一、需求背景
13
+
14
+
15
+ Push 在用户离开 App 后,产品与用户保持持续连接的最重要触点。通过系统化的 Push 能力,在合适的时间,可把有价值的内容主动送达到用户手中,从而:
16
+
17
+ - 提升留存:通过每日塔罗牌 / 箴言提醒、回流提醒等,帮助用户养成使用习惯;
18
+ - 建立情感连接:在节日、生日、特殊节点推送祝福或关怀内容,让产品不仅是工具,而是放大产品情感化;
19
+ - 支持运营活动:在新功能上线、限时活动、专属优惠权益等场景,通过 Push 进行触达和转化,放大运营效果;
20
+ - 获取用户声音:通过调研问卷、满意度回访等 Push,引导用户反馈体验,为后续产品优化提供数据支撑。
21
+
22
+ 因此,本版本围绕 Push 能力进行初版建设,为后续的精细化触达、运营玩法和留存提升打下基础。
23
+
24
+
25
+ ### **需求触发:**
26
+
27
+ 1. **信息缺口(站外)**:新功能上线、版本更新
28
+ 2. **用户潜在需求唤醒(留存):活跃用户**:限时折扣(站内外)、专属权益(站内外);~~**沉睡用户**~~~~:召回优惠券、回归礼包/折扣(站外)~~
29
+ 3. *特殊节点推送(站内外):**节日/生日祝福(适当配合折扣福利)
30
+ 4. *社区活动推送(站内):**站内push引流到站外社区(话题讨论、用户反馈等)(配合社区运营)
31
+
32
+ ### **展现形式:**
33
+
34
+
35
+ **站外:****ios**系统Push、邮箱、社区消息
36
+
37
+ - *站内:**站内信(banner)、弹窗
38
+
39
+ ### **推送时间:**
40
+
41
+ 1. *“Daily push”:****固定每天早上的时间(8h30-9h):**每日运势
42
+ 2. **生日/节日** **“Celebration”:****特殊日的早上(8h30-9h):**生日祝福、节日祝福结合每日运势文案,如:“xxx祝你生日快乐,今日宇宙与你的链接最强,来看看今天的牌有什么指引”
43
+ 3. **要apple store 好评、评论** **“Review”:****晚上20h(8:00pm)**
44
+ 4. **折扣/福利** **“Offer”:****根据用户习惯使用时间(初始默认中午12h30):**折扣福利、社区引流、专属权益
45
+ 5. **社区话题** **“Community”:****晚上20h(8:00pm):**加入社区、话题讨论
46
+ 6. 用户个性化设置推送时间(站内的推送)
47
+
48
+ 需要监测点:
49
+
50
+
51
+ push到达率、push打开率、push转化率、卸载率
52
+
53
+
54
+ # 二、需求详情
55
+
56
+
57
+ ### 2.1 权限获取
58
+
59
+
60
+ **新增字段:notification_permission_status**
61
+
62
+
63
+ ### 规则:
64
+
65
+ 1. **APNs 权限获取弹窗:**
66
+
67
+ **授权**
68
+
69
+
70
+ **拒绝:拒绝后官方默认不会再出现,用户使用一个月后站内弹窗**出现:提醒用户去iphone**后台打开权限**,再次拒绝后本**年度**不会再提醒
71
+
72
+ 1. **站内权限获取弹窗:**
73
+
74
+ 默认开着权限,
75
+
76
+
77
+ 在 **23:00 - 08:00** 之间禁止发送任何push
78
+
79
+
80
+ **取值**
81
+
82
+ - not_determined (从未请求过权限)
83
+ - denied(用户明确拒绝/关闭)
84
+ - authorized(已授权)
85
+
86
+ ### 弹窗
87
+
88
+ | 说明 | 文件 | 场景 |
89
+ | --- | --- | --- |
90
+ | 弹窗时机**—站外系统通知权限**<br><br>苹果官方的 APNs (Apple Push Notification service)<br><br>- 第一次使用App:第一次 quick card 的**“reading completed”**解读完成时<br> <br>   有了体验后可以降低拒绝概率<br> <br> - 弹权限获取弹窗<br> | https://romangic.sg.larksuite.com/space/api/box/stream/download/asynccode/?code=NTQwN2NhNzM1ZDJiMzcxNWU2OGJlZTJlNGVkMmExMmRfcTJ0TERvNFRHSnA2aE9UV3Rhb245YjRtMU04QmhSbFNfVG9rZW46Wng4SWJOZTA3b2swZWV4QW5X | |
91
+ | 弹窗时机—站内系统通知跳转ios系统权限<br><br>- 弹出节点:第二次使用app:Quick card 解读完成“reading completed”时(也还没有深度问问题解读)<br> <br>- 第一次弹出:<br> <br> - 判断notification_permission_status是否为authorized<br> <br> - 是,不 | https://romangic.sg.larksuite.com/space/api/box/stream/download/asynccode/?code=MWI5NjUxZmJlYTU3OTU1ODE2MjJkZmVjMzA0NzAwMzJfTGR5Q0NncGEwR2t1ZFNqV3JqV3VvRWtZWXo3ZWZjMmNfVG9rZW46REREVWJnbXh0b0wxelh4YVBp | |
92
+
93
+
94
+
95
+ ### 2.2 push内容
96
+
97
+
98
+ ### push内容
99
+
100
+ | 类型 | 文件 | 推送对象<br><br>Audience | 标题 / 内容 | 推送时间 | Trigger<br><br>(触发条件) | 点击跳转页面 | | 优先级 | UI原型 | 位置 |
101
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
102
+ | 生日/节日<br><br>Celebration | https://romangic.sg.larksuite.com/space/api/box/stream/download/asynccode/?code=NTljNzUwOGRlMTUzNGJiMjM3YmM0NjAxYTc4ZGY2NGFfOEJvWjZmWHpQT0hGVGpWa0IyNzg0Z0pGMWEwUVhzbWhfVG9rZW46QWllQmJka3B3b1NMYmt4anpx | 节日for全体用户 | 随机抽取 WooMoo 节日Push中对应的内容<br><br>即将到来的:3月 St. Patrick's Day | 用户时区每天8:30am | Auto<br><br>3月 St. Patrick's Day | 首页 | | 极高 | 情人节<br><br> | ios系统push |
103
+ | Daily push | https://romangic.sg.larksuite.com/space/api/box/stream/download/asynccode/?code=NGU3ZmE4NWUxMDZjNTVjYTBjMjY5Y2I3NmM4YWE0MWVfVFJ6NnVPbkdVSDZNQ0htbDh1Y3ZrRFAxcmthaHQxU3JfVG9rZW46UXlySGJ0ZGFRb2ZRQTl4T3FM | 全体用户 | 随机抽取WooMoo DailyPush中任意一条标题+内容 | 用户时区每天8:30am | Auto | 首页 | | 高 (核心留存) | | ios系统push |
104
+ | 折扣/福利/活动<br><br>“Offer” | https://romangic.sg.larksuite.com/space/api/box/stream/download/asynccode/?code=MWZmZGJhYjBiNDAyNDRiZjhhOWRmOGYzNGE0ZmM2YzNfRXMxUHo2S29aTE9OM25lb0ZDZ3ZHT2cwZUVhSHV1YmJfVG9rZW46UW45NmJQZlBPb3BlZlZ4a3hx | <br><br>全体用户 | 按照福利活动计划再编辑 | **站外push:**用户时区中午12:30pm<br><br>站内:当天打开App就显示<br><br>社区、邮箱:群发 | 有产品福利活动推送的时候 | 与用户身份相对应的订阅墙、<br><br>跳转Discord | | 中高 | | ios系统push、社区、邮箱、站内信 |
105
+ | 社区话题<br><br>“Community” | https://romangic.sg.larksuite.com/space/api/box/stream/download/asynccode/?code=MGEyOWY0ZTEyMjQ4ZjAwNGM0NWEzYjNkOWY2Njg5MzdfRjFLRTJYdTVCMmZsbkRyU0dhSm9iWkVKWlJObjliMWtfVG9rZW46U2hlamJ0MTd3b1NrMEp4YmRy | 全体用户 | 标题:(以情人节话题为例) Valentine’s is coming—got tea?<br><br>Body:Join our Discord to spill the tea & score exclusive freebie alerts. Don't miss the steal!<br><br> <br><br>WooMoo 社区引流 | 站外push:用户时区每周五晚上 8:00pm<br><br>**站内:**当天打开App就显示 时效:24h<br><br>社区:运营群发 | 循环触发: 每周五晚上 8:00pm<br><br>站内触发场景:用户打开App,到达首页 | 跳转到外部Diacord里WooMoo对话加入 | | 中 | | iOS系统push、站内push、Discord社区 |
106
+ | 要apple store 好评、评论<br><br>“Review” | https://romangic.sg.larksuite.com/space/api/box/stream/download/asynccode/?code=YWE4NTQyOWUzOTlkZjY2YTZkZjRkZWU5N2QwNTFmN2JfSXB4cnhrN05pcDNnQlZrZUR6eG1jbFhvZHFlRUJ1REtfVG9rZW46SlhsMGJ2bERZb2c1TGl4cmJy | 全体用户 | - 标题:<br> <br><br>Headline: Love our readings? 🔮<br><br>Body: Score more free readings! Just rate us and snap a screenshot of your review.<br><br>按钮:关闭icon / Go!<br><br> <br><br> <br><br>- 标题: H | | 用户解读完一次触发,不管是Quick card 还是Deep drive 都是完成一次“reading completed”就触发 第一次弹窗“banner push comment”<br><br>(现在设置一个评论就赠送2次免费解读的活动)<br><br>- 用户点击“banner push comment”直接跳转iOS评价页面<br> <br>- 用户回到WooMoo APP,识别判 | 跳转到外部iOS评价 | | 中高 | <br><br> <br><br> | 站内push、Discord社区 |
107
+ | | | | | | | 订阅墙 | | | | |
108
+
109
+
110
+
111
+ # 三、数据需求
112
+
113
+
114
+ **埋点需求**
115
+
116
+
117
+ | | | | | | | | | | | | | | |
118
+ | ------------------------------- | --------------------------- | -------- | ---- | ---- | --- | ---- | ---- | --- | --- | --- | --- | --- | ---- |
119
+ | 模块 | 事件名称(英文) | 事件说明(中文) | 事件类型 | 触发时机 | 参数名 | 参数说明 | 参数类型 | 枚举 | 埋点端 | 优先级 | 备注 | 状态 | 问题记录 |
120
+ | 系统通知权限弹窗 | notification_popup_show | 系统弹窗展示 | 弹窗曝光 | | | | | | 客户端 | P0 | | 待验收 | |
121
+ | notification_popup_allow_click | 系统弹窗点击allow | 点击事件 | | | | | | 客户端 | P0 | | 待验收 | | |
122
+ | notification_popup_refuse_click | 系统弹窗点击don't allow | 点击事件 | | | | | | 客户端 | P0 | | 待验收 | | |
123
+ | app通知权限弹窗 | app_notification_popup_show | app弹窗展示 | 弹窗曝光 | | | | | | 客户端 | P0 | | | |
124
+ | app_notification_allow_click | app弹窗点击allow | 点击事件 | | | | | | 客户端 | P0 | | | | |
125
+ | app_notification_refuse_click | app弹窗点击don't allow | 点击事件 | | | | | | 客户端 | P0 | | | | |
126
+
package/test/push.md ADDED
@@ -0,0 +1,119 @@
1
+ # 【V1.8】Push
2
+
3
+ | 日期 | 版本 | 修改人 |
4
+ | --- | --- | --- |
5
+ | 2025/11/28 | V1.0: 创建文档 | @ou_aee8dadd00d620825dc7d479734dc300 |
6
+
7
+ # 一、需求背景
8
+
9
+ Push 在用户离开 App 后,产品与用户保持持续连接的最重要触点。通过系统化的 Push 能力,在合适的时间,可把有价值的内容主动送达到用户手中,从而:
10
+
11
+ - 提升留存:通过每日塔罗牌 / 箴言提醒、回流提醒等,帮助用户养成使用习惯;
12
+ - 建立情感连接:在节日、生日、特殊节点推送祝福或关怀内容,让产品不仅是工具,而是放大产品情感化;
13
+ - 支持运营活动:在新功能上线、限时活动、专属优惠权益等场景,通过 Push 进行触达和转化,放大运营效果;
14
+ - 获取用户声音:通过调研问卷、满意度回访等 Push,引导用户反馈体验,为后续产品优化提供数据支撑。
15
+ 因此,本版本围绕 Push 能力进行初版建设,为后续的精细化触达、运营玩法和留存提升打下基础。
16
+
17
+
18
+ ### **需求触发:**
19
+
20
+ 1. **信息缺口(站外)**:新功能上线、版本更新
21
+ 1. **用户潜在需求唤醒(留存):活跃用户**:限时折扣(站内外)、专属权益(站内外);~~**沉睡用户**~~~~:召回优惠券、回归礼包/折扣(站外)~~
22
+ 1. **特殊节点推送(站内外):**节日/生日祝福(适当配合折扣福利)
23
+ 1. **社区活动推送(站内):**站内push引流到站外社区(话题讨论、用户反馈等)(配合社区运营)
24
+
25
+ ### **展现形式:**
26
+
27
+ **站外:****ios**系统Push、邮箱、社区消息
28
+
29
+ **站内:**站内信(banner)、弹窗
30
+
31
+
32
+ ### **推送时间:**
33
+
34
+ 1. **“Daily push”:****固定每天早上的时间(8h30-9h):**每日运势
35
+ 1. **生日/节日 ****“Celebration”:****特殊日的早上(8h30-9h):**生日祝福、节日祝福结合每日运势文案,如:“xxx祝你生日快乐,今日宇宙与你的链接最强,来看看今天的牌有什么指引”
36
+ 1. **要apple store 好评、评论**** “Review”:****晚上20h(8:00pm)**
37
+ 1. **折扣/福利 ****“Offer”:****根据用户习惯使用时间(初始默认中午12h30):**折扣福利、社区引流、专属权益
38
+ 1. **社区话题**** “Community”:****晚上20h(8:00pm):**加入社区、话题讨论
39
+ 1. 用户个性化设置推送时间(站内的推送)
40
+
41
+ 需要监测点:
42
+
43
+ push到达率、push打开率、push转化率、卸载率
44
+
45
+
46
+ # 二、需求详情
47
+
48
+ ### 2.1 权限获取
49
+
50
+ **新增字段:notification_permission_status**
51
+
52
+ #### 规则:
53
+
54
+ 1. **APNs 权限获取弹窗:**
55
+ ** 授权**
56
+
57
+ ** 拒绝:**拒绝后官方默认不会再出现,用户使用**一个月后站内弹窗**出现:提醒用户去iphone**后台打开权限**,再次拒绝后本**年度**不会再提醒
58
+
59
+
60
+ 1. **站内权限获取弹窗:**
61
+ 默认开着权限,
62
+
63
+ ~~但在设置页面也加一个“关闭弹窗”的选项通道,如果用户自己关闭了,就每过30天时间弹一次权限弹窗提醒~~
64
+
65
+ ~~** 授权**~~
66
+
67
+ ~~ ~~~~** 拒绝:**~~~~用户使用~~~~**30天后站内弹窗再次**~~~~出现,提醒打开站内消息提醒~~
68
+
69
+
70
+ 在 **23:00 - 08:00** 之间禁止发送任何push
71
+
72
+
73
+
74
+ **取值**
75
+
76
+ - not_determined (从未请求过权限)
77
+ - denied(用户明确拒绝/关闭)
78
+ - authorized(已授权)
79
+ | 场景 | 说明 |
80
+ | --- | --- |
81
+ | | **弹窗时机****—站外系统通知权限 ** **苹果官方的 APNs (Apple Push Notification service)** |
82
+ | | **弹窗时机—站内系统通知跳转ios系统权限** |
83
+
84
+ ### 2.2 push内容
85
+
86
+ | 位置 | 类型 | 优先级 | 推送对象 Audience | Trigger (触发条件) | 推送时间 | UI原型 | 标题 / 内容 | | 点击跳转页面 |
87
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
88
+ | ios系统push | ~~**生日**~~**/节日** **Celebration** | 极高 | 节日for全体用户 ~~生日for个别用户(等加入)~~ | Auto 3月 St. Patrick's Day | 用户时区每天8:30am | 情人节 | 随机抽取 [WooMoo 节日Push](https://romangic.sg.larksuite.com/wiki/M8jnwaDUxiFZ46kxe9SlRCMlgQd)中对应的内容 即将到来的:3月 St. Patrick's Day | | 首页 |
89
+ | ios系统push | **Daily push** | 高 (核心留存) | 全体用户 | Auto | 用户时区每天8:30am | | 随机抽取[WooMoo DailyPush](https://romangic.sg.larksuite.com/wiki/JPNAwDX3tiWR30kKPqklcElJghd?sheet=a33f4c)中任意一条标题+内容 | | 首页 |
90
+ | 站内push、Discord社区 | **要apple store 好评、评论** **“Review”** | 中高 | 全体用户 | 用户解读完一次触发,不管是Quick card 还是Deep drive 都是完成一次“**reading completed”**就触发 **第一次弹窗“banner push comment”** (现在设置一个评论就赠送2次免费解读的活动) | | | **Headline:** **Love our readings? 🔮** **Body:** **Score more free readings! Just rate us and snap a screenshot of your review.** **按钮:关闭icon / Go!** **按钮:** **Go on Discord** | | 跳转到外部iOS评价 |
91
+ | ios系统push、社区、邮箱、站内信 | **折扣/福利/活动** **“Offer”** | 中高 | ~~活跃用户(激励)~~ 全体用户 | 有产品福利活动推送的时候 | **站外push:**用户时区中午12:30pm **站内**:当天打开App就显示 社区、邮箱:群发 | | 按照福利活动计划再编辑 | | 与用户身份相对应的订阅墙、 跳转Discord |
92
+ | ~~iOS系统push、邮箱~~ | ~~**折扣/福利**~~ | ~~中高~~ | ~~沉睡用户(召回)~~ | | | | | | 订阅墙 |
93
+ | iOS系统push、站内push、Discord社区 | **社区话题** **“Community”** | 中 | 全体用户 | **循环触发:** 每周五晚上 **8:00pm** **站内触发场景**:用户打开App,到达首页 | **站外push**:用户时区每周五晚上 8:00pm **站内:**当天打开App就显示 时效:24h 社区:运营群发 | | 标题:(以情人节话题为例) Valentine’s is coming—got tea? Body:Join our Discord to spill the tea & score exclusive freebie alerts. Don't miss the steal! [WooMoo 社区引流](https://romangic.sg.larksuite.com/wiki/Fjsnw2zLXiHDVzkDl9yliZpVgre?from=from_copylink) | | 跳转到外部Diacord里WooMoo对话加入 |
94
+
95
+ # 三、数据需求
96
+
97
+ **埋点需求**
98
+
99
+ [嵌入表格: N8cAsiN6yhVh8dtP5MOl0MSggIf_hyggyt]
100
+
101
+ # 调研附录
102
+
103
+ ### 竞品调研
104
+
105
+ (目前调研的竞品中moonly和costar的push比较积极:moonly上午9:00和11:00各一次;costar11:30一次)
106
+
107
+ | 产品 | 图例 | 流程简述 |
108
+ | --- | --- | --- |
109
+ | starla | | 设计:以主页为底,直接跳系统页; timing:先要评分,然后对其他APP的活动追踪,接着要push权限; 手动关闭系统设置后,APP内没有内置按钮可以打开。 |
110
+ | Co-Star | | 设计:有消息示例(每日更新、好友申请通知) timing:注册完直接跳转打开通知 二次请求:设置了not right now按钮(较小),点击后跳转到消息定制页(每日建议、好友申请通知、申请通过通知)且三个开关默认打开 内置按钮:APP内置推送按钮,点击后跳转到系统页,同意后进入消息定制页,这里新增(联系人加入通知、Eros) |
111
+ | Moonly | | 设计:背景为使用前后变化对比 timing:注册后直接跳出系统页 |
112
+ | CHANI | | 设计:登录页 timing:首次打开应用后 手动关闭系统设置后,APP内没有内置按钮可以打开; |
113
+ | Futurio | | 设计:通知图标为底 timing:注册后直接选通知时间段,选完后弹出系统页,拒绝后收集邮箱 内置按钮:setting处有邮箱和通知权限按钮 |
114
+ | MoonX (Moon calendar) | | 设计:消息示例 timing:打开APP未注册,先要地理位置权限,然后要push权限,点继续后跳转到系统页 内置按钮:setting中有细分通知按钮,可选多个消息类别、提示时间 |
115
+ | Astrotalk | | 设计:登录页 timing:首次打开就要push权限 没有内置push开关 |
116
+ | Faladin | | 设计: timing:注册后要通知权限,点击非高亮处退出 内置按钮:setting处有通知按钮,打开后可选择性关闭不同功能按钮 |
117
+ | nebula | | |
118
+ | headspace | | |
119
+
@@ -0,0 +1,186 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import * as yaml from 'yaml';
6
+
7
+ // 直接测试配置解析逻辑,不依赖 process.cwd()
8
+ interface DocConfig {
9
+ name: string;
10
+ type: 'feishu' | 'notion';
11
+ url: string;
12
+ output: string;
13
+ }
14
+
15
+ interface GroworkConfig {
16
+ feishu?: { appId: string; appSecret: string; domain?: 'feishu' | 'lark' };
17
+ notion?: { token: string };
18
+ docs: DocConfig[];
19
+ }
20
+
21
+ function validateConfig(config: GroworkConfig): void {
22
+ if (!config.docs || config.docs.length === 0) {
23
+ throw new Error('配置文件中没有配置任何文档');
24
+ }
25
+
26
+ const hasFeishuDocs = config.docs.some(d => d.type === 'feishu');
27
+ const hasNotionDocs = config.docs.some(d => d.type === 'notion');
28
+
29
+ if (hasFeishuDocs && (!config.feishu?.appId || !config.feishu?.appSecret)) {
30
+ throw new Error('配置文件缺少飞书凭证 (feishu.appId, feishu.appSecret)');
31
+ }
32
+
33
+ if (hasNotionDocs && !config.notion?.token) {
34
+ throw new Error('配置文件缺少 Notion 凭证 (notion.token)');
35
+ }
36
+ }
37
+
38
+ describe('配置文件解析', () => {
39
+ it('应正确解析有效的 YAML 配置', () => {
40
+ const yamlContent = `
41
+ feishu:
42
+ appId: "cli_test"
43
+ appSecret: "secret_test"
44
+ docs:
45
+ - name: prd
46
+ type: feishu
47
+ url: "https://xxx.feishu.cn/docx/abc123"
48
+ output: "docs/prd.md"
49
+ `;
50
+ const config = yaml.parse(yamlContent) as GroworkConfig;
51
+
52
+ assert.strictEqual(config.feishu?.appId, 'cli_test');
53
+ assert.strictEqual(config.docs.length, 1);
54
+ assert.strictEqual(config.docs[0].name, 'prd');
55
+ assert.strictEqual(config.docs[0].type, 'feishu');
56
+ });
57
+
58
+ it('应解析包含 Notion 配置的 YAML', () => {
59
+ const yamlContent = `
60
+ notion:
61
+ token: "ntn_test_token"
62
+ docs:
63
+ - name: notion-doc
64
+ type: notion
65
+ url: "https://www.notion.so/abc123"
66
+ output: "docs/notion.md"
67
+ `;
68
+ const config = yaml.parse(yamlContent) as GroworkConfig;
69
+
70
+ assert.strictEqual(config.notion?.token, 'ntn_test_token');
71
+ assert.strictEqual(config.docs[0].type, 'notion');
72
+ });
73
+
74
+ it('应解析混合配置(飞书 + Notion)', () => {
75
+ const yamlContent = `
76
+ feishu:
77
+ appId: "cli_test"
78
+ appSecret: "secret_test"
79
+ notion:
80
+ token: "ntn_test"
81
+ docs:
82
+ - name: feishu-doc
83
+ type: feishu
84
+ url: "https://xxx.feishu.cn/docx/abc"
85
+ output: "docs/feishu.md"
86
+ - name: notion-doc
87
+ type: notion
88
+ url: "https://www.notion.so/def"
89
+ output: "docs/notion.md"
90
+ `;
91
+ const config = yaml.parse(yamlContent) as GroworkConfig;
92
+
93
+ assert.strictEqual(config.docs.length, 2);
94
+ assert.strictEqual(config.docs[0].type, 'feishu');
95
+ assert.strictEqual(config.docs[1].type, 'notion');
96
+ });
97
+ });
98
+
99
+ describe('配置文件验证', () => {
100
+ it('应拒绝空文档配置', () => {
101
+ const config: GroworkConfig = {
102
+ feishu: { appId: 'test', appSecret: 'test' },
103
+ docs: []
104
+ };
105
+
106
+ assert.throws(
107
+ () => validateConfig(config),
108
+ /配置文件中没有配置任何文档/
109
+ );
110
+ });
111
+
112
+ it('应拒绝缺少飞书凭证的配置', () => {
113
+ const config: GroworkConfig = {
114
+ docs: [{
115
+ name: 'test',
116
+ type: 'feishu',
117
+ url: 'https://xxx.feishu.cn/docx/abc',
118
+ output: 'docs/test.md'
119
+ }]
120
+ };
121
+
122
+ assert.throws(
123
+ () => validateConfig(config),
124
+ /配置文件缺少飞书凭证/
125
+ );
126
+ });
127
+
128
+ it('应拒绝缺少 Notion 凭证的配置', () => {
129
+ const config: GroworkConfig = {
130
+ docs: [{
131
+ name: 'test',
132
+ type: 'notion',
133
+ url: 'https://www.notion.so/abc',
134
+ output: 'docs/test.md'
135
+ }]
136
+ };
137
+
138
+ assert.throws(
139
+ () => validateConfig(config),
140
+ /配置文件缺少 Notion 凭证/
141
+ );
142
+ });
143
+
144
+ it('应接受只有飞书文档的配置(无需 Notion 凭证)', () => {
145
+ const config: GroworkConfig = {
146
+ feishu: { appId: 'test', appSecret: 'test' },
147
+ docs: [{
148
+ name: 'test',
149
+ type: 'feishu',
150
+ url: 'https://xxx.feishu.cn/docx/abc',
151
+ output: 'docs/test.md'
152
+ }]
153
+ };
154
+
155
+ assert.doesNotThrow(() => validateConfig(config));
156
+ });
157
+
158
+ it('应接受只有 Notion 文档的配置(无需飞书凭证)', () => {
159
+ const config: GroworkConfig = {
160
+ notion: { token: 'ntn_test' },
161
+ docs: [{
162
+ name: 'test',
163
+ type: 'notion',
164
+ url: 'https://www.notion.so/abc',
165
+ output: 'docs/test.md'
166
+ }]
167
+ };
168
+
169
+ assert.doesNotThrow(() => validateConfig(config));
170
+ });
171
+
172
+ it('混合文档配置需要所有相关凭证', () => {
173
+ const configMissingNotion: GroworkConfig = {
174
+ feishu: { appId: 'test', appSecret: 'test' },
175
+ docs: [
176
+ { name: 'feishu', type: 'feishu', url: 'https://xxx.feishu.cn/docx/a', output: 'a.md' },
177
+ { name: 'notion', type: 'notion', url: 'https://www.notion.so/b', output: 'b.md' }
178
+ ]
179
+ };
180
+
181
+ assert.throws(
182
+ () => validateConfig(configMissingNotion),
183
+ /配置文件缺少 Notion 凭证/
184
+ );
185
+ });
186
+ });