tb-order-sync 0.4.5 → 0.4.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.4.6] - 2026-03-20
6
+
7
+ ### 变更
8
+
9
+ - 简化 `tb setup` 配置向导:链接选择统一改为“输入编号打开,直接回车跳过”,不再显示 `0` 和 `/skip` 等额外选项。
10
+ - 简化腾讯文档凭证录入:向导现只要求填写 `Client ID`、`Open ID`、`Access Token`,不再弹出 `Client Secret` 输入。
11
+ - 简化表格配置:A / B 表现改为直接粘贴完整腾讯文档链接并自动解析,不再要求用户手动输入 `File ID`。
12
+ - 简化配置流程:隐藏飞书配置、运行环境、日志级别、C 表同步模式等当前不用的项目,减少新手初始化负担。
13
+ - 优化中文文案:`A表(订单表)` 改为 `A表(订单表/毛利率表)`,`A/B 表按名称自动选最新月份` 改为 `A/B表表格关键字匹配`。
14
+ - 优化运行模式选择:毛利计算模式和退款匹配模式现改为中文二选一说明,不再直接显示英文枚举。
15
+
16
+ ### 修复
17
+
18
+ - 修复配置向导帮助说明过于技术化的问题,改成小白可理解的“打开表格 -> 复制完整链接 -> 粘贴自动解析”流程。
19
+ - 修复 README 与当前向导行为不一致的问题,更新腾讯文档配置说明和最新测试结果。
20
+
5
21
  ## [0.4.5] - 2026-03-20
6
22
 
7
23
  ### 新增
