siluzan-tso-cli 1.1.19-beta.3 → 1.1.19-beta.5

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
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
- > **注意**:当前为测试版(1.1.19-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.19-beta.5),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -4738,10 +4738,23 @@ var init_sections = __esm({
4738
4738
  },
4739
4739
  {
4740
4740
  name: "geographic",
4741
- description: "\u56FD\u5BB6/\u5730\u533A\u5206\u6BB5 GeographicSectionData",
4741
+ description: "\u56FD\u5BB6/\u5730\u533A\u5206\u6BB5 GeographicSectionData\uFF08\u8D26\u6237\u7EA7\u805A\u5408\uFF09",
4742
4742
  dateMode: "range",
4743
4743
  path: (id) => `/reporting/media-account/${id}/GeographicSectionData`
4744
4744
  },
4745
+ {
4746
+ name: "campaign-geo",
4747
+ description: "\u5E7F\u544A\u7CFB\u5217\u7EF4\u5EA6\u5730\u7406 GET \u2026/campaigns/reports/geo\uFF08GetCampaignGeoReport\uFF1B\u884C\u53EF\u542B campaign/adGroup\uFF09",
4748
+ dateMode: "range",
4749
+ path: (id) => `/reporting/media-account/${id}/campaigns/reports/geo`,
4750
+ campaignGeoFilterOption: true
4751
+ },
4752
+ {
4753
+ name: "campaign-device",
4754
+ description: "\u5E7F\u544A\u7CFB\u5217\u7EF4\u5EA6\u8BBE\u5907 GET \u2026/campaigns/reports/device\uFF08GetCampaignDeviceReport\uFF1B\u884C\u53EF\u542B campaign/adGroup\uFF09",
4755
+ dateMode: "range",
4756
+ path: (id) => `/reporting/media-account/${id}/campaigns/reports/device`
4757
+ },
4745
4758
  {
4746
4759
  name: "audience",
4747
4760
  description: "\u53D7\u4F17\u5206\u6BB5 AdGroupAudienceData",
@@ -4885,6 +4898,10 @@ async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose, name) {
4885
4898
  return fetchJson(config, fullPath, verbose);
4886
4899
  case "geographic":
4887
4900
  return fetchJson(config, fullPath, verbose);
4901
+ case "campaign-geo":
4902
+ return fetchJson(config, fullPath, verbose);
4903
+ case "campaign-device":
4904
+ return fetchJson(config, fullPath, verbose);
4888
4905
  case "audience":
4889
4906
  return fetchJson(config, fullPath, verbose);
4890
4907
  case "asset-images":
@@ -4935,6 +4952,12 @@ async function fetchSectionPayload(def, opts, config, id) {
4935
4952
  if (def.audienceFilterOption && opts.audienceType) {
4936
4953
  extras.audienceTypeFilter = opts.audienceType;
4937
4954
  }
4955
+ if (def.campaignGeoFilterOption) {
4956
+ const n = (x) => typeof x === "number" && Number.isFinite(x);
4957
+ if (n(opts.costGreater)) extras.costGreater = opts.costGreater;
4958
+ if (n(opts.clickGreater)) extras.clickGreater = opts.clickGreater;
4959
+ if (n(opts.conversionsGreater)) extras.conversionsGreater = opts.conversionsGreater;
4960
+ }
4938
4961
  if (def.name === "materials") {
4939
4962
  const { startDate, endDate } = resolveDateRange2(opts.start, opts.end);
4940
4963
  const q = `?${new URLSearchParams({ startDate, endDate }).toString()}`;
@@ -4986,7 +5009,10 @@ async function fetchOneGoogleSection(opts) {
4986
5009
  limit: opts.limit,
4987
5010
  noOrderByCost: opts.noOrderByCost,
4988
5011
  level: opts.level,
4989
- audienceType: opts.audienceType
5012
+ audienceType: opts.audienceType,
5013
+ costGreater: opts.costGreater,
5014
+ clickGreater: opts.clickGreater,
5015
+ conversionsGreater: opts.conversionsGreater
4990
5016
  },
4991
5017
  opts.config,
4992
5018
  opts.accountId
@@ -5594,7 +5620,10 @@ async function runBatch(opts) {
5594
5620
  section: task.section,
5595
5621
  start: opts.start,
5596
5622
  end: opts.end,
5597
- limit: opts.keywordLimit
5623
+ limit: opts.keywordLimit,
5624
+ costGreater: opts.campaignGeoCostGreater,
5625
+ clickGreater: opts.campaignGeoClickGreater,
5626
+ conversionsGreater: opts.campaignGeoConversionsGreater
5598
5627
  }),
5599
5628
  {
5600
5629
  label: `${task.section}/${task.accountId}`,
@@ -5715,6 +5744,9 @@ async function persistRunMetadata(input) {
5715
5744
  },
5716
5745
  minSpend: input.minSpend,
5717
5746
  keywordLimit: input.keywordLimit,
5747
+ campaignGeoCostGreater: input.campaignGeoCostGreater,
5748
+ campaignGeoClickGreater: input.campaignGeoClickGreater,
5749
+ campaignGeoConversionsGreater: input.campaignGeoConversionsGreater,
5718
5750
  deadlineMs: input.deadlineMs,
5719
5751
  accountsTotal: input.accounts.length,
5720
5752
  invocation: input.invocation
@@ -5868,6 +5900,9 @@ function decideExitCode(state, tokenInvalidated) {
5868
5900
  if (state === "paused") return EXIT_PARTIAL;
5869
5901
  return EXIT_FAILED;
5870
5902
  }
5903
+ function finiteBatchOpt(n) {
5904
+ return typeof n === "number" && Number.isFinite(n) ? n : void 0;
5905
+ }
5871
5906
  async function resolveBatchAccounts(config, opts) {
5872
5907
  if (opts.explicitAccountIds && opts.explicitAccountIds.length > 0) {
5873
5908
  const accounts2 = opts.explicitAccountIds.map((id) => ({
@@ -5963,6 +5998,9 @@ async function executeBatchRun(input) {
5963
5998
  start: input.start,
5964
5999
  end: input.end,
5965
6000
  keywordLimit: input.keywordLimit,
6001
+ campaignGeoCostGreater: input.campaignGeoCostGreater,
6002
+ campaignGeoClickGreater: input.campaignGeoClickGreater,
6003
+ campaignGeoConversionsGreater: input.campaignGeoConversionsGreater,
5966
6004
  minSpend: input.minSpend,
5967
6005
  accountConcurrency: input.accountConcurrency,
5968
6006
  sectionConcurrency: input.sectionConcurrency,
@@ -6015,6 +6053,9 @@ async function executeBatchRun(input) {
6015
6053
  start: input.start,
6016
6054
  end: input.end,
6017
6055
  keywordLimit: input.keywordLimit,
6056
+ campaignGeoCostGreater: input.campaignGeoCostGreater,
6057
+ campaignGeoClickGreater: input.campaignGeoClickGreater,
6058
+ campaignGeoConversionsGreater: input.campaignGeoConversionsGreater,
6018
6059
  accountConcurrency: input.accountConcurrency,
6019
6060
  sectionConcurrency: input.sectionConcurrency,
6020
6061
  retry: input.retry,
@@ -6089,6 +6130,9 @@ async function cmdRun(opts) {
6089
6130
  accountConcurrency,
6090
6131
  sectionConcurrency,
6091
6132
  keywordLimit: opts.keywordLimit,
6133
+ campaignGeoCostGreater: finiteBatchOpt(opts.campaignGeoCostGreater),
6134
+ campaignGeoClickGreater: finiteBatchOpt(opts.campaignGeoClickGreater),
6135
+ campaignGeoConversionsGreater: finiteBatchOpt(opts.campaignGeoConversionsGreater),
6092
6136
  minSpend,
6093
6137
  deadlineMs,
6094
6138
  retry,
@@ -6161,6 +6205,9 @@ async function cmdResume(opts) {
6161
6205
  start: manifest.dateRange.start,
6162
6206
  end: manifest.dateRange.end,
6163
6207
  keywordLimit: manifest.keywordLimit,
6208
+ campaignGeoCostGreater: finiteBatchOpt(manifest.campaignGeoCostGreater),
6209
+ campaignGeoClickGreater: finiteBatchOpt(manifest.campaignGeoClickGreater),
6210
+ campaignGeoConversionsGreater: finiteBatchOpt(manifest.campaignGeoConversionsGreater),
6164
6211
  accountConcurrency: manifest.concurrency.account,
6165
6212
  sectionConcurrency: manifest.concurrency.section,
6166
6213
  retry: { ...DEFAULT_RETRY_POLICY, ...manifest.retry },
@@ -6264,6 +6311,18 @@ function registerGoogleAnalysisBatchCommands(program2) {
6264
6311
  "--keyword-limit <n>",
6265
6312
  "\u900F\u4F20\u7ED9 keywords / search-terms \u7684\u6761\u6570\u4E0A\u9650\uFF08\u63A7\u5236\u5927\u8D26\u6237\u54CD\u5E94\u4F53\u79EF\uFF09",
6266
6313
  (v) => parseInt(v, 10)
6314
+ ).option(
6315
+ "--campaign-geo-cost-greater <n>",
6316
+ "\u4EC5 campaign-geo\uFF1A\u7F51\u5173 costGreater \u8FC7\u6EE4\uFF08\u6574\u6570\uFF09",
6317
+ (v) => parseInt(v, 10)
6318
+ ).option(
6319
+ "--campaign-geo-click-greater <n>",
6320
+ "\u4EC5 campaign-geo\uFF1A\u7F51\u5173 clickGreater \u8FC7\u6EE4\uFF08\u6574\u6570\uFF09",
6321
+ (v) => parseInt(v, 10)
6322
+ ).option(
6323
+ "--campaign-geo-conversions-greater <n>",
6324
+ "\u4EC5 campaign-geo\uFF1A\u7F51\u5173 conversionsGreater \u8FC7\u6EE4\uFF08\u6574\u6570\uFF09",
6325
+ (v) => parseInt(v, 10)
6267
6326
  ).option(
6268
6327
  "--min-spend <n>",
6269
6328
  "\u533A\u95F4\u5185\u6D88\u8017 \u2264 \u6B64\u503C\u7684\u8D26\u6237\u8DF3\u8FC7\uFF1B0=\u4E0D\u8FC7\u6EE4\uFF08\u4E0E --accounts \u540C\u4F20\u65F6\u65E0\u6548\uFF09",
@@ -6349,7 +6408,7 @@ async function runAllSections(opts) {
6349
6408
  }
6350
6409
  if (!opts.jsonOut || !opts.jsonOut.trim()) {
6351
6410
  console.error(
6352
- "\n\u274C all \u6A21\u5F0F\u5FC5\u987B\u4F20 --json-out <\u76EE\u5F55>\uFF0821 \u4E2A\u7EF4\u5EA6\u7ED3\u679C\u843D\u76D8\u540E\u7531 manifest \u7D22\u5F15\uFF09\u3002\n"
6411
+ "\n\u274C all \u6A21\u5F0F\u5FC5\u987B\u4F20 --json-out <\u76EE\u5F55>\uFF0823 \u4E2A\u7EF4\u5EA6\u7ED3\u679C\u843D\u76D8\u540E\u7531 manifest \u7D22\u5F15\uFF09\u3002\n"
6353
6412
  );
6354
6413
  process.exit(1);
6355
6414
  }
@@ -6411,6 +6470,9 @@ async function runAllSections(opts) {
6411
6470
  console.log(JSON.stringify(summary));
6412
6471
  if (failed > 0) process.exit(2);
6413
6472
  }
6473
+ function finiteOpt(n) {
6474
+ return typeof n === "number" && Number.isFinite(n) ? n : void 0;
6475
+ }
6414
6476
  function parseAccountIdsForAnalysis(input) {
6415
6477
  const out = [];
6416
6478
  const seen = /* @__PURE__ */ new Set();
@@ -6451,6 +6513,9 @@ async function runMultiAccountViaBatch(accountIds, opts) {
6451
6513
  accountConcurrency,
6452
6514
  sectionConcurrency: concurrency,
6453
6515
  keywordLimit: opts.limit,
6516
+ campaignGeoCostGreater: finiteOpt(opts.costGreater),
6517
+ campaignGeoClickGreater: finiteOpt(opts.clickGreater),
6518
+ campaignGeoConversionsGreater: finiteOpt(opts.conversionsGreater),
6454
6519
  minSpend: 0,
6455
6520
  retry: DEFAULT_RETRY_POLICY,
6456
6521
  token: opts.token,
@@ -6500,7 +6565,7 @@ function registerGoogleAnalysisCommands(program2) {
6500
6565
  "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08range \u7C7B\u7EF4\u5EA6\u751F\u6548\uFF1B\u7701\u7565=\u8FD1 7 \u5929\u622A\u81F3\u6628\u5929\uFF1B\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\uFF09"
6501
6566
  ).option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
6502
6567
  "--sections <list>",
6503
- "\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,keywords,ads\uFF1B\u7701\u7565=\u5168\u90E8 21 \u4E2A"
6568
+ "\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,keywords,ads\uFF1B\u7701\u7565=\u5168\u90E8 23 \u4E2A"
6504
6569
  ).option(
6505
6570
  "--exclude <list>",
6506
6571
  "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 materials,gold-account\uFF1B\u4E0E --sections \u53EF\u53E0\u52A0"
@@ -6511,15 +6576,27 @@ function registerGoogleAnalysisCommands(program2) {
6511
6576
  ).option("--limit <n>", "\u900F\u4F20\u7ED9 keywords / search-terms \u7EF4\u5EA6\u7684\u6761\u6570\u4E0A\u9650", (v) => parseInt(v, 10)).option("--no-order-by-cost", "\u900F\u4F20\uFF1Akeywords / search-terms \u4E0D\u6309\u6D88\u8017\u6392\u5E8F", false).option("--level <level>", "\u900F\u4F20\u7ED9 extensions \u7EF4\u5EA6\u7684 level \u8FC7\u6EE4\uFF1AAccount | Campaign | Ad Group").option(
6512
6577
  "--audience-type <type>",
6513
6578
  "\u900F\u4F20\u7ED9 audience \u7EF4\u5EA6\u7684 audienceTypeFilter\uFF1ASystemDefined | UserDefined"
6579
+ ).option(
6580
+ "--cost-greater <n>",
6581
+ "\u4EC5 campaign-geo\uFF1A\u7F51\u5173\u53EF\u9009\u8FC7\u6EE4 costGreater\uFF08\u6574\u6570\uFF0C\u5355\u4F4D\u4EE5\u540E\u7AEF\u4E3A\u51C6\uFF09",
6582
+ (v) => parseInt(v, 10)
6583
+ ).option(
6584
+ "--click-greater <n>",
6585
+ "\u4EC5 campaign-geo\uFF1A\u7F51\u5173\u53EF\u9009\u8FC7\u6EE4 clickGreater\uFF08\u6574\u6570\uFF09",
6586
+ (v) => parseInt(v, 10)
6587
+ ).option(
6588
+ "--conversions-greater <n>",
6589
+ "\u4EC5 campaign-geo\uFF1A\u7F51\u5173\u53EF\u9009\u8FC7\u6EE4 conversionsGreater\uFF08\u6574\u6570\uFF09",
6590
+ (v) => parseInt(v, 10)
6514
6591
  ).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).addHelpText(
6515
6592
  "after",
6516
6593
  [
6517
6594
  "",
6518
- "\u53EF\u7528\u7EF4\u5EA6\uFF0821 \u4E2A\uFF0C\u4E0E --sections / --exclude \u53D6\u503C\u4E00\u81F4\uFF09\uFF1A",
6595
+ "\u53EF\u7528\u7EF4\u5EA6\uFF0823 \u4E2A\uFF0C\u4E0E --sections / --exclude \u53D6\u503C\u4E00\u81F4\uFF09\uFF1A",
6519
6596
  ...SECTIONS.map((s) => ` ${s.name.padEnd(20)} ${s.description}`),
6520
6597
  "",
6521
6598
  "\u793A\u4F8B\uFF1A",
6522
- " # \u5355\u8D26\u6237\uFF1A\u62C9\u5168\u90E8 21 \u4E2A\u7EF4\u5EA6",
6599
+ " # \u5355\u8D26\u6237\uFF1A\u62C9\u5168\u90E8 23 \u4E2A\u7EF4\u5EA6",
6523
6600
  " siluzan-tso google-analysis -a <mediaCustomerId> --json-out ./snap",
6524
6601
  "",
6525
6602
  " # \u5355\u8D26\u6237\uFF1A\u4EC5\u62C9\u5355\u4E2A\u7EF4\u5EA6\uFF08\u66FF\u4EE3\u5386\u53F2\u7684 google-analysis overview ...\uFF09",
@@ -6533,7 +6610,11 @@ function registerGoogleAnalysisCommands(program2) {
6533
6610
  " siluzan-tso google-analysis -a id1,id2,id3 --start 2026-04-01 --end 2026-04-30 \\",
6534
6611
  " --sections campaigns,geographic,keywords --json-out ./snap",
6535
6612
  "",
6536
- " # \u591A\u8D26\u6237\u573A\u666F\u5982\u9700 resume / \u81EA\u9002\u5E94\u9650\u6D41 / deadline\uFF0C\u8BF7\u76F4\u63A5\u7528 google-analysis-batch run -a ..."
6613
+ " # \u591A\u8D26\u6237\u573A\u666F\u5982\u9700 resume / \u81EA\u9002\u5E94\u9650\u6D41 / deadline\uFF0C\u8BF7\u76F4\u63A5\u7528 google-analysis-batch run -a ...",
6614
+ "",
6615
+ " # campaign-geo \u53EF\u9009\u9608\u503C\uFF08\u4E0E\u7F51\u5173 GetCampaignsGeoReport \u4E00\u81F4\uFF09",
6616
+ " siluzan-tso google-analysis -a <id> --sections campaign-geo --start <s> --end <e> \\",
6617
+ " --cost-greater 1000000 --json-out ./snap"
6537
6618
  ].join("\n")
6538
6619
  ).action(async (legacySection, opts) => {
6539
6620
  if (legacySection) {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.19-beta.3",
4
- "publishedAt": 1778568598630
3
+ "version": "1.1.19-beta.5",
4
+ "publishedAt": 1778576964597
5
5
  }
@@ -89,7 +89,7 @@
89
89
 
90
90
  ## Google 账户分析:`google-analysis` 命令
91
91
 
92
- > **重要**:`google-analysis` 是统一入口,所有 21 个维度都通过 `--sections` 选取。关键词维度用 `--sections keywords`,**不要**用 `ad keywords` 代替。涉及「今天/当天」消耗的口径见本文顶部「数据时效性」表。
92
+ > **重要**:`google-analysis` 是统一入口,所有 23 个维度都通过 `--sections` 选取。关键词维度用 `--sections keywords`,**不要**用 `ad keywords` 代替。涉及「今天/当天」消耗的口径见本文顶部「数据时效性」表。
93
93
 
94
94
  ```bash
95
95
  # 单维度
@@ -99,7 +99,7 @@ siluzan-tso google-analysis -a <id> --sections overview --json-out <dir>
99
99
  siluzan-tso google-analysis -a <id> --start YYYY-MM-DD --end YYYY-MM-DD \
100
100
  --sections overview,campaigns,devices,geographic,keywords,daily-metrics --json-out <dir>
101
101
 
102
- # 全量 21 个(省略 --sections)
102
+ # 全量 23 个(省略 --sections)
103
103
  siluzan-tso google-analysis -a <id> --json-out <dir>
104
104
 
105
105
  # 排除慢/不需要的维度
@@ -112,7 +112,7 @@ siluzan-tso google-analysis -a <id> --exclude materials,gold-account --json-out
112
112
  | ------------------------ | ------------------------------------------------------------------------------------ |
113
113
  | `-a, --account <id>` | Google `mediaCustomerId`(必填) |
114
114
  | `--json-out <dir>` | 必填;每维一个 `<section>-<accountId>.json` + `manifest-<accountId>.json` |
115
- | `--sections <list>` | 仅执行指定维度(逗号分隔);省略=全部 21 个 |
115
+ | `--sections <list>` | 仅执行指定维度(逗号分隔);省略=全部 23 个 |
116
116
  | `--exclude <list>` | 排除指定维度;与 `--sections` 可叠加 |
117
117
  | `--start` / `--end` | 统计区间(YYYY-MM-DD);省略=近 7 天截至昨天;`final-urls`/`campaign-types` 自动忽略 |
118
118
  | `--concurrency <n>` | 并发数,默认 5,上限 16 |
@@ -120,9 +120,12 @@ siluzan-tso google-analysis -a <id> --exclude materials,gold-account --json-out
120
120
  | `--level <lvl>` | 透传给 `extensions`(Account/Campaign/Ad Group) |
121
121
  | `--audience-type <type>` | 透传给 `audience`(SystemDefined/UserDefined) |
122
122
  | `--no-order-by-cost` | 透传给 `keywords`/`search-terms` |
123
+ | `--cost-greater <n>` | 仅 **`campaign-geo`**:网关 `costGreater`(整数,单位以后端为准) |
124
+ | `--click-greater <n>` | 仅 **`campaign-geo`**:网关 `clickGreater` |
125
+ | `--conversions-greater <n>` | 仅 **`campaign-geo`**:网关 `conversionsGreater` |
123
126
  | `--verbose` | 打印详细错误 |
124
127
 
125
- ### 维度列表(21 个)
128
+ ### 维度列表(23 个)
126
129
 
127
130
  | 维度 | 说明 |
128
131
  | -------------------- | --------------------------------------------------------------------------------------------------- |
@@ -133,8 +136,10 @@ siluzan-tso google-analysis -a <id> --exclude materials,gold-account --json-out
133
136
  | `campaign-hour` | 系列按小时(根为 JSON 数组) |
134
137
  | `ads` | 广告;与 `ad list` 同源 |
135
138
  | `extensions` | 附加信息;可选 `--level` |
136
- | `devices` | 设备分布 |
137
- | `geographic` | 地域分布 |
139
+ | `devices` | 设备分布(账户级 `DeviceSectionData`) |
140
+ | `geographic` | 地域分布(账户级 `GeographicSectionData`,网关侧常按国家聚合) |
141
+ | `campaign-geo` | 广告系列维度地理 (可选 `--cost-greater` / `--click-greater` / `--conversions-greater` 与网关一致) |
142
+ | `campaign-device` | 广告系列维度设备 (行可含系列/组) |
138
143
  | `audience` | 受众;可选 `--audience-type` |
139
144
  | `asset-images` | 图片素材 |
140
145
  | `videos` | 视频 |
@@ -156,8 +161,8 @@ siluzan-tso google-analysis -a <id> --exclude materials,gold-account --json-out
156
161
  "absoluteSnapshotDir": "/abs/path/to/snap-google",
157
162
  "manifestFile": "manifest-9526903813.json",
158
163
  "accountId": "9526903813",
159
- "totalSections": 21,
160
- "succeeded": 21,
164
+ "totalSections": 23,
165
+ "succeeded": 23,
161
166
  "failed": 0,
162
167
  "concurrency": 5,
163
168
  "elapsedMs": 18712,
@@ -73,6 +73,7 @@ siluzan-tso google-analysis-batch status --json-out ./snap-batch --run-id <runId
73
73
  | `--account-concurrency <n>` | 账户级并发(1~16) | 4 |
74
74
  | `--section-concurrency <n>` | 单账户内维度并发(1~16) | 6 |
75
75
  | `--keyword-limit <n>` | keywords / search-terms 条数上限 | google-analysis 默认 |
76
+ | `--campaign-geo-cost-greater <n>` 等 | 仅 **`campaign-geo`**:与网关 `GetCampaignsGeoReport` 可选 query 一致;写入 `run-manifest.json`,`resume` 沿用 | 不设 |
76
77
  | `--min-spend <n>` | 区间消耗 ≤ 此值跳过(与 `-a` 同传无效) | 0 |
77
78
  | `--deadline <dur>` | 整任务硬截止(如 `30m` / `2h`),到期标记 paused | 不设 |
78
79
  | `--task-timeout-ms <n>` | 单 task 硬超时 | 60000 |
@@ -48,9 +48,19 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
48
48
 
49
49
  - `stats`:区间消耗、曝光、点击、转化、CTR、CPC 等与**账户级周报摘要**对齐;金额用 `*Display` 或文档规定的展示字段。
50
50
  - `balance`:**当前**账户余额(非历史快照);话术里写清「截至查询时点」。
51
- - `google-analysis`:系列 / 词 / 搜索词 / 设备 / 国家或地区**明细表**;TopN 排序在脚本里对 JSON 做,禁止心算。
51
+ - `google-analysis`:系列 / 词 / 搜索词;**设备**与**国家**两 Sheet 的指标来自账户级 **`devices`** / **`geographic`**(落盘 `devices-<id>.json`、`geographic-<id>.json`)。样表 R3 含「广告系列」「级别」「出价调整」等列,而账户级 JSON **常无**系列/组字段——写 xlsx 时由脚本按下文 **「样表占位(账户级→样表列)」** 统一补齐,**禁止**为凑列改拉 `campaign-device` / `campaign-geo` 冒充账户口径。
52
+ - TopN、排序、聚合均在脚本内对 JSON 完成,禁止心算。
52
53
  - **写脚本前**先读各 `<section>-<id>.outline.txt`(及 stats/balance 的 outline,若有),再读对应 `.json`。
53
54
 
55
+ **样表占位(账户级 `devices` / `geographic` → 设备与国家 Sheet)**(写脚本时**必须**遵守,便于与运营样表逐列对齐):
56
+
57
+ | 列(样表语义) | 规则 |
58
+ | --- | --- |
59
+ | **广告系列** | JSON 无 `campaignName`(或等价字段)时,**统一填** `所有广告系列` |
60
+ | **级别** | **统一填** `账户` |
61
+ | **出价调整** | **统一填** `-`(半角减号;勿与空单元格混用,除非运营另行约定) |
62
+ | **广告组**(仅「设备」Sheet R3 含该列) | 账户级 JSON 无广告组字段时,**统一填** `—`(全角长横,**勿**用 `-`,以免与「出价调整」列混淆) |
63
+
54
64
  ---
55
65
 
56
66
  ## 最终 xlsx 版式规范(与运营样表对齐)
@@ -64,8 +74,8 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
64
74
  | 1 | `账户报告` | `campaigns` + 复盘文案(stats/overview 汇总后由 Agent 撰写) |
65
75
  | 2 | `关键词` | `keywords` |
66
76
  | 3 | `搜索词` | `search-terms` |
67
- | 4 | `设备` | `devices` |
68
- | 5 | `国家` | `geographic`(列「地理位置」为**国家/地区**中文名) |
77
+ | 4 | `设备` | **`devices`**(`devices-*.json`;账户级;见上「样表占位」补齐广告系列/级别/出价调整列) |
78
+ | 5 | `国家` | **`geographic`**(`geographic-*.json`;账户级;**广告系列**列无字段时填 `所有广告系列`) |
69
79
 
70
80
  ### 各 Sheet 共同版式
71
81
 
@@ -98,8 +108,8 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
98
108
  **1. 账户层级**(整体消耗、展示、点击、点击率与 3% 基准对比、询盘/转化数、平均询盘成本等,数来自 stats/overview 与上表合计);
99
109
  **2. 关键词层级**(高转化词、高消耗低转化词及出价/竞争力建议);
100
110
  **3. 搜索字词层级**(标红/高意向词、否词与加词建议);
101
- **4. 设备层级**(消耗与转化在各设备的分布);
102
- **5. 国家层级**(高消耗/低转化国家与降权/调价建议)。
111
+ **4. 设备层级**(账户级 **`devices`**:各设备消耗/转化分布;结合样表占位列阅读);
112
+ **5. 国家层级**(账户级 **`geographic`**:各国家/地区消耗/转化;**广告系列**列在占位规则下为 `所有广告系列`)。
103
113
  文案由 Agent 据 JSON 撰写,**不得编造**表中不存在的数字。
104
114
 
105
115
  ---
@@ -133,7 +143,7 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
133
143
  | R1 标题 | `设备报告` |
134
144
  | R2 | 统计区间 |
135
145
  | R3 表头(A→M,共 13 列) | `设备` \| `级别` \| `出价调整` \| `广告系列` \| `广告组` \| `展示次数` \| `点击次数` \| `点击率` \| `平均每次点击费用` \| `费用` \| `转化次数` \| `每次转化费用` \| `转化率` |
136
- | R4… | `devices-*.json`:`deviceType` → 设备;**级别 / 出价调整** JSON 无字段则留空或「—」 |
146
+ | R4… | **`devices-*.json`**(`devices[]`):`deviceType` → 设备;指标列按 outline 映射。**广告系列**无字段 **`所有广告系列`**;**级别** **`账户`**;**出价调整** → **`-`**;**广告组**无字段 → **`—`** |
137
147
 
138
148
  ---
139
149
 
@@ -144,7 +154,7 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
144
154
  | R1 标题 | `地理位置报告` |
145
155
  | R2 | 统计区间 |
146
156
  | R3 表头(A→L,共 12 列) | `地理位置` \| `广告系列` \| `展示次数` \| `互动次数` \| `互动率` \| `费用` \| `平均费用` \| `点击次数` \| `点击率` \| `转化次数` \| `每次转化费用` \| `转化率` |
147
- | R4… | `geographic-*.json`:`countryOrRegion` → 地理位置;**互动次数 / 互动率 / 平均费用** 按 outline 与网关字段映射,无则留空或从 `interactions` 等派生并注释 |
157
+ | R4… | **`geographic-*.json`**(`countries[]`):`countryOrRegion` → 地理位置;**广告系列**无 `campaignName`(或等价字段)→ **`所有广告系列`**;**互动次数 / 互动率 / 平均费用** 按 outline 与网关字段映射,无则留空或从 `interactions` 等派生并注释 |
148
158
 
149
159
  ---
150
160
 
@@ -197,3 +207,7 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
197
207
  | 默认维度 | 8 维 + 主动追问可选追加 | **固定** `overview,campaigns,keywords,search-terms,devices,geographic` + `stats` + `balance` |
198
208
  | 输出形态 | 长文分析报告 | **固定话术** + 短复盘 + **多 Sheet Excel(Agent 脚本写,非 CLI)** |
199
209
  | 典型用户 | 内部分析 | **发客户**的同步简报 |
210
+
211
+
212
+
213
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.19-beta.3",
3
+ "version": "1.1.19-beta.5",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",