@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.
- package/dist/{api-client-DWbwtQbl.mjs → api-client-CDNjkArS.mjs} +2 -2
- package/dist/{api-JZnABA4y.mjs → api-fsHHWWa3.mjs} +5 -4
- package/dist/{audio-BHWH6uhw.mjs → audio-CRSMqaKo.mjs} +2 -2
- package/dist/{auth-BpzWivxf.mjs → auth-_3hBh87U.mjs} +3 -3
- package/dist/cli.mjs +10 -10
- package/dist/{consultant-kV5r8Wj8.mjs → consultant-E33hTGUO.mjs} +1 -1
- package/dist/{customer-B2OV8BTR.mjs → customer-DcQ83oKD.mjs} +1 -1
- package/dist/{focus-BjWD5PRV.mjs → focus-XMRxnHPw.mjs} +1 -1
- package/dist/{list-C30zFN6h.mjs → list-B3nN4pd2.mjs} +1 -1
- package/dist/{list-CHububT5.mjs → list-CXquxzex.mjs} +1 -1
- package/dist/{list-FM3vkQJa.mjs → list-Cc4zcaNz.mjs} +1 -1
- package/dist/{list-VpZkzTEE.mjs → list-CnlQdzPp.mjs} +1 -1
- package/dist/{list-Bw8dFf8a.mjs → list-CrMOrnS-.mjs} +1 -1
- package/dist/{list-h1nuSJiN.mjs → list-DFdDcHFq.mjs} +1 -1
- package/dist/{list-CVMSGK08.mjs → list-dm9YaCG1.mjs} +1 -1
- package/dist/login-CUUcjWPd.mjs +156 -0
- package/dist/{logout-DVJX8_x6.mjs → logout-DP7REhCd.mjs} +2 -1
- package/dist/{project-CJkTgGOq.mjs → project-BD0g4oAW.mjs} +1 -1
- package/dist/{qa-CQDRFw-g.mjs → qa-CdMcxCE_.mjs} +1 -1
- package/dist/{resistance-Ss-eUZoN.mjs → resistance-6_V6At3T.mjs} +1 -1
- package/dist/{text-5q8lIWi8.mjs → text-C3Ap6L5n.mjs} +1 -1
- package/dist/{token-store-D2umpIk-.mjs → token-store-CXizLE2_.mjs} +37 -1
- package/dist/{visit-CUI2ov-H.mjs → visit-DoeECi8w.mjs} +1 -1
- package/dist/{whoami-Uv7GBtGB.mjs → whoami-z-76WaCP.mjs} +2 -2
- package/package.json +5 -1
- package/skills/wangxiaobao-audio-query/SKILL.md +2 -2
- package/skills/wangxiaobao-audio-wiki/SKILL.md +4 -4
- package/skills/wangxiaobao-customer-focus-query/SKILL.md +2 -2
- package/skills/wangxiaobao-customer-query/SKILL.md +2 -2
- package/skills/wangxiaobao-customer-resistance-query/SKILL.md +2 -2
- package/skills/wangxiaobao-html-to-pdf/SKILL.md +116 -0
- package/skills/wangxiaobao-html-to-pdf/scripts/html2pdf.py +132 -0
- package/skills/wangxiaobao-html-to-pdf/scripts/html2pdf_exact.py +92 -0
- package/skills/wangxiaobao-project-daily-report/SKILL.md +170 -0
- package/skills/wangxiaobao-project-daily-report/_meta.json +9 -0
- package/skills/wangxiaobao-project-daily-report/references/report_template.html +308 -0
- package/skills/wangxiaobao-project-daily-report/scripts/fetch_data.py +219 -0
- package/skills/wangxiaobao-project-daily-report/scripts/generate_report.py +1029 -0
- package/skills/wangxiaobao-project-daily-report/scripts/run.py +34 -0
- package/skills/wangxiaobao-project-weekly-report/SKILL.md +169 -0
- package/skills/wangxiaobao-project-weekly-report/scripts/generate_report.py +308 -0
- package/skills/wangxiaobao-project-weekly-report/scripts/html2pdf_fast.py +161 -0
- package/skills/wangxiaobao-project-weekly-report/scripts/logo_wangai.png +0 -0
- package/skills/wangxiaobao-project-weekly-report/scripts/logo_wangxiaobao.png +0 -0
- package/skills/wangxiaobao-project-weekly-report/scripts/prepare_data.py +682 -0
- package/skills/wangxiaobao-project-weekly-report/scripts/template.py +925 -0
- package/skills/wangxiaobao-quick-qa/SKILL.md +2 -2
- package/skills/wangxiaobao-shared/SKILL.md +40 -7
- package/skills/wangxiaobao-switch-project/SKILL.md +3 -3
- package/skills/wangxiaobao-visit-query/SKILL.md +2 -2
- 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)
|