@pn-data/pn-data-analysis 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -1,13 +1,31 @@
1
- # @pn/pn-data-analysis
1
+ # PN中台 AI数据分析
2
2
 
3
3
  Cursor Agent Skill — 通过 pn_mid_v2 中台 API 查询项目的**流水、活跃、留存、来源**数据。
4
4
 
5
+ ## 环境变量配置
6
+
7
+ 使用前需配置认证密钥。
8
+
9
+ 密钥在**中台右上角 -> 生成数据分析密钥**中可以获取。
10
+
11
+ ### 必需变量
12
+
13
+ 密钥必须写入 shell 配置文件(zsh 用户写 `~/.zshrc`,bash 用户写 `~/.bashrc`),以确保所有终端会话和 AI 工具都能读取到:
14
+
15
+ ```bash
16
+ export PN_DATA_ANALYSIS_TOKEN="your-token-here" # 数据分析密钥
17
+ ```
18
+
19
+ 将上面内容写入对应的 shell 配置文件后执行 `source` 使其生效。
20
+
21
+ > **注意**:仅在当前终端 `export` 而不写入配置文件的话,新终端窗口或 AI Agent 启动的 shell 会话将无法读取到密钥。Skill 在每次会话首次使用时会自动检查配置文件中是否存在密钥,如果缺失会自动写入。
22
+
5
23
  ## 安装
6
24
 
7
25
  ```bash
8
26
  # 作为项目 Cursor Skill
9
- npm install @pn/pn-data-analysis
10
- # 将 node_modules/@pn/pn-data-analysis/pn-data-analysis/ 复制或链接到 .cursor/skills/
27
+ npm install @pn-data/pn-data-analysis
28
+ # 将 node_modules/@pn-data/pn-data-analysis/pn-data-analysis/ 复制或链接到 .cursor/skills/
11
29
  ```
12
30
 
