@pn-data/pn-data-analysis 0.0.1

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 ADDED
@@ -0,0 +1,71 @@
1
+ # @pn/pn-data-analysis
2
+
3
+ Cursor Agent Skill — 通过 pn_mid_v2 中台 API 查询项目的**流水、活跃、留存、来源**数据。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ # 作为项目 Cursor Skill
9
+ npm install @pn/pn-data-analysis
10
+ # 将 node_modules/@pn/pn-data-analysis/pn-data-analysis/ 复制或链接到 .cursor/skills/
11
+ ```
12
+
13
+ ## 使用方式
14
+
15
+ ### 在 Cursor 中触发
16
+
17
+ 安装后 Cursor Agent 会自动识别该 Skill,通过自然语言触发:
18
+
19
+ - 「用pn查 2088 2026-01 留存」
20
+ - 「用中台分析 2023 项目 2026 年 1 月流水」
21
+ - 「用pn查一下 2088 项目来源数据」
22
+
23
+ ### 命令行直接使用
24
+
25
+ ```bash
26
+ python3 pn-data-analysis/pn_query.py <pid> <dataType> <YYYY-MM> [选项]
27
+ ```
28
+
29
+ #### 数据类型
30
+
31
+ | dataType | 说明 |
32
+ |-----------|------|
33
+ | pay | 流水、商店流水、收入 |
34
+ | activity | 活跃(新增、r1/r7/r30) |
35
+ | retention | 留存(新增、r1~r90) |
36
+ | source | 来源(新增设备、转化率) |
37
+
38
+ #### 选项
39
+
40
+ | 选项 | 说明 | 默认 |
41
+ |------|------|------|
42
+ | `--appPlatform=N` | 0=全平台, 1=iOS, 2=Android | 0 |
43
+ | `--group=N` | 分组:2=日期, 3=系统, 5=地区, 8=账号 | 2(留存为8) |
44
+ | `--json` | 输出原始 JSON | 否 |
45
+
46
+ #### 示例
47
+
48
+ ```bash
49
+ # 流水
50
+ python3 pn-data-analysis/pn_query.py 2023 pay 2026-01
51
+
52
+ # 留存
53
+ python3 pn-data-analysis/pn_query.py 2088 retention 2026-01
54
+
55
+ # iOS 活跃
56
+ python3 pn-data-analysis/pn_query.py 2023 activity 2026-01 --appPlatform=1
57
+
58
+ # 来源(按地区分组)
59
+ python3 pn-data-analysis/pn_query.py 2023 source 2026-01 --group=5
60
+
61
+ # 原始 JSON
62
+ python3 pn-data-analysis/pn_query.py 2023 retention 2026-01 --json
63
+ ```
64
+
65
+ ## 依赖
66
+
67
+ - Python 3.6+(仅使用标准库,无第三方依赖)
68
+
69
+ ## License
70
+
71
+ MIT
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
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)",
5
+ "keywords": [
6
+ "cursor",
7
+ "cursor-skill",
8
+ "data-analysis"
9
+ ],
10
+ "author": "pn",
11
+ "license": "MIT",
12
+ "files": [
13
+ "pn-data-analysis/SKILL.md",
14
+ "pn-data-analysis/pn_query.py",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "query": "python3 pn-data-analysis/pn_query.py"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": ""
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ }
27
+ }
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: pn-data-analysis
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
+ ---
5
+
6
+ # 中台项目数据分析
7
+
8
+ 通过 `pn_query.py` 远程查询 pn_mid_v2 中台各项目数据,支持**流水、活跃、留存、来源**四类查询。
9
+
10
+ ## 触发方式
11
+
12
+ - **关键词 + 项目 + 日期 + 数据类型**:如「用pn查2023项目2026年1月流水」「用中台分析项目10的活跃」「用pn查一下XX项目来源数据」
13
+ - **关键词**:用pn、用中台
14
+ - **数据类型词**:收入、流水、商店流水、活跃、留存、来源
15
+
16
+ ## 项目名称解析
17
+
18
+ - 项目 ID 直接使用(如「2023项目」→ pid=2023)
19
+ - 「项目10」→ pid=2023
20
+
21
+ ---
22
+
23
+ ## 查询命令
24
+
25
+ 脚本位置:`.cursor/skills/pn-data-analysis/pn_query.py`
26
+
27
+ ```bash
28
+ python3 .cursor/skills/pn-data-analysis/pn_query.py <pid> <dataType> <YYYY-MM> [选项]
29
+ ```
30
+
31
+ ### 四类数据
32
+
33
+ | dataType | 说明 | 默认 group |
34
+ |----------|------|-----------|
35
+ | pay | 付费 (流水、商店流水、收入) | 2 (日期) |
36
+ | activity | 活跃 (新增、r1/r7/r30) | 2 (日期) |
37
+ | retention | 留存 (新增、r1/r7/r30) | 8 (账号) |
38
+ | source | 来源 (新增设备、转化、转化率) | 2 (日期) |
39
+
40
+ ### 选项
41
+
42
+ | 选项 | 说明 | 默认 |
43
+ |------|------|------|
44
+ | `--appPlatform=N` | 0=全平台, 1=iOS, 2=Android | 0 |
45
+ | `--group=N` | 分组类型 | 2(日期)/8(留存) |
46
+ | `--json` | 输出原始 JSON | 否 |
47
+
48
+ ### 分组类型 (group)
49
+
50
+ | 值 | 说明 |
51
+ |----|------|
52
+ | 2 | 按日期 |
53
+ | 3 | 按系统 |
54
+ | 5 | 按地区 |
55
+ | 8 | 按账号(留存默认) |
56
+
57
+ ---
58
+
59
+ ## 示例
60
+
61
+ ```bash
62
+ # 流水
63
+ python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 pay 2026-01
64
+
65
+ # 活跃(全平台)
66
+ python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 activity 2026-01
67
+
68
+ # 留存(全平台)
69
+ python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 retention 2026-01
70
+
71
+ # 来源
72
+ python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 source 2026-01
73
+
74
+ # 仅 iOS 活跃
75
+ python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 activity 2026-01 --appPlatform=1
76
+
77
+ # 原始 JSON
78
+ python3 .cursor/skills/pn-data-analysis/pn_query.py 2023 retention 2026-01 --json
79
+ ```
80
+
81
+ ## 默认行为
82
+
83
+ - **全平台**(appPlatform=0)
84
+ - **按日期分组**(group=2),留存按账号(group=8)
85
+ - **取总计**(key=total 行数据)
86
+
87
+ ## API 信息
88
+
89
+ - 服务器:`https://pnv2.17995api.net`
90
+ - 接口路径:`/service/pn_data_analysis`
91
+ - 后端方法:`DataManager::pnDataAnalysis`
92
+ - API 编号:3036(`Api::PN_DATA_ANALYSIS`)
93
+
94
+ ### 请求参数
95
+
96
+ ```json
97
+ {
98
+ "pid": 2023,
99
+ "dataType": "activity",
100
+ "timeSpan": [1735689600, 1738367999],
101
+ "appPlatform": 0,
102
+ "group": 2,
103
+ "useCache": true
104
+ }
105
+ ```
106
+
107
+ ### 返回结构
108
+
109
+ - **pay**:`list` 数组,每项含 `method`、`pay_ta_usd`、`pay_store_usd`、`income_ta_usd` 等
110
+ - **activity**:`listData` 数组,key=total 行含 `new`、`r1`~`r30`
111
+ - **retention**:`listData` 数组,key=total 行含 `new`、`r1`~`r90`
112
+ - **source**:`listData` 数组,各行含 `install`/`new`、`login`/`luu`
113
+
114
+ ## 流水核心字段
115
+
116
+ | 字段 | 说明 |
117
+ |------|------|
118
+ | pay_ta_usd/cny | 流水 |
119
+ | pay_store_usd/cny | 商店流水 |
120
+ | income_ta_usd/cny | 收入 |
121
+ | income_store_usd/cny | 商店收入 |
122
+ | share_ta_usd/cny | 分成 |
123
+ | profit_ta_usd/cny | 利润 |
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ pn_query.py - 远程查询 pn_mid_v2 中台数据
4
+
5
+ 通过 HTTP API 调用 pnDataAnalysis 接口,查询项目的流水、活跃、留存、来源数据。
6
+
7
+ 用法:
8
+ python3 pn_query.py <pid> <dataType> <YYYY-MM> [选项]
9
+ python3 pn_query.py 2023 pay 2026-01
10
+ python3 pn_query.py 2023 activity 2026-01
11
+ python3 pn_query.py 2023 retention 2026-01
12
+ python3 pn_query.py 2023 source 2026-01 --group=5
13
+
14
+ 选项:
15
+ --appPlatform=N 0=全平台(默认) 1=iOS 2=Android
16
+ --group=N 分组类型,默认2(日期)。留存默认8(账号)
17
+ """
18
+
19
+ import argparse
20
+ import json
21
+ import sys
22
+ import urllib.request
23
+ from calendar import monthrange
24
+ from datetime import datetime
25
+
26
+ API_BASE = "https://pnv2.17995api.net"
27
+ API_PATH = "/service/pn_data_analysis"
28
+
29
+
30
+ def query(pid: int, data_type: str, month: str, app_platform: int = 0, group: int = 2) -> dict:
31
+ st_date = datetime.strptime(f"{month}-01", "%Y-%m-%d")
32
+ y, m = st_date.year, st_date.month
33
+ _, last = monthrange(y, m)
34
+ st = int(datetime(y, m, 1).timestamp())
35
+ et = int(datetime(y, m, last, 23, 59, 59).timestamp())
36
+
37
+ payload = {
38
+ "pid": pid,
39
+ "dataType": data_type,
40
+ "timeSpan": [st, et],
41
+ "appPlatform": app_platform,
42
+ "group": group,
43
+ "useCache": True,
44
+ }
45
+ url = f"{API_BASE}{API_PATH}"
46
+ headers = {"Content-Type": "application/json;charset=UTF-8"}
47
+ req = urllib.request.Request(
48
+ url,
49
+ data=json.dumps(payload).encode("utf-8"),
50
+ headers=headers,
51
+ method="POST",
52
+ )
53
+ with urllib.request.urlopen(req, timeout=60) as resp:
54
+ return json.loads(resp.read().decode("utf-8"))
55
+
56
+
57
+ def format_pay(data: list) -> str:
58
+ if not data:
59
+ return "无流水数据"
60
+ total = next((d for d in data if d.get("method") == "total"), data[0] if data else {})
61
+ lines = ["流水数据:"]
62
+ for k, label in [
63
+ ("pay_ta_usd", "流水(USD)"),
64
+ ("pay_ta_cny", "流水(CNY)"),
65
+ ("pay_store_usd", "商店流水(USD)"),
66
+ ("pay_store_cny", "商店流水(CNY)"),
67
+ ("income_ta_usd", "收入(USD)"),
68
+ ("income_ta_cny", "收入(CNY)"),
69
+ ("income_store_usd", "商店收入(USD)"),
70
+ ("income_store_cny", "商店收入(CNY)"),
71
+ ]:
72
+ if k in total:
73
+ lines.append(f" {label}: {total[k]:,.2f}")
74
+ return "\n".join(lines)
75
+
76
+
77
+ def format_activity(data: list) -> str:
78
+ if not data:
79
+ return "无活跃数据"
80
+ total = next((d for d in data if d.get("key") == "total"), data[0])
81
+ new = total.get("new", 0)
82
+ lines = [f"活跃数据:", f" 新增用户 new: {new:,}"]
83
+ for d in [1, 7, 30]:
84
+ k = f"r{d}"
85
+ if k in total:
86
+ lines.append(f" +{d}日留存 {k}: {total[k] * 100:.2f}%")
87
+ return "\n".join(lines)
88
+
89
+
90
+ def format_retention(data: list) -> str:
91
+ if not data:
92
+ return "无留存数据"
93
+ total = next((d for d in data if d.get("key") == "total"), data[0])
94
+ new = total.get("new", 0)
95
+ lines = [f"留存数据:", f" 新增用户 new: {new:,}"]
96
+ for d in [1, 7, 30]:
97
+ k = f"r{d}"
98
+ if k in total:
99
+ lines.append(f" +{d}日留存 {k}: {total[k] * 100:.2f}%")
100
+ return "\n".join(lines)
101
+
102
+
103
+ def format_source(data: list) -> str:
104
+ if not data:
105
+ return "无来源数据"
106
+ total = next((d for d in data if d.get("key") == "total"), None)
107
+ if total:
108
+ install = total.get("install", total.get("new", 0))
109
+ login = total.get("login", total.get("luu", 0))
110
+ rate = f"{login / install * 100:.2f}%" if install > 0 else "-"
111
+ return f"来源数据:\n 新增设备: {install:,}\n 转化(login): {login:,}\n 转化率: {rate}"
112
+ return json.dumps(data, ensure_ascii=False, indent=2)
113
+
114
+
115
+ def main():
116
+ 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")
120
+ parser.add_argument("--appPlatform", type=int, default=0, help="0=全平台 1=iOS 2=Android (默认0)")
121
+ parser.add_argument("--group", type=int, default=None, help="分组类型,默认2(日期),留存默认8(账号)")
122
+ parser.add_argument("--json", action="store_true", help="输出原始 JSON")
123
+ args = parser.parse_args()
124
+
125
+ if args.group is None:
126
+ args.group = 8 if args.dataType == "retention" else 2
127
+
128
+ try:
129
+ result = query(args.pid, args.dataType, args.month, args.appPlatform, args.group)
130
+ except Exception as e:
131
+ print(f"查询失败: {e}", file=sys.stderr)
132
+ sys.exit(1)
133
+
134
+ if args.json:
135
+ print(json.dumps(result, ensure_ascii=False, indent=2))
136
+ return
137
+
138
+ data = result if isinstance(result, list) else result.get("listData", result.get("list", []))
139
+
140
+ formatters = {
141
+ "pay": format_pay,
142
+ "activity": format_activity,
143
+ "retention": format_retention,
144
+ "source": format_source,
145
+ }
146
+ print(formatters[args.dataType](data))
147
+
148
+
149
+ if __name__ == "__main__":
150
+ main()