@puyinkai/xiaobao-cli 0.1.6 → 0.1.9

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.
Files changed (51) hide show
  1. package/dist/{api-client-DWbwtQbl.mjs → api-client-CDNjkArS.mjs} +2 -2
  2. package/dist/{api-JZnABA4y.mjs → api-fsHHWWa3.mjs} +5 -4
  3. package/dist/{audio-BHWH6uhw.mjs → audio-CRSMqaKo.mjs} +2 -2
  4. package/dist/{auth-BpzWivxf.mjs → auth-_3hBh87U.mjs} +3 -3
  5. package/dist/cli.mjs +10 -10
  6. package/dist/{consultant-kV5r8Wj8.mjs → consultant-E33hTGUO.mjs} +1 -1
  7. package/dist/{customer-B2OV8BTR.mjs → customer-DcQ83oKD.mjs} +1 -1
  8. package/dist/{focus-BjWD5PRV.mjs → focus-XMRxnHPw.mjs} +1 -1
  9. package/dist/{list-C30zFN6h.mjs → list-B3nN4pd2.mjs} +1 -1
  10. package/dist/{list-CHububT5.mjs → list-CXquxzex.mjs} +1 -1
  11. package/dist/{list-FM3vkQJa.mjs → list-Cc4zcaNz.mjs} +1 -1
  12. package/dist/{list-VpZkzTEE.mjs → list-CnlQdzPp.mjs} +1 -1
  13. package/dist/{list-Bw8dFf8a.mjs → list-CrMOrnS-.mjs} +1 -1
  14. package/dist/{list-h1nuSJiN.mjs → list-DFdDcHFq.mjs} +1 -1
  15. package/dist/{list-CVMSGK08.mjs → list-dm9YaCG1.mjs} +1 -1
  16. package/dist/login-CUUcjWPd.mjs +156 -0
  17. package/dist/{logout-DVJX8_x6.mjs → logout-DP7REhCd.mjs} +2 -1
  18. package/dist/{project-CJkTgGOq.mjs → project-BD0g4oAW.mjs} +1 -1
  19. package/dist/{qa-CQDRFw-g.mjs → qa-CdMcxCE_.mjs} +1 -1
  20. package/dist/{resistance-Ss-eUZoN.mjs → resistance-6_V6At3T.mjs} +1 -1
  21. package/dist/{text-5q8lIWi8.mjs → text-C3Ap6L5n.mjs} +1 -1
  22. package/dist/{token-store-D2umpIk-.mjs → token-store-CXizLE2_.mjs} +37 -1
  23. package/dist/{visit-CUI2ov-H.mjs → visit-DoeECi8w.mjs} +1 -1
  24. package/dist/{whoami-Uv7GBtGB.mjs → whoami-z-76WaCP.mjs} +2 -2
  25. package/package.json +5 -1
  26. package/skills/wangxiaobao-audio-query/SKILL.md +2 -2
  27. package/skills/wangxiaobao-audio-wiki/SKILL.md +4 -4
  28. package/skills/wangxiaobao-customer-focus-query/SKILL.md +2 -2
  29. package/skills/wangxiaobao-customer-query/SKILL.md +2 -2
  30. package/skills/wangxiaobao-customer-resistance-query/SKILL.md +2 -2
  31. package/skills/wangxiaobao-html-to-pdf/SKILL.md +116 -0
  32. package/skills/wangxiaobao-html-to-pdf/scripts/html2pdf.py +132 -0
  33. package/skills/wangxiaobao-html-to-pdf/scripts/html2pdf_exact.py +92 -0
  34. package/skills/wangxiaobao-project-daily-report/SKILL.md +170 -0
  35. package/skills/wangxiaobao-project-daily-report/_meta.json +9 -0
  36. package/skills/wangxiaobao-project-daily-report/references/report_template.html +308 -0
  37. package/skills/wangxiaobao-project-daily-report/scripts/fetch_data.py +219 -0
  38. package/skills/wangxiaobao-project-daily-report/scripts/generate_report.py +1029 -0
  39. package/skills/wangxiaobao-project-daily-report/scripts/run.py +34 -0
  40. package/skills/wangxiaobao-project-weekly-report/SKILL.md +169 -0
  41. package/skills/wangxiaobao-project-weekly-report/scripts/generate_report.py +308 -0
  42. package/skills/wangxiaobao-project-weekly-report/scripts/html2pdf_fast.py +161 -0
  43. package/skills/wangxiaobao-project-weekly-report/scripts/logo_wangai.png +0 -0
  44. package/skills/wangxiaobao-project-weekly-report/scripts/logo_wangxiaobao.png +0 -0
  45. package/skills/wangxiaobao-project-weekly-report/scripts/prepare_data.py +682 -0
  46. package/skills/wangxiaobao-project-weekly-report/scripts/template.py +925 -0
  47. package/skills/wangxiaobao-quick-qa/SKILL.md +2 -2
  48. package/skills/wangxiaobao-shared/SKILL.md +40 -7
  49. package/skills/wangxiaobao-switch-project/SKILL.md +3 -3
  50. package/skills/wangxiaobao-visit-query/SKILL.md +2 -2
  51. package/dist/login-DtDxISE8.mjs +0 -91