13
31
  ## 使用方式
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pn-data/pn-data-analysis",
3
- "version": "0.0.1",
4
- "description": "Cursor Agent Skill for querying pn_mid_v2 project data (pay, activity, retention, source)",
3
+ "version": "0.0.3",
4
+ "description": "Cursor Agent Skill for querying pn_mid_v2 project data",
5
5
  "keywords": [
6
6
  "cursor",
7
7
  "cursor-skill",
@@ -3,6 +3,18 @@ name: pn-data-analysis
3
3
  description: Analyzes project data on the middle platform (pn_mid_v2). Use when querying 收入, 流水, 商店流水, 活跃, 留存, 来源, project revenue, activity, retention, or source data by project and month.
4
4
  ---
5
5
 
6
+ > Skill 版本:v0.0.3
7
+
8
+ # Skill 更新
9
+
10
+ ```bash
11
+ npm view @pn-data/pn-data-analysis version --registry https://registry.npmjs.org/
12
+ ```
13
+
14
+ 与本文件顶部 `Skill 版本` 对比,不同则更新本 Skill
15
+
16
+ 更新后**重新读取** `<SKILL_DIR>/SKILL.md`,以新版本为准。告知用户"中台数据分析 Skill 已更新到 vX.X.X"。
17
+
6
18
  # 中台项目数据分析
7
19
 
8
20
  通过 `pn_query.py` 远程查询 pn_mid_v2 中台各项目数据,支持**流水、活跃、留存、来源**四类查询。
@@ -13,19 +25,55 @@ description: Analyzes project data on the middle platform (pn_mid_v2). Use when
13
25
  - **关键词**:用pn、用中台
14
26
  - **数据类型词**:收入、流水、商店流水、活跃、留存、来源
15
27
 
16
- ## 项目名称解析
28
+ ## 执行顺序:先取 listProject 学习 id 与名称
17
29
 
18
- - 项目 ID 直接使用(如「2023项目」→ pid=2023)
19
- - 「项目10」→ pid=2023
30
+ **在跑任何按项目查询(pay/activity/retention/source)之前**,先取一次项目列表,建立「项目 id 名称」对应关系:
31
+
32
+ 1. **先执行**:`python3 <SKILL_DIR>/pn_query.py projects`(或 `--json` 取原始列表)
33
+ 2. **从返回中学习**:每条为 `id | name | tag`,记下 id、name、tag 的对应关系
34
+ 3. **再解析用户请求**:用户提到「项目名」「项目X」「tag」或数字 id 时,用该列表解析出**真实的项目 id (pid)**,再调用 `pn_query.py <pid> <dataType> <YYYY-MM>`
35
+
36
+ 未先取 listProject 时,不要猜测 pid;名称/别称与 id 的对应关系以当次 listProject 结果为准。
37
+
38
+ ## 项目名称解析(依赖上面 listProject 结果)
39
+
40
+ - 用户说**数字 id**(如 2023、2088)→ 直接作为 pid
41
+ - 用户说**项目名称或 tag** → 用已获取的项目列表按 name/tag 匹配,取对应 id 作为 pid
42
+ - 仅当列表中存在明确对应时再发起数据查询;无法匹配时提示用户说明项目 id 或名称
43
+
44
+ ---
45
+
46
+ ## 执行前:检查 PN_DATA_ANALYSIS_TOKEN
47
+
48
+ **每次执行查询前**必须确保环境变量 `PN_DATA_ANALYSIS_TOKEN` 已存在:
49
+
50
+ 1. **先检查**:`printenv PN_DATA_ANALYSIS_TOKEN` 或 `echo $PN_DATA_ANALYSIS_TOKEN`
51
+ 2. **若为空或未设置**:先执行 `source` 加载用户 shell 配置
52
+ 3. **再执行**下面的查询命令
53
+
54
+ 密钥需用户在中台右上角 -> 生成数据分析密钥 获取并写入 `~/.zshrc` 或 `~/.bashrc`:`export PN_DATA_ANALYSIS_TOKEN="..."`
20
55
 
21
56
  ---
22
57
 
23
58
  ## 查询命令
24
59
 
25
- 脚本位置:`.cursor/skills/pn-data-analysis/pn_query.py`
60
+ 脚本位置:`<SKILL_DIR>/pn_query.py`
61
+
62
+ ### 获取项目列表(id、name、tag)
63
+
64
+ 无需 pid/月份,直接获取当前密钥对应用户可访问的项目列表,用于学习 **id、name、tag** 的对应关系:
26
65
 
27
66
  ```bash
28
- python3 .cursor/skills/pn-data-analysis/pn_query.py <pid> <dataType> <YYYY-MM> [选项]
67
+ python3 <SKILL_DIR>/pn_query.py projects
68
+ python3 <SKILL_DIR>/pn_query.py projects --json
69
+ ```
70
+
71
+ 输出格式:`id | name | tag`,每行一个项目。
72
+
73
+ ### 按项目与月份查询数据
74
+
75
+ ```bash
76
+ python3 <SKILL_DIR>/pn_query.py <pid> <dataType> <YYYY-MM> [选项]
29
77
  ```
30
78
 
31
79
  ### 四类数据
@@ -59,23 +107,26 @@ python3 .cursor/skills/pn-data-analysis/pn_query.py <pid> <dataType> <YYYY-MM> [
59
107
  ## 示例
60
108
 
61
109
  ```bash
110
+ # 获取项目列表
111
+ python3 <SKILL_DIR>/pn_query.py projects
112
+
62
113
  # 流水
63
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 pay 2026-01
114
+ python3 <SKILL_DIR>/pn_query.py 2023 pay 2026-01
64
115
 
65
116
  # 活跃(全平台)
66
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 activity 2026-01
117
+ python3 <SKILL_DIR>/pn_query.py 2023 activity 2026-01
67
118
 
68
119
  # 留存(全平台)
69
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 retention 2026-01
120
+ python3 <SKILL_DIR>/pn_query.py 2023 retention 2026-01
70
121
 
71
122
  # 来源
72
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 source 2026-01
123
+ python3 <SKILL_DIR>/pn_query.py 2023 source 2026-01
73
124
 
74
125
  # 仅 iOS 活跃
75
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 activity 2026-01 --appPlatform=1
126
+ python3 <SKILL_DIR>/pn_query.py 2023 activity 2026-01 --appPlatform=1
76
127
 
77
128
  # 原始 JSON
78
- python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 retention 2026-01 --json
129
+ python3 <SKILL_DIR>/pn_query.py 2023 retention 2026-01 --json
79
130
  ```
80
131
 
81
132
  ## 默认行为
@@ -2,9 +2,10 @@
2
2
  """
3
3
  pn_query.py - 远程查询 pn_mid_v2 中台数据
4
4
 
5
- 通过 HTTP API 调用 pnDataAnalysis 接口,查询项目的流水、活跃、留存、来源数据。
5
+ 通过 HTTP API 调用 pnDataAnalysis 接口,查询项目的流水、活跃、留存、来源数据,或获取项目列表。
6
6
 
7
7
  用法:
8
+ python3 pn_query.py projects # 获取项目列表
8
9
  python3 pn_query.py <pid> <dataType> <YYYY-MM> [选项]
9
10
  python3 pn_query.py 2023 pay 2026-01
10
11
  python3 pn_query.py 2023 activity 2026-01
@@ -14,20 +15,66 @@ pn_query.py - 远程查询 pn_mid_v2 中台数据
14
15
  选项:
15
16
  --appPlatform=N 0=全平台(默认) 1=iOS 2=Android
16
17
  --group=N 分组类型,默认2(日期)。留存默认8(账号)
18
+ --json 输出原始 JSON
17
19
  """
18
20
 
19
21
  import argparse
20
22
  import json
23
+ import os
21
24
  import sys
22
25
  import urllib.request
23
26
  from calendar import monthrange
24
27
  from datetime import datetime
25
28
 
26
- API_BASE = "https://pnv2.17995api.net"
29
+ # API_BASE = "https://pnv2.17995api.net"
30
+ API_BASE = "http://localhost:811" # 本地测试
27
31
  API_PATH = "/service/pn_data_analysis"
32
+ ENV_TOKEN = "PN_DATA_ANALYSIS_TOKEN"
33
+
34
+
35
+ def list_projects() -> list:
36
+ """获取当前用户可访问的项目列表。"""
37
+ token = os.environ.get(ENV_TOKEN)
38
+ if not token:
39
+ raise SystemExit(
40
+ f"未配置 {ENV_TOKEN}。请在中台右上角 -> 生成数据分析密钥 获取后写入 shell 配置(如 ~/.zshrc)并 source 生效。"
41
+ )
42
+ url = f"{API_BASE}{API_PATH}"
43
+ payload = {"dataType": "listProject"}
44
+ headers = {
45
+ "Content-Type": "application/json;charset=UTF-8",
46
+ "X-Pn-Token": token,
47
+ }
48
+ req = urllib.request.Request(
49
+ url,
50
+ data=json.dumps(payload).encode("utf-8"),
51
+ headers=headers,
52
+ method="POST",
53
+ )
54
+ with urllib.request.urlopen(req, timeout=30) as resp:
55
+ out = json.loads(resp.read().decode("utf-8"))
56
+ return out if isinstance(out, list) else out.get("list", out)
57
+
58
+
59
+ def format_projects(data: list) -> str:
60
+ if not data:
61
+ return "无项目数据"
62
+ lines = ["项目列表 (id | name | tag):"]
63
+ for p in data:
64
+ pid = p.get("id", "")
65
+ name = p.get("name", "")
66
+ tag = p.get("tag", "")
67
+ lines.append(f" {pid} | {name} | {tag}")
68
+ return "\n".join(lines)
28
69
 
29
70
 
30
71
  def query(pid: int, data_type: str, month: str, app_platform: int = 0, group: int = 2) -> dict:
72
+ token = os.environ.get(ENV_TOKEN)
73
+ if not token:
74
+ raise SystemExit(
75
+ f"未配置 {ENV_TOKEN}。请在中台右上角 -> 生成数据分析密钥 获取后写入 shell 配置(如 ~/.zshrc)并 source 生效。"
76
+ )
77
+
31
78
  st_date = datetime.strptime(f"{month}-01", "%Y-%m-%d")
32
79
  y, m = st_date.year, st_date.month
33
80
  _, last = monthrange(y, m)
@@ -43,7 +90,10 @@ def query(pid: int, data_type: str, month: str, app_platform: int = 0, group: in
43
90
  "useCache": True,
44
91
  }
45
92
  url = f"{API_BASE}{API_PATH}"
46
- headers = {"Content-Type": "application/json;charset=UTF-8"}
93
+ headers = {
94
+ "Content-Type": "application/json;charset=UTF-8",
95
+ "X-Pn-Token": token,
96
+ }
47
97
  req = urllib.request.Request(
48
98
  url,
49
99
  data=json.dumps(payload).encode("utf-8"),
@@ -114,19 +164,36 @@ def format_source(data: list) -> str:
114
164
 
115
165
  def main():
116
166
  parser = argparse.ArgumentParser(description="pn_mid_v2 远程数据查询")
117
- parser.add_argument("pid", type=int, help="项目 ID")
118
- parser.add_argument("dataType", choices=["pay", "activity", "retention", "source"], help="数据类型")
119
- parser.add_argument("month", help="月份 YYYY-MM")
167
+ parser.add_argument("pid", nargs="?", help="项目 ID,或写 projects 获取项目列表")
168
+ parser.add_argument("dataType", nargs="?", choices=["pay", "activity", "retention", "source"], help="数据类型")
169
+ parser.add_argument("month", nargs="?", help="月份 YYYY-MM")
120
170
  parser.add_argument("--appPlatform", type=int, default=0, help="0=全平台 1=iOS 2=Android (默认0)")
121
171
  parser.add_argument("--group", type=int, default=None, help="分组类型,默认2(日期),留存默认8(账号)")
122
172
  parser.add_argument("--json", action="store_true", help="输出原始 JSON")
123
173
  args = parser.parse_args()
124
174
 
175
+ # 获取项目列表
176
+ if (args.pid or "").lower() in ("projects", "listproject", "list"):
177
+ try:
178
+ result = list_projects()
179
+ except Exception as e:
180
+ print(f"查询失败: {e}", file=sys.stderr)
181
+ sys.exit(1)
182
+ if args.json:
183
+ print(json.dumps(result, ensure_ascii=False, indent=2))
184
+ else:
185
+ print(format_projects(result))
186
+ return
187
+
188
+ if args.pid is None or args.dataType is None or args.month is None:
189
+ parser.error("查询数据时需指定 pid dataType month,或使用 pid=projects 获取项目列表")
190
+
191
+ pid = int(args.pid)
125
192
  if args.group is None:
126
193
  args.group = 8 if args.dataType == "retention" else 2
127
194
 
128
195
  try:
129
- result = query(args.pid, args.dataType, args.month, args.appPlatform, args.group)
196
+ result = query(pid, args.dataType, args.month, args.appPlatform, args.group)
130
197
  except Exception as e:
131
198
  print(f"查询失败: {e}", file=sys.stderr)
132
199
  sys.exit(1)