package/README.md CHANGED
@@ -127,13 +127,12 @@ tb daemon autostart-disable
127
127
  - 开发者平台: [腾讯文档开放生态](https://docs.qq.com/open/developers/)
128
128
  - 建议流程:
129
129
  1. 先进入开发者平台创建应用
130
- 2. 在应用详情页获取 `Client ID` 和 `Client Secret`
131
- 3. 按官方 OAuth2 流程获取 `Access Token`
130
+ 2. 在应用详情页获取 `Client ID`
131
+ 3. 完成授权后获取 `Open ID` `Access Token`
132
132
  4. 再回到本项目执行 `tb setup`
133
133
  - 当前说明:
134
134
  - 本项目 MVP 目前依赖你手工提供有效 `Access Token`
135
135
  - 当前运行链路要求 `Client ID + Open ID + Access Token`
136
- - `Client Secret` 目前保留为可选项,后续接自动刷新 token 时再使用
137
136
  - 在线表格 v3 读写链路已经完成真实联调验证
138
137
 
139
138
  ### 飞书 API
@@ -351,7 +350,8 @@ B_COL_ORDER_NO=A
351
350
  ```
352
351
 
353
352
  补充说明:
354
- - `tb setup` 支持直接粘贴腾讯文档完整链接,自动拆出 `File ID / Sheet ID`
353
+ - `tb setup` 会直接要求你粘贴腾讯文档完整链接,并自动解析 `File ID / Sheet ID`
354
+ - `tb setup` 当前只保留腾讯文档最小必填项,不再展示飞书和其他暂时不用的高级配置
355
355
  - 如果填写 `TENCENT_A_SHEET_NAME_KEYWORD` / `TENCENT_B_SHEET_NAME_KEYWORD`,系统会在对应文件里自动选取标题中匹配关键字的“最新月份”工作表
356
356
  - `tb check` 会做启动自检,不只是看 `.env` 是否存在
357
357
  - 当前退款高亮效果是“整行红色文字”,不是背景填充
@@ -362,7 +362,7 @@ B_COL_ORDER_NO=A
362
362
  ## 🧪 测试
363
363
 
364
364
  ```bash
365
- # 运行全部测试(49 tests)
365
+ # 运行全部测试
366
366
  pytest tests/ -v
367
367
 
368
368
  # 单独运行
package/cli/setup.py CHANGED
@@ -39,9 +39,8 @@ BANNER_SUBTITLE = "按照提示逐步完成配置,按 Ctrl+C 随时退出"
39
39
 
40
40
  STEP_TENCENT = "第 1 步:腾讯文档凭证"
41
41
  STEP_SHEETS = "第 2 步:表格 ID 配置"
42
- STEP_FEISHU = "第 3 步:飞书配置(可选)"
43
- STEP_RUNTIME = "第 4 步:运行参数"
44
- STEP_COLUMNS = "第 5 步:列映射"
42
+ STEP_RUNTIME = "第 3 步:运行参数"
43
+ STEP_COLUMNS = "第 4 步:列映射"
45
44
  STEP_SUMMARY = "配置总览"
46
45
  STEP_WRITE = "写入配置"
47
46
  STEP_TEST = "连接测试"
@@ -55,12 +54,6 @@ _TENCENT_FILE_RE = re.compile(r"/(?:sheet|doc|slide|mind|form|pdf)/([^/?#]+)")
55
54
 
56
55
  TENCENT_DOCS_GUIDE_URL = "https://docs.qq.com/open/document/app/"
57
56
  TENCENT_DEVELOPER_CONSOLE_URL = "https://docs.qq.com/open/developers/"
58
- FEISHU_DEVELOPER_CONSOLE_URL = "https://open.feishu.cn/app"
59
- FEISHU_TOKEN_DOC_URL = (
60
- "https://open.feishu.cn/document/server-docs/"
61
- "authentication-management/access-token/tenant_access_token_internal"
62
- )
63
- FEISHU_TOKEN_TUTORIAL_URL = "https://www.feishu.cn/content/000214591773"
64
57
  _SETUP_LOGO = (
65
58
  "[bold #8ecae6]████████╗██████╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗[/bold #8ecae6]\n"
66
59
  "[bold #38bdf8]╚══██╔══╝██╔══██╗ ██╔═══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗[/bold #38bdf8]\n"
@@ -120,24 +113,18 @@ def resolve_link_selection(raw: str, link_count: int) -> list[int]:
120
113
  """Resolve numeric selection for link-opening prompts.
121
114
 
122
115
  Rules:
123
- - 0 / empty / /skip => skip
124
- - 1 => open all
125
- - 2..N+1 => open one specific link
116
+ - empty => skip
117
+ - 1..N => open one specific link
126
118
  """
127
- value = raw.strip().lower()
128
- if value in {"", "0", "/skip", "skip", "n", "no", "q", "quit"}:
119
+ value = raw.strip()
120
+ if not value:
129
121
  return []
130
122
  try:
131
123
  choice = int(value)
132
124
  except ValueError as exc:
133
125
  raise ValueError("请输入数字编号") from exc
134
126
 
135
- if choice == 0:
136
- return []
137
- if choice == 1:
138
- return list(range(link_count))
139
-
140
- index = choice - 2
127
+ index = choice - 1
141
128
  if 0 <= index < link_count:
142
129
  return [index]
143
130
  raise ValueError("编号超出范围")
@@ -254,6 +241,90 @@ class SetupWizard:
254
241
  return default
255
242
  return raw in ("y", "yes", "是")
256
243
 
244
+ def _prompt_choice(
245
+ self,
246
+ label: str,
247
+ key: str,
248
+ options: Sequence[tuple[str, str, str]],
249
+ default_value: str,
250
+ ) -> str:
251
+ """Prompt the user to choose one option via a numbered list."""
252
+ existing = (self._existing.get(key, "") or "").strip()
253
+ effective_default = existing or default_value
254
+ option_map = {str(idx): value for idx, (value, _, _) in enumerate(options, start=1)}
255
+ default_choice = next(
256
+ (str(idx) for idx, (value, _, _) in enumerate(options, start=1) if value == effective_default),
257
+ "1",
258
+ )
259
+
260
+ self.console.print(f" {label}")
261
+ for idx, (_, title, desc) in enumerate(options, start=1):
262
+ suffix = " [dim](默认)[/dim]" if str(idx) == default_choice else ""
263
+ self.console.print(f" {idx}. {title}{suffix}")
264
+ self.console.print(f" [dim]{desc}[/dim]")
265
+
266
+ while True:
267
+ self.console.print(f" [dim]请输入编号,直接回车使用默认选项({default_choice})[/dim]")
268
+ raw = self._read_line()
269
+ if not raw:
270
+ return option_map[default_choice]
271
+ if raw in option_map:
272
+ return option_map[raw]
273
+ self.console.print(" [red]请输入有效编号[/red]")
274
+
275
+ def _prompt_sheet_link(self, name: str, file_key: str, sheet_key: str) -> None:
276
+ """Prompt for a full Tencent Docs sheet link and parse both file/sheet ids."""
277
+ existing_file = (self._existing.get(file_key, "") or "").strip()
278
+ existing_sheet = (self._existing.get(sheet_key, "") or "").strip()
279
+
280
+ self.console.print(f" [bold]{name}[/bold]")
281
+ if existing_file and existing_sheet:
282
+ self.console.print(" [dim]直接粘贴完整腾讯文档链接;直接回车则保持当前配置[/dim]")
283
+ else:
284
+ self.console.print(" [dim]请直接粘贴完整腾讯文档在线表格链接[/dim]")
285
+
286
+ while True:
287
+ hint = " [dim](可直接粘贴)[/dim]"
288
+ self.console.print(f" {name}链接{hint}")
289
+ raw = self._read_line()
290
+
291
+ if not raw:
292
+ if existing_file and existing_sheet:
293
+ self.values[file_key] = existing_file
294
+ self.values[sheet_key] = existing_sheet
295
+ self.console.print(f" [green]已保留当前配置:{existing_sheet}[/green]")
296
+ return
297
+ self.console.print(" [red]请直接粘贴完整腾讯文档链接[/red]")
298
+ continue
299
+
300
+ file_id, sheet_id = parse_tencent_sheet_reference(raw)
301
+ if not file_id or not sheet_id:
302
+ self.console.print(" [red]无法自动解析,请直接粘贴完整腾讯文档在线表格链接[/red]\n")
303
+ continue
304
+
305
+ self.values[file_key] = file_id
306
+ self.values[sheet_key] = sheet_id
307
+ self.console.print(f" [green]已自动解析 Sheet ID: {sheet_id}[/green]")
308
+ return
309
+
310
+ def _apply_implicit_defaults(self) -> None:
311
+ """Preserve hidden or omitted config values when setup skips them."""
312
+ implicit_defaults = {
313
+ "TENCENT_CLIENT_SECRET": "",
314
+ "FEISHU_APP_ID": "",
315
+ "FEISHU_APP_SECRET": "",
316
+ "FEISHU_C_FILE_TOKEN": "",
317
+ "FEISHU_C_SHEET_ID": "",
318
+ "APP_ENV": "dev",
319
+ "LOG_LEVEL": "INFO",
320
+ "C_SYNC_MODE": "incremental",
321
+ "REFUND_STATUS_TEXT": "已退款",
322
+ "DATA_ERROR_TEXT": "数据异常",
323
+ }
324
+ for key, default in implicit_defaults.items():
325
+ if key not in self.values:
326
+ self.values[key] = (self._existing.get(key, "") or default).strip() or default
327
+
257
328
  def _offer_open_links(self, title: str, links: Sequence[tuple[str, str]]) -> None:
258
329
  """Optionally open one or more documentation links in the default browser."""
259
330
  if not links:
@@ -263,10 +334,10 @@ class SetupWizard:
263
334
  for idx, (label, url) in enumerate(links, start=1):
264
335
  self.console.print(f" {idx}. {label}: [cyan]{url}[/cyan]")
265
336
  if len(links) == 1:
266
- choice_help = "0=暂时跳过,1=打开全部,2=打开该链接"
337
+ choice_help = "输入 1 打开链接,直接回车跳过"
267
338
  else:
268
- choice_help = f"0=暂时跳过,1=打开全部,2..{len(links) + 1}=打开单个链接"
269
- self.console.print(f" [dim]输入编号:{choice_help},也可输入 /skip[/dim]")
339
+ choice_help = f"输入 1{len(links)} 打开对应链接,直接回车跳过"
340
+ self.console.print(f" [dim]{choice_help}[/dim]")
270
341
 
271
342
  while True:
272
343
  try:
@@ -333,15 +404,13 @@ class SetupWizard:
333
404
  def _show_tencent_guide(self) -> None:
334
405
  """Display beginner guidance for Tencent Docs Open API setup."""
335
406
  body = (
336
- "1. 打开腾讯文档开放平台开发文档,先确认你要接的是 Open API。\n"
407
+ "1. 先打开腾讯文档开放平台开发文档,确认你接入的是 Open API。\n"
337
408
  f" 文档入口: {TENCENT_DOCS_GUIDE_URL}\n"
338
- "2. 打开开发者平台,创建应用并进入应用详情页。\n"
409
+ "2. 再打开开发者平台,创建应用并进入应用详情页。\n"
339
410
  f" 开发者平台: {TENCENT_DEVELOPER_CONSOLE_URL}\n"
340
- "3. 在应用详情中获取 Client ID / Client Secret。\n"
341
- "4. 按官方 OAuth2 授权流程获取 Access Token。\n"
342
- "5. 本项目当前 MVP 需要你先手工提供有效 Access Token"
343
- "自动刷新 token 还没接入。\n"
344
- "6. Open ID 目前是可选项,部分接口或企业场景可能会用到。"
411
+ "3. 在应用详情页里获取 Client ID。\n"
412
+ "4. 完成授权后,拿到 Open ID Access Token。\n"
413
+ "5. 当前向导只需要你填写 Client ID、Open ID、Access Token 这三项即可。"
345
414
  )
346
415
  self.console.print(Panel(
347
416
  body,
@@ -358,12 +427,9 @@ class SetupWizard:
358
427
  """Display how to locate file id and sheet id."""
359
428
  body = (
360
429
  "1. 先在浏览器打开目标腾讯文档 A 表 / B 表。\n"
361
- "2. File ID / Sheet ID 的展示形式可能因腾讯文档产品类型不同而不同。\n"
362
- "3. 第一版请以你在官方链接、页面参数或开发者文档中实际看到的 ID 为准。\n"
363
- "4. 如果你不确定,请先在腾讯文档开发文档里核对在线表格 API 的文件和 sheet 标识规则。\n"
364
- f" 文档入口: {TENCENT_DOCS_GUIDE_URL}\n"
365
- "5. 本项目代码里已把腾讯文档 endpoint 标为 TODO / NEED_VERIFY,"
366
- "如果你的表格类型不是标准在线表格,后续可能需要再补对接。"
430
+ "2. 复制浏览器地址栏里的完整表格链接。\n"
431
+ "3. 直接把完整链接粘贴到下面,系统会自动解析 File ID 和 Sheet ID。\n"
432
+ "4. 如果没有识别成功,通常是因为链接不完整,或者当前页面不是在线表格页。"
367
433
  )
368
434
  self.console.print(Panel(
369
435
  body,
@@ -375,32 +441,6 @@ class SetupWizard:
375
441
  ("腾讯文档开发文档", TENCENT_DOCS_GUIDE_URL),
376
442
  ])
377
443
 
378
- def _show_feishu_guide(self) -> None:
379
- """Display beginner guidance for Feishu Open Platform setup."""
380
- body = (
381
- "1. 打开飞书开放平台,创建自建应用。\n"
382
- f" 开发者平台: {FEISHU_DEVELOPER_CONSOLE_URL}\n"
383
- "2. 在应用凭证页获取 App ID 和 App Secret。\n"
384
- "3. 按官方服务端认证文档获取 tenant_access_token。\n"
385
- f" 官方文档: {FEISHU_TOKEN_DOC_URL}\n"
386
- "4. 如果你是第一次接飞书 API,可以先看一遍官方教程示例,"
387
- "它演示了 token 的获取和后续 API 调用链路。\n"
388
- f" 教程文章: {FEISHU_TOKEN_TUTORIAL_URL}\n"
389
- "5. C 表的 File Token / Sheet ID 请以你实际接入的飞书文档或表格链接规则为准。\n"
390
- "6. 当前项目里飞书 connector 还是 skeleton,先录入配置,后续第二阶段接通。"
391
- )
392
- self.console.print(Panel(
393
- body,
394
- title="[bold]飞书 API 获取指引[/bold]",
395
- border_style="green",
396
- expand=False,
397
- ))
398
- self._offer_open_links("飞书相关链接", [
399
- ("飞书开发者平台", FEISHU_DEVELOPER_CONSOLE_URL),
400
- ("tenant_access_token 官方文档", FEISHU_TOKEN_DOC_URL),
401
- ("动态 Token 教程", FEISHU_TOKEN_TUTORIAL_URL),
402
- ])
403
-
404
444
  # ── Steps ──────────────────────────────────────────────────────────────
405
445
 
406
446
  def _step_tencent_creds(self) -> None:
@@ -412,120 +452,59 @@ class SetupWizard:
412
452
  self.values["TENCENT_CLIENT_ID"] = self._prompt(
413
453
  "Client ID", "TENCENT_CLIENT_ID", secret=True, validator=_not_empty,
414
454
  error_msg="Client ID 不能为空",
415
- allow_skip=True,
416
- )
417
- self.values["TENCENT_CLIENT_SECRET"] = self._prompt(
418
- "Client Secret(当前运行可留空)", "TENCENT_CLIENT_SECRET", secret=True,
419
- allow_skip=True,
420
455
  )
421
456
  self.values["TENCENT_OPEN_ID"] = self._prompt(
422
- "Open ID(可选,部分接口需要)", "TENCENT_OPEN_ID", secret=True,
423
- allow_skip=True,
457
+ "Open ID", "TENCENT_OPEN_ID", secret=True, validator=_not_empty,
458
+ error_msg="Open ID 不能为空",
424
459
  )
425
460
  self.values["TENCENT_ACCESS_TOKEN"] = self._prompt(
426
461
  "Access Token", "TENCENT_ACCESS_TOKEN", secret=True, validator=_not_empty,
427
462
  error_msg="Access Token 不能为空",
428
- allow_skip=True,
429
463
  )
430
464
 
431
465
  def _step_sheet_ids(self) -> None:
432
466
  self.console.print(f"\n[bold cyan]📊 {STEP_SHEETS}[/bold cyan]")
433
- self.console.print(" 可直接粘贴腾讯文档完整链接,系统会自动拆出 File ID / Sheet ID\n")
467
+ self.console.print(" 直接粘贴腾讯文档完整链接,系统会自动解析表格信息\n")
434
468
  self._show_sheet_id_guide()
435
469
  self.console.print("")
436
470
 
437
- def prompt_sheet_target(name: str, file_key: str, sheet_key: str) -> None:
438
- while True:
439
- self.console.print(f" [bold]{name}[/bold]")
440
- ref = self._prompt(
441
- f"{name}链接或 File ID", file_key, validator=_not_empty,
442
- error_msg="请输入腾讯文档链接或 File ID",
443
- allow_skip=True,
444
- )
445
- if not ref:
446
- self.values[file_key] = ""
447
- self.values[sheet_key] = ""
448
- break
449
- file_id, sheet_id = parse_tencent_sheet_reference(ref)
450
- if not file_id:
451
- self.console.print(" [red]无法从链接中解析 File ID,请重新输入完整链接或直接填 File ID[/red]\n")
452
- continue
453
- self.values[file_key] = file_id
454
- if sheet_id:
455
- self.console.print(f" [green]已自动解析 {name} Sheet ID: {sheet_id}[/green]")
456
- self.values[sheet_key] = self._prompt(
457
- f"{name} Sheet ID", sheet_key, default=sheet_id, validator=_not_empty,
458
- error_msg="Sheet ID 不能为空",
459
- allow_skip=True,
460
- )
461
- break
462
-
463
- prompt_sheet_target("A 表(订单表)", "TENCENT_A_FILE_ID", "TENCENT_A_SHEET_ID")
471
+ self._prompt_sheet_link("A表(订单表/毛利率表)", "TENCENT_A_FILE_ID", "TENCENT_A_SHEET_ID")
464
472
  self.values["TENCENT_A_SHEET_NAME_KEYWORD"] = self._prompt(
465
- "A 表按名称自动选最新月份(可选关键字,如 毛利率)",
473
+ "A表表格关键字匹配(可选,例如 毛利率)",
466
474
  "TENCENT_A_SHEET_NAME_KEYWORD",
467
475
  default="",
468
476
  )
469
477
  self.console.print("")
470
- prompt_sheet_target("B 表(退款表)", "TENCENT_B_FILE_ID", "TENCENT_B_SHEET_ID")
478
+ self._prompt_sheet_link("B表(客户退款表)", "TENCENT_B_FILE_ID", "TENCENT_B_SHEET_ID")
471
479
  self.values["TENCENT_B_SHEET_NAME_KEYWORD"] = self._prompt(
472
- "B 表按名称自动选最新月份(可选关键字,如 客户退款)",
480
+ "B表表格关键字匹配(可选,例如 客户退款)",
473
481
  "TENCENT_B_SHEET_NAME_KEYWORD",
474
482
  default="",
475
483
  )
476
484
 
477
- def _step_feishu_creds(self) -> None:
478
- self.console.print(f"\n[bold cyan]🐦 {STEP_FEISHU}[/bold cyan]")
479
- self._show_feishu_guide()
480
- self.console.print("")
481
- if not self._prompt_bool("是否现在配置飞书(C 表)?", default=False):
482
- self.values.setdefault("FEISHU_APP_ID", "")
483
- self.values.setdefault("FEISHU_APP_SECRET", "")
484
- self.values.setdefault("FEISHU_C_FILE_TOKEN", "")
485
- self.values.setdefault("FEISHU_C_SHEET_ID", "")
486
- self.console.print(" [dim]已跳过飞书配置,后续可重新运行 setup 补充[/dim]")
487
- return
488
-
489
- self.values["FEISHU_APP_ID"] = self._prompt(
490
- "飞书 App ID", "FEISHU_APP_ID", secret=True, validator=_not_empty,
491
- allow_skip=True,
492
- )
493
- self.values["FEISHU_APP_SECRET"] = self._prompt(
494
- "飞书 App Secret", "FEISHU_APP_SECRET", secret=True, validator=_not_empty,
495
- allow_skip=True,
496
- )
497
- self.values["FEISHU_C_FILE_TOKEN"] = self._prompt(
498
- "C 表 File Token", "FEISHU_C_FILE_TOKEN", validator=_not_empty,
499
- allow_skip=True,
500
- )
501
- self.values["FEISHU_C_SHEET_ID"] = self._prompt(
502
- "C 表 Sheet ID", "FEISHU_C_SHEET_ID", validator=_not_empty,
503
- allow_skip=True,
504
- )
505
-
506
485
  def _step_runtime(self) -> None:
507
486
  self.console.print(f"\n[bold cyan]⚙️ {STEP_RUNTIME}[/bold cyan]\n")
508
487
 
509
- self.values["APP_ENV"] = self._prompt(
510
- "运行环境 (dev / staging / prod)", "APP_ENV", default="dev",
511
- )
512
- self.values["LOG_LEVEL"] = self._prompt(
513
- "日志级别 (DEBUG / INFO / WARNING / ERROR)", "LOG_LEVEL", default="INFO",
514
- )
515
488
  self.values["STATE_DIR"] = self._prompt(
516
489
  "状态文件目录", "STATE_DIR", default="state",
517
490
  )
518
- self.values["GROSS_PROFIT_MODE"] = self._prompt(
519
- "毛利计算模式 (incremental / full)", "GROSS_PROFIT_MODE", default="incremental",
520
- validator=_is_sync_mode, error_msg="请输入 incremental 或 full",
521
- )
522
- self.values["REFUND_MATCH_MODE"] = self._prompt(
523
- "退款匹配模式 (incremental / full)", "REFUND_MATCH_MODE", default="incremental",
524
- validator=_is_sync_mode, error_msg="请输入 incremental 或 full",
491
+ self.values["GROSS_PROFIT_MODE"] = self._prompt_choice(
492
+ "毛利计算模式",
493
+ "GROSS_PROFIT_MODE",
494
+ [
495
+ ("incremental", "增量模式", "只处理新增行或发生变化的行"),
496
+ ("full", "全量模式", "重新计算整张表的毛利"),
497
+ ],
498
+ default_value="incremental",
525
499
  )
526
- self.values["C_SYNC_MODE"] = self._prompt(
527
- "C 表同步模式 (incremental / full)", "C_SYNC_MODE", default="incremental",
528
- validator=_is_sync_mode, error_msg="请输入 incremental 或 full",
500
+ self.values["REFUND_MATCH_MODE"] = self._prompt_choice(
501
+ "退款匹配模式",
502
+ "REFUND_MATCH_MODE",
503
+ [
504
+ ("incremental", "增量模式", "只处理新增退款单和发生变化的订单"),
505
+ ("full", "全量模式", "全表重扫并重建退款状态"),
506
+ ],
507
+ default_value="incremental",
529
508
  )
530
509
  self.values["TASK_INTERVAL_MINUTES"] = self._prompt(
531
510
  "定时任务间隔(分钟)", "TASK_INTERVAL_MINUTES", default="10",
@@ -608,8 +587,7 @@ class SetupWizard:
608
587
 
609
588
  sections = [
610
589
  ("腾讯文档凭证", [
611
- "TENCENT_CLIENT_ID", "TENCENT_CLIENT_SECRET",
612
- "TENCENT_OPEN_ID", "TENCENT_ACCESS_TOKEN",
590
+ "TENCENT_CLIENT_ID", "TENCENT_OPEN_ID", "TENCENT_ACCESS_TOKEN",
613
591
  ]),
614
592
  ("表格 ID", [
615
593
  "TENCENT_A_FILE_ID", "TENCENT_A_SHEET_ID",
@@ -617,13 +595,8 @@ class SetupWizard:
617
595
  "TENCENT_B_FILE_ID", "TENCENT_B_SHEET_ID",
618
596
  "TENCENT_B_SHEET_NAME_KEYWORD",
619
597
  ]),
620
- ("飞书配置", [
621
- "FEISHU_APP_ID", "FEISHU_APP_SECRET",
622
- "FEISHU_C_FILE_TOKEN", "FEISHU_C_SHEET_ID",
623
- ]),
624
598
  ("运行参数", [
625
- "APP_ENV", "LOG_LEVEL", "STATE_DIR",
626
- "GROSS_PROFIT_MODE", "REFUND_MATCH_MODE", "C_SYNC_MODE",
599
+ "STATE_DIR", "GROSS_PROFIT_MODE", "REFUND_MATCH_MODE",
627
600
  "TASK_INTERVAL_MINUTES", "STARTUP_JITTER_SECONDS",
628
601
  "WRITE_BATCH_SIZE", "RETRY_TIMES",
629
602
  "DRY_RUN", "ENABLE_STYLE_UPDATE",
@@ -880,9 +853,9 @@ class SetupWizard:
880
853
 
881
854
  self._step_tencent_creds()
882
855
  self._step_sheet_ids()
883
- self._step_feishu_creds()
884
856
  self._step_runtime()
885
857
  self._step_column_mapping()
858
+ self._apply_implicit_defaults()
886
859
 
887
860
  # Ensure business text defaults
888
861
  self.values.setdefault("REFUND_STATUS_TEXT", "已退款")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tb-order-sync",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Tencent Docs order sync CLI with one-command npm install, gross-profit automation, refund marking, self-check, and daemon mode",
5
5
  "bin": {
6
6
  "tb": "bin/tb.js"