@@ -0,0 +1,308 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{project_name} 日报 {date}</title>
7
+ <style>
8
+ * { box-sizing: border-box; margin: 0; padding: 0; }
9
+ body { font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif; font-size: 12px; color: #333; background: #fff; padding: 15px; }
10
+ table { width: 100%; border-collapse: collapse; margin-bottom: 12px; }
11
+ th, td { border: 1px solid #ddd; padding: 6px 8px; text-align: center; vertical-align: middle; font-size: 11px; }
12
+ th { color: #fff; font-weight: bold; }
13
+
14
+ .title-bg { background: #1F4E79; }
15
+ .orange-bg { background: #975765; }
16
+ .pink-bg { background: #EEE2E6; color: #333; }
17
+ .peach-bg { background: #C59BA6; }
18
+ .light-bg { background: #f5f5f5; }
19
+
20
+ .kpi-row { display: flex; gap: 8px; margin-bottom: 12px; }
21
+ .kpi-card { flex: 1; border: 2px solid #1F4E79; border-radius: 4px; padding: 10px; text-align: center; background: #fafafa; }
22
+ .kpi-card .label { font-size: 10px; color: #666; margin-bottom: 3px; }
23
+ .kpi-card .value { font-size: 22px; font-weight: bold; color: #1F4E79; line-height: 1.2; }
24
+ .kpi-card .sub { font-size: 9px; color: #888; margin-top: 3px; }
25
+
26
+ .col-2 { display: flex; gap: 12px; margin-bottom: 12px; }
27
+ .col-2 > div { flex: 1; }
28
+
29
+ .section-title { background: #1F4E79; color: #fff; font-size: 13px; font-weight: bold; padding: 8px; text-align: center; }
30
+ .table-header { background: #975765; color: #fff; font-weight: bold; }
31
+ .table-header-pink { background: #EEE2E6; color: #333; font-weight: bold; }
32
+ .table-header-peach { background: #C59BA6; color: #fff; font-weight: bold; }
33
+
34
+ .data-value { font-weight: bold; font-size: 14px; color: #1F4E79; }
35
+ .data-red { color: #c00; font-weight: bold; }
36
+
37
+ tr:nth-child(even) { background: #f9f9f9; }
38
+
39
+ .footer { text-align: center; padding: 15px; color: #999; font-size: 10px; border-top: 1px solid #ddd; margin-top: 20px; }
40
+ </style>
41
+ </head>
42
+ <body>
43
+
44
+ <!-- 项目标题 -->
45
+ <table>
46
+ <tr>
47
+ <td class="title-bg" style="font-size: 18px; font-weight: bold; padding: 12px; text-align: center;">
48
+ {project_name} 营销日报
49
+ </td>
50
+ </tr>
51
+ <tr>
52
+ <td style="text-align: center; padding: 6px; color: #666; background: #f5f5f5; font-size: 11px;">
53
+ 数据日期:{yesterday}(昨日) | 统计周期:{month_start} ~ {yesterday} | 截至时间:23:59
54
+ </td>
55
+ </tr>
56
+ </table>
57
+
58
+ <!-- KPI概览 -->
59
+ <div class="kpi-row">
60
+ <div class="kpi-card">
61
+ <div class="label">本月来访(组)</div>
62
+ <div class="value">{month_visit_actual}</div>
63
+ <div class="sub">目标{month_visit_target}组 | 完成率{visit_complete_rate}%</div>
64
+ </div>
65
+ <div class="kpi-card">
66
+ <div class="label">来访进度达成</div>
67
+ <div class="value">{progress_rate}%</div>
68
+ <div class="sub">时间进度{time_progress}% | 超额{over_progress}%</div>
69
+ </div>
70
+ <div class="kpi-card">
71
+ <div class="label">本月成交(组)</div>
72
+ <div class="value">{month_deals}</div>
73
+ <div class="sub">目标待确认 | 成交金额待填报</div>
74
+ </div>
75
+ <div class="kpi-card">
76
+ <div class="label">昨日来访(组)</div>
77
+ <div class="value">{yesterday_visits}</div>
78
+ <div class="sub">本周来访量平稳</div>
79
+ </div>
80
+ </div>
81
+
82
+ <!-- 来访指标 + 来访意向等级 -->
83
+ <div class="col-2">
84
+ <div>
85
+ <table>
86
+ <tr><td colspan="6" class="section-title" style="font-size:12px;">本月来访指标及达成</td></tr>
87
+ <tr class="table-header">
88
+ <td>月度客储指标</td>
89
+ <td>截至昨日来访</td>
90
+ <td>完成率</td>
91
+ <td>进度达成率</td>
92
+ <td>总组数</td>
93
+ <td>转化率</td>
94
+ </tr>
95
+ <tr>
96
+ <td class="data-value">{month_visit_target}</td>
97
+ <td class="data-value">{month_visit_actual}</td>
98
+ <td class="data-red">{visit_complete_rate}%</td>
99
+ <td class="data-red">{progress_rate}%</td>
100
+ <td class="data-value">{month_visit_actual}</td>
101
+ <td>-</td>
102
+ </tr>
103
+ </table>
104
+ </div>
105
+ <div>
106
+ <table>
107
+ <tr><td colspan="5" class="section-title" style="font-size:12px;">本月来访客户意向等级分析</td></tr>
108
+ <tr class="table-header">
109
+ <td>A级(高意向)</td>
110
+ <td>B级(中高)</td>
111
+ <td>C级(中意向)</td>
112
+ <td>D级(低意向)</td>
113
+ <td>合计</td>
114
+ </tr>
115
+ <tr>
116
+ <td class="data-value">{intent_A}</td>
117
+ <td class="data-value">{intent_B}</td>
118
+ <td class="data-value">{intent_C}</td>
119
+ <td class="data-value">{intent_D}</td>
120
+ <td class="data-value" style="font-size:16px;">{intent_total}</td>
121
+ </tr>
122
+ <tr style="background:#f5f5f5;">
123
+ <td>{intent_A_pct}%</td>
124
+ <td>{intent_B_pct}%</td>
125
+ <td>{intent_C_pct}%</td>
126
+ <td>{intent_D_pct}%</td>
127
+ <td>100%</td>
128
+ </tr>
129
+ </table>
130
+ </div>
131
+ </div>
132
+
133
+ <!-- 销售指标 + 成交意向等级 -->
134
+ <div class="col-2">
135
+ <div>
136
+ <table>
137
+ <tr><td colspan="6" class="section-title" style="font-size:12px;">本月销售指标及达成</td></tr>
138
+ <tr class="table-header">
139
+ <td>月度销售指标(万)</td>
140
+ <td>截至昨日成交(万)</td>
141
+ <td>完成率</td>
142
+ <td>进度达成率</td>
143
+ <td>总套数</td>
144
+ <td>转化率</td>
145
+ </tr>
146
+ <tr>
147
+ <td class="data-value">{month_sales_target:,}</td>
148
+ <td class="data-value">待填报</td>
149
+ <td class="data-red">-</td>
150
+ <td class="data-red">-</td>
151
+ <td class="data-value">{month_deals}</td>
152
+ <td>-</td>
153
+ </tr>
154
+ <tr style="background:#fff3cd; font-size:9px;">
155
+ <td colspan="6" style="text-align:left;">⚠ 成交金额待确认,建议从销售管理系统补充数据</td>
156
+ </tr>
157
+ </table>
158
+ </div>
159
+ <div>
160
+ <table>
161
+ <tr><td colspan="5" class="section-title" style="font-size:12px;">本月成交客户意向等级分析</td></tr>
162
+ <tr class="table-header">
163
+ <td>A级</td>
164
+ <td>B级</td>
165
+ <td>C/C+级</td>
166
+ <td>D级</td>
167
+ <td>合计</td>
168
+ </tr>
169
+ <tr>
170
+ <td class="data-value">{deal_A}</td>
171
+ <td>{deal_B}</td>
172
+ <td>{deal_C}</td>
173
+ <td>{deal_D}</td>
174
+ <td class="data-value">{month_deals}</td>
175
+ </tr>
176
+ </table>
177
+ </div>
178
+ </div>
179
+
180
+ <!-- 来访渠道分布 -->
181
+ <table>
182
+ <tr><td colspan="5" class="section-title" style="font-size:12px;">来访渠道分布(本月)</td></tr>
183
+ <tr class="table-header">
184
+ <td>渠道类型</td>
185
+ <td>来访组数</td>
186
+ <td>占比</td>
187
+ <td>意向等级分布</td>
188
+ <td>备注</td>
189
+ </tr>
190
+ {channel_rows}
191
+ <tr style="background:#1F4E79; color:#fff;">
192
+ <td><strong>合计</strong></td>
193
+ <td class="data-value" style="color:#fff;">{channel_total}</td>
194
+ <td>100%</td>
195
+ <td>A:{intent_A}, B:{intent_B}, C:{intent_C}, D:{intent_D}</td>
196
+ <td>-</td>
197
+ </tr>
198
+ </table>
199
+
200
+ <!-- 存量客户汇总 -->
201
+ <table>
202
+ <tr><td colspan="7" class="section-title" style="font-size:12px;">存量客户本周复访 - 近3月未成交({three_months_ago} ~ {yesterday})</td></tr>
203
+ <tr class="table-header">
204
+ <td>渠道</td>
205
+ <td>未成交总数</td>
206
+ <td>A级</td>
207
+ <td>B级</td>
208
+ <td>C级</td>
209
+ <td>D级</td>
210
+ <td>占比</td>
211
+ </tr>
212
+ {stock_rows}
213
+ <tr style="background:#1F4E79; color:#fff;">
214
+ <td><strong>合计</strong></td>
215
+ <td class="data-value" style="color:#fff;">{stock_total}</td>
216
+ <td>{stock_A}</td>
217
+ <td>{stock_B}</td>
218
+ <td>{stock_C}</td>
219
+ <td>{stock_D}</td>
220
+ <td>100%</td>
221
+ </tr>
222
+ </table>
223
+
224
+ <!-- 存量客户明细 -->
225
+ <table>
226
+ <tr><td colspan="12" class="section-title" style="font-size:12px; background:#C59BA6;">存量客户预计本周复访明细</td></tr>
227
+ <tr class="table-header-peach">
228
+ <td style="width:30px;">序号</td>
229
+ <td>客户姓名</td>
230
+ <td>来访渠道</td>
231
+ <td>置业顾问</td>
232
+ <td>最近来访</td>
233
+ <td>意向等级</td>
234
+ <td>跟进情况</td>
235
+ <td>决策周期</td>
236
+ <td>未成交原因</td>
237
+ <td>复访概率</td>
238
+ <td>成交概率</td>
239
+ <td>跟进策略</td>
240
+ </tr>
241
+ {customer_rows}
242
+ </table>
243
+
244
+ <!-- 昨日来访明细 -->
245
+ <table>
246
+ <tr><td colspan="7" class="section-title" style="font-size:12px;">昨日来访明细({yesterday} 共{yesterday_visits}组)</td></tr>
247
+ <tr class="table-header">
248
+ <td style="width:30px;">序号</td>
249
+ <td>到访时间</td>
250
+ <td>顾问</td>
251
+ <td>到访次数</td>
252
+ <td>停留时长</td>
253
+ <td>首访/复访</td>
254
+ <td>备注</td>
255
+ </tr>
256
+ {yesterday_rows}
257
+ </table>
258
+
259
+ <!-- 数据说明 -->
260
+ <table>
261
+ <tr><td colspan="4" class="section-title" style="font-size:12px;">数据说明</td></tr>
262
+ <tr class="table-header-pink">
263
+ <td>数据项</td>
264
+ <td>数据来源</td>
265
+ <td>数据状态</td>
266
+ <td>备注</td>
267
+ </tr>
268
+ <tr>
269
+ <td>来访数据</td>
270
+ <td>旺小宝来访记录API</td>
271
+ <td class="data-value" style="color:#0a0;">已获取</td>
272
+ <td>{month_start}~{yesterday}共{month_visit_actual}组</td>
273
+ </tr>
274
+ <tr>
275
+ <td>意向等级</td>
276
+ <td>旺小宝客户画像API</td>
277
+ <td class="data-value" style="color:#0a0;">已获取</td>
278
+ <td>顾问评级(A/B/C/D)</td>
279
+ </tr>
280
+ <tr>
281
+ <td>渠道分类</td>
282
+ <td>旺小宝dynamicTags</td>
283
+ <td class="data-value" style="color:#0a0;">已获取</td>
284
+ <td>五大类来访渠道</td>
285
+ </tr>
286
+ <tr>
287
+ <td>成交数据</td>
288
+ <td>旺小宝客户画像API</td>
289
+ <td class="data-value" style="color:#f90;">待确认</td>
290
+ <td>本月成交{month_deals}组,金额待填报</td>
291
+ </tr>
292
+ <tr>
293
+ <td>月度KPI指标</td>
294
+ <td>IMA知识库/配置</td>
295
+ <td class="data-value" style="color:#0a0;">已配置</td>
296
+ <td>来访{month_visit_target}组/销售{month_sales_target}万</td>
297
+ </tr>
298
+ </table>
299
+
300
+ <!-- 页脚 -->
301
+ <div class="footer">
302
+ <p>{project_name} 营销日报 | 数据日期:{yesterday} | 生成时间:{date}</p>
303
+ <p>数据来源:旺小宝智能营销系统 | 本月来访{month_visit_actual}组 | 意向客户{intent_A}组(A级)</p>
304
+ <p style="color:#c00; font-weight:bold;">⚠ 成交金额待从销售管理系统补充,建议每日同步</p>
305
+ </div>
306
+
307
+ </body>
308
+ </html>
@@ -0,0 +1,219 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 旺小宝项目日报数据拉取脚本
4
+ 功能:拉取旺小宝API数据,生成日报所需数据
5
+ """
6
+ import json
7
+ import subprocess
8
+ import os
9
+ import argparse
10
+ from collections import Counter
11
+
12
+ # 项目配置(优先级:命令行参数 > 环境变量 > 默认值)
13
+ TENANT_ID = os.environ.get("XIAOBAO_TENANT_ID", "")
14
+ PROJECT_ID = os.environ.get("XIAOBAO_PROJECT_ID", "")
15
+ PROJECT_NAME = os.environ.get("XIAOBAO_PROJECT_NAME", "<项目名>")
16
+ # KPI指标(示例值 500 / 21000,请按项目实际目标调整)
17
+ MONTH_VISIT_TARGET = int(os.environ.get("XIAOBAO_MONTH_VISIT_TARGET", "500"))
18
+ MONTH_SALES_TARGET = int(os.environ.get("XIAOBAO_MONTH_SALES_TARGET", "21000"))
19
+ TOKEN_PATH = os.path.join(os.path.expanduser("~"), ".openclaw/state/wangxiaobao/token.json")
20
+
21
+
22
+ def get_token():
23
+ """获取旺小宝access token"""
24
+ with open(TOKEN_PATH, 'r') as f:
25
+ return json.load(f)['access_token']
26
+
27
+
28
+ def api_call(endpoint, body, token):
29
+ """调用旺小宝API"""
30
+ cmd = [
31
+ 'curl', '-s', '-X', 'POST',
32
+ f'https://open-ai.wangxiaobao.com{endpoint}',
33
+ '-H', f'Authorization: Bearer {token}',
34
+ '-H', f'X-Tenant-Id: {TENANT_ID}',
35
+ '-H', f'X-Project-Id: {PROJECT_ID}',
36
+ '-H', 'Content-Type: application/json',
37
+ '-d', body
38
+ ]
39
+ result = subprocess.run(cmd, capture_output=True, text=True)
40
+ return json.loads(result.stdout)
41
+
42
+
43
+ def get_yesterday_visits(token, yesterday):
44
+ """获取昨日来访数据"""
45
+ body = json.dumps({
46
+ "current": 1, "size": 100,
47
+ "startTime": f"{yesterday} 00:00:00",
48
+ "endTime": f"{yesterday} 23:59:59"
49
+ })
50
+ data = api_call("/ai-open/visits/page", body, token)
51
+ all_visits = data.get('data', {}).get('content', [])
52
+ # 过滤只保留昨日
53
+ return [v for v in all_visits if v.get('visitTime', '').startswith(yesterday)]
54
+
55
+
56
+ def get_month_visits(token, month_start, yesterday):
57
+ """获取本月来访数据"""
58
+ body = json.dumps({
59
+ "current": 1, "size": 500,
60
+ "startTime": f"{month_start} 00:00:00",
61
+ "endTime": f"{yesterday} 23:59:59"
62
+ })
63
+ data = api_call("/ai-open/visits/page", body, token)
64
+ all_visits = data.get('data', {}).get('content', [])
65
+ # 过滤本月
66
+ return [v for v in all_visits if v.get('visitTime', '').startswith('2026-05-')]
67
+
68
+
69
+ def get_customers(token, month_start, yesterday):
70
+ """获取客户画像数据"""
71
+ body = json.dumps({
72
+ "page": 1, "size": 500,
73
+ "startTime": f"{month_start} 00:00:00",
74
+ "endTime": f"{yesterday} 23:59:59"
75
+ })
76
+ data = api_call("/ai-open/customers/page", body, token)
77
+ return data.get('data', {}).get('content', [])
78
+
79
+
80
+ def get_stock_customers(token, three_months_ago, yesterday):
81
+ """获取存量客户(近3月未成交)"""
82
+ body = json.dumps({
83
+ "page": 1, "size": 100,
84
+ "startTime": f"{three_months_ago} 00:00:00",
85
+ "endTime": f"{yesterday} 23:59:59"
86
+ })
87
+ data = api_call("/ai-open/customers/page", body, token)
88
+ all_custs = data.get('data', {}).get('content', [])
89
+ # 过滤未成交
90
+ return [c for c in all_custs if c.get('dealStatus') != 1]
91
+
92
+
93
+ def analyze_intent(customers):
94
+ """分析意向等级分布"""
95
+ intent_dist = Counter()
96
+ for c in customers:
97
+ level = c.get('intentLevel', '未知') or '未知'
98
+ if level == '':
99
+ level = '未知'
100
+ intent_dist[level] += 1
101
+ return dict(intent_dist)
102
+
103
+
104
+ def analyze_channel(customers):
105
+ """分析渠道分布"""
106
+ channel_dist = Counter()
107
+ for c in customers:
108
+ tags = c.get('dynamicTags', {})
109
+ channel = tags.get('五大类来访渠道', '未知')
110
+ if channel == '':
111
+ channel = '未知'
112
+ channel_dist[channel] += 1
113
+ return dict(channel_dist)
114
+
115
+
116
+ def main(date_str='2026-05-21'):
117
+ """主函数"""
118
+ today = date_str
119
+ yesterday = '2026-05-20'
120
+ month_start = '2026-05-01'
121
+ three_months_ago = '2026-02-20'
122
+
123
+ print("="*60)
124
+ print(f"{PROJECT_NAME}日报数据拉取 - {today}")
125
+ print("="*60)
126
+
127
+ token = get_token()
128
+
129
+ # 1. 昨日来访
130
+ print("\n[1] 拉取昨日来访数据...")
131
+ yesterday_visits = get_yesterday_visits(token, yesterday)
132
+ print(f" 昨日来访: {len(yesterday_visits)}组")
133
+
134
+ # 2. 本月来访
135
+ print("\n[2] 拉取本月来访数据...")
136
+ month_visits = get_month_visits(token, month_start, yesterday)
137
+ print(f" 本月来访: {len(month_visits)}组")
138
+
139
+ # 3. 客户画像
140
+ print("\n[3] 拉取客户画像数据...")
141
+ customers = get_customers(token, month_start, yesterday)
142
+ print(f" 本月客户总数: {len(customers)}")
143
+
144
+ # 意向等级
145
+ intent_dist = analyze_intent(customers)
146
+ print("\n 意向等级分布:")
147
+ for level, cnt in sorted(intent_dist.items(), key=lambda x: -x[1]):
148
+ print(f" {level}: {cnt}组")
149
+
150
+ # 渠道分布
151
+ channel_dist = analyze_channel(customers)
152
+ print("\n 渠道分布:")
153
+ for ch, cnt in sorted(channel_dist.items(), key=lambda x: -x[1])[:5]:
154
+ print(f" {ch}: {cnt}组")
155
+
156
+ # 4. 存量客户
157
+ print("\n[4] 拉取存量客户数据...")
158
+ stock_customers = get_stock_customers(token, three_months_ago, yesterday)
159
+ print(f" 存量客户(未成交): {len(stock_customers)}组")
160
+
161
+ # 5. 成交客户
162
+ print("\n[5] 分析成交数据...")
163
+ deal_customers = [c for c in customers if c.get('dealStatus') == 1]
164
+ deal_intent = analyze_intent(deal_customers)
165
+ print(f" 本月成交: {len(deal_customers)}组")
166
+ print(" 成交意向等级:", deal_intent)
167
+
168
+ # 保存数据
169
+ report_data = {
170
+ 'date': today,
171
+ 'yesterday': yesterday,
172
+ 'project_name': PROJECT_NAME,
173
+ 'month_visit_target': MONTH_VISIT_TARGET,
174
+ 'month_visit_actual': len(month_visits),
175
+ 'yesterday_visits': len(yesterday_visits),
176
+ 'yesterday_visit_list': [{
177
+ 'time': v['visitTime'].split(' ')[1],
178
+ 'advisor': v['userName'],
179
+ 'count': v['visitCount'],
180
+ 'duration': int(int(v.get('visitTimer', '0')) / 60000) if v.get('visitTimer', '0') != '0' else 0
181
+ } for v in yesterday_visits],
182
+ 'month_sales_target': MONTH_SALES_TARGET,
183
+ 'month_deals': len(deal_customers),
184
+ 'visit_intent': intent_dist,
185
+ 'channel_dist': channel_dist,
186
+ 'stock_total': len(stock_customers),
187
+ 'deal_intent': deal_intent
188
+ }
189
+
190
+ output_path = os.path.join(os.getcwd(), "final_report_data.json")
191
+ with open(output_path, 'w', encoding='utf-8') as f:
192
+ json.dump(report_data, f, ensure_ascii=False, indent=2)
193
+
194
+ print(f"\n数据已保存: {output_path}")
195
+ return report_data
196
+
197
+
198
+ if __name__ == "__main__":
199
+ parser = argparse.ArgumentParser(description="旺小宝项目日报数据拉取")
200
+ parser.add_argument("date", nargs="?", default="2026-05-21", help="日期 YYYY-MM-DD")
201
+ parser.add_argument("--tenant-id", help="旺小宝租户ID(覆盖环境变量)")
202
+ parser.add_argument("--project-id", help="旺小宝项目ID(覆盖环境变量)")
203
+ parser.add_argument("--project-name", help="项目名称(覆盖环境变量)")
204
+ parser.add_argument("--month-visit-target", type=int, help="月度来访目标")
205
+ parser.add_argument("--month-sales-target", type=int, help="月度销售目标(万元)")
206
+ args = parser.parse_args()
207
+
208
+ if args.tenant_id:
209
+ TENANT_ID = args.tenant_id
210
+ if args.project_id:
211
+ PROJECT_ID = args.project_id
212
+ if args.project_name:
213
+ PROJECT_NAME = args.project_name
214
+ if args.month_visit_target is not None:
215
+ MONTH_VISIT_TARGET = args.month_visit_target
216
+ if args.month_sales_target is not None:
217
+ MONTH_SALES_TARGET = args.month_sales_target
218
+
219
+ main(args.date)