tb-order-sync 0.3.0
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/.env.example +50 -0
- package/CHANGELOG.md +29 -0
- package/README.md +392 -0
- package/bin/tb.js +143 -0
- package/build.py +91 -0
- package/cli/__init__.py +0 -0
- package/cli/commands.py +258 -0
- package/cli/dashboard.py +327 -0
- package/cli/setup.py +698 -0
- package/config/__init__.py +4 -0
- package/config/mappings.py +63 -0
- package/config/settings.py +95 -0
- package/connectors/__init__.py +0 -0
- package/connectors/base.py +98 -0
- package/connectors/feishu_sheets.py +96 -0
- package/connectors/tencent_docs.py +253 -0
- package/main.py +6 -0
- package/models/__init__.py +12 -0
- package/models/records.py +38 -0
- package/models/state_models.py +35 -0
- package/models/task_models.py +47 -0
- package/package.json +40 -0
- package/requirements.txt +8 -0
- package/services/__init__.py +0 -0
- package/services/c_to_a_sync_service.py +49 -0
- package/services/daemon_service.py +319 -0
- package/services/gross_profit_service.py +202 -0
- package/services/refund_match_service.py +196 -0
- package/services/scheduler_service.py +76 -0
- package/services/state_service.py +50 -0
- package/sync_service.spec +93 -0
- package/utils/__init__.py +0 -0
- package/utils/diff.py +27 -0
- package/utils/logger.py +47 -0
- package/utils/parser.py +50 -0
- package/utils/retry.py +26 -0
- package//345/220/257/345/212/250.bat +125 -0
- package//345/220/257/345/212/250.command +125 -0
package/utils/parser.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Value parsing and normalization utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_number(value: object) -> Optional[float]:
|
|
9
|
+
"""Parse a value to float.
|
|
10
|
+
|
|
11
|
+
Handles: int, float, numeric strings (with whitespace).
|
|
12
|
+
Returns None for anything that cannot be safely parsed as a pure number.
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
>>> parse_number("650")
|
|
16
|
+
650.0
|
|
17
|
+
>>> parse_number(" 12.5 ")
|
|
18
|
+
12.5
|
|
19
|
+
>>> parse_number(None)
|
|
20
|
+
>>> parse_number("没报价")
|
|
21
|
+
"""
|
|
22
|
+
if value is None:
|
|
23
|
+
return None
|
|
24
|
+
if isinstance(value, (int, float)):
|
|
25
|
+
return float(value)
|
|
26
|
+
if isinstance(value, str):
|
|
27
|
+
stripped = value.strip()
|
|
28
|
+
if not stripped:
|
|
29
|
+
return None
|
|
30
|
+
try:
|
|
31
|
+
return float(stripped)
|
|
32
|
+
except ValueError:
|
|
33
|
+
return None
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def normalize_order_no(value: object) -> str:
|
|
38
|
+
"""Normalize an order number string.
|
|
39
|
+
|
|
40
|
+
Rules (v1 – strict):
|
|
41
|
+
- trim leading/trailing whitespace
|
|
42
|
+
- preserve original characters
|
|
43
|
+
- return empty string for None / empty
|
|
44
|
+
|
|
45
|
+
Future hook: could add prefix normalization, dedup hyphens, etc.
|
|
46
|
+
"""
|
|
47
|
+
if value is None:
|
|
48
|
+
return ""
|
|
49
|
+
s = str(value).strip()
|
|
50
|
+
return s
|
package/utils/retry.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Retry / backoff helpers built on tenacity."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from tenacity import (
|
|
6
|
+
retry,
|
|
7
|
+
retry_if_exception_type,
|
|
8
|
+
stop_after_attempt,
|
|
9
|
+
wait_exponential,
|
|
10
|
+
before_sleep_log,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from utils.logger import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def default_retry(max_attempts: int = 3):
|
|
19
|
+
"""Decorator: retry with exponential backoff on transient errors."""
|
|
20
|
+
return retry(
|
|
21
|
+
stop=stop_after_attempt(max_attempts),
|
|
22
|
+
wait=wait_exponential(multiplier=1, min=2, max=30),
|
|
23
|
+
retry=retry_if_exception_type((IOError, ConnectionError, TimeoutError)),
|
|
24
|
+
before_sleep=before_sleep_log(logger, log_level=20), # INFO
|
|
25
|
+
reraise=True,
|
|
26
|
+
)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
chcp 65001 >nul 2>&1
|
|
3
|
+
title 多表格同步服务
|
|
4
|
+
cd /d "%~dp0"
|
|
5
|
+
|
|
6
|
+
echo.
|
|
7
|
+
echo ╔══════════════════════════════════════╗
|
|
8
|
+
echo ║ 多表格同步与退款标记服务 ║
|
|
9
|
+
echo ╚══════════════════════════════════════╝
|
|
10
|
+
echo.
|
|
11
|
+
|
|
12
|
+
:: ── 优先级1:已有打包好的 exe ────────────────────────
|
|
13
|
+
if exist "sync_service.exe" (
|
|
14
|
+
set "CMD=sync_service.exe"
|
|
15
|
+
goto :menu
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
:: ── 优先级2:已有虚拟环境 ───────────────────────────
|
|
19
|
+
if exist ".venv\Scripts\python.exe" (
|
|
20
|
+
set "CMD=.venv\Scripts\python main.py"
|
|
21
|
+
goto :menu
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
:: ── 优先级3:系统有 Python ──────────────────────────
|
|
25
|
+
echo [*] 首次运行,正在初始化环境...
|
|
26
|
+
echo.
|
|
27
|
+
|
|
28
|
+
where python >nul 2>&1
|
|
29
|
+
if not errorlevel 1 (
|
|
30
|
+
echo [*] 检测到系统 Python,创建虚拟环境...
|
|
31
|
+
goto :venv_setup
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
:: ── 优先级4:使用嵌入式 Python(自动下载)───────────
|
|
35
|
+
if exist "python_embed\python.exe" (
|
|
36
|
+
echo [*] 使用本地嵌入式 Python...
|
|
37
|
+
goto :venv_from_embed
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
echo [*] 未检测到 Python,正在自动下载嵌入式 Python...
|
|
41
|
+
echo (约 15MB,仅需下载一次)
|
|
42
|
+
echo.
|
|
43
|
+
|
|
44
|
+
:: 创建下载目录
|
|
45
|
+
if not exist "python_embed" mkdir python_embed
|
|
46
|
+
|
|
47
|
+
:: 使用 PowerShell 下载 Python 3.12 嵌入式版本
|
|
48
|
+
:: Windows 内置 PowerShell,无需额外依赖
|
|
49
|
+
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
|
|
50
|
+
"$url = 'https://www.python.org/ftp/python/3.12.8/python-3.12.8-embed-amd64.zip'; " ^
|
|
51
|
+
"$zip = 'python_embed\python_embed.zip'; " ^
|
|
52
|
+
"Write-Host ' 下载中...' ; " ^
|
|
53
|
+
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; " ^
|
|
54
|
+
"Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing; " ^
|
|
55
|
+
"Write-Host ' 解压中...' ; " ^
|
|
56
|
+
"Expand-Archive -Path $zip -DestinationPath 'python_embed' -Force; " ^
|
|
57
|
+
"Remove-Item $zip -Force; " ^
|
|
58
|
+
"Write-Host ' 完成!' "
|
|
59
|
+
|
|
60
|
+
if not exist "python_embed\python.exe" (
|
|
61
|
+
echo.
|
|
62
|
+
echo [!] Python 下载失败,请检查网络连接
|
|
63
|
+
echo 或手动下载 Python 3.12+ 安装: https://www.python.org/downloads/
|
|
64
|
+
echo.
|
|
65
|
+
pause
|
|
66
|
+
exit /b 1
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
:: 启用嵌入式 Python 的 pip 支持
|
|
70
|
+
:: 需要修改 python312._pth 文件,取消注释 import site
|
|
71
|
+
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
|
|
72
|
+
"$pth = Get-ChildItem 'python_embed\python*._pth' | Select-Object -First 1; " ^
|
|
73
|
+
"if ($pth) { " ^
|
|
74
|
+
" $content = Get-Content $pth.FullName; " ^
|
|
75
|
+
" $content = $content -replace '^#import site', 'import site'; " ^
|
|
76
|
+
" Set-Content $pth.FullName $content; " ^
|
|
77
|
+
"} "
|
|
78
|
+
|
|
79
|
+
:: 安装 pip
|
|
80
|
+
echo.
|
|
81
|
+
echo [*] 安装 pip...
|
|
82
|
+
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
|
|
83
|
+
"Invoke-WebRequest -Uri 'https://bootstrap.pypa.io/get-pip.py' -OutFile 'python_embed\get-pip.py' -UseBasicParsing"
|
|
84
|
+
python_embed\python.exe python_embed\get-pip.py --no-warn-script-location -q 2>nul
|
|
85
|
+
del python_embed\get-pip.py 2>nul
|
|
86
|
+
|
|
87
|
+
:venv_from_embed
|
|
88
|
+
echo [*] 使用嵌入式 Python 安装依赖...
|
|
89
|
+
echo.
|
|
90
|
+
python_embed\python.exe -m pip install -q -r requirements.txt --target=".deps" 2>nul
|
|
91
|
+
set "PYTHONPATH=%~dp0.deps;%~dp0"
|
|
92
|
+
set "CMD=python_embed\python.exe main.py"
|
|
93
|
+
goto :menu
|
|
94
|
+
|
|
95
|
+
:venv_setup
|
|
96
|
+
echo [1/3] 创建虚拟环境...
|
|
97
|
+
python -m venv .venv
|
|
98
|
+
if errorlevel 1 (
|
|
99
|
+
echo [!] 虚拟环境创建失败
|
|
100
|
+
pause
|
|
101
|
+
exit /b 1
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
echo [2/3] 安装依赖...
|
|
105
|
+
.venv\Scripts\pip install -q -r requirements.txt
|
|
106
|
+
if errorlevel 1 (
|
|
107
|
+
echo [!] 依赖安装失败
|
|
108
|
+
pause
|
|
109
|
+
exit /b 1
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
echo [3/3] 环境初始化完成!
|
|
113
|
+
echo.
|
|
114
|
+
set "CMD=.venv\Scripts\python main.py"
|
|
115
|
+
|
|
116
|
+
:: ── 启动 Rich 控制台 / 执行指定命令 ────────────────
|
|
117
|
+
if "%~1"=="" (
|
|
118
|
+
%CMD%
|
|
119
|
+
echo.
|
|
120
|
+
pause
|
|
121
|
+
exit /b 0
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
%CMD% %*
|
|
125
|
+
exit /b %errorlevel%
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# macOS 一键启动脚本 — 双击即可运行
|
|
3
|
+
# .command 文件在 macOS 上双击会自动在 Terminal 中打开
|
|
4
|
+
|
|
5
|
+
cd "$(dirname "$0")"
|
|
6
|
+
|
|
7
|
+
echo ""
|
|
8
|
+
echo " ╔══════════════════════════════════════╗"
|
|
9
|
+
echo " ║ 多表格同步与退款标记服务 ║"
|
|
10
|
+
echo " ╚══════════════════════════════════════╝"
|
|
11
|
+
echo ""
|
|
12
|
+
|
|
13
|
+
# ── 优先级1:已有打包好的可执行文件 ──────────────────
|
|
14
|
+
if [ -x "./sync_service" ]; then
|
|
15
|
+
CMD="./sync_service"
|
|
16
|
+
elif [ -x "./dist/sync_service/sync_service" ]; then
|
|
17
|
+
CMD="./dist/sync_service/sync_service"
|
|
18
|
+
|
|
19
|
+
# ── 优先级2:已有虚拟环境 ────────────────────────────
|
|
20
|
+
elif [ -f ".venv/bin/python" ]; then
|
|
21
|
+
CMD=".venv/bin/python main.py"
|
|
22
|
+
|
|
23
|
+
# ── 优先级3:需要初始化 ─────────────────────────────
|
|
24
|
+
else
|
|
25
|
+
echo " [*] 首次运行,正在初始化环境..."
|
|
26
|
+
echo ""
|
|
27
|
+
|
|
28
|
+
# 查找可用的 Python 3.11+
|
|
29
|
+
PYTHON=""
|
|
30
|
+
for p in python3.14 python3.13 python3.12 python3.11 python3 python; do
|
|
31
|
+
if command -v "$p" &>/dev/null; then
|
|
32
|
+
ver=$("$p" -c "import sys; print(sys.version_info[:2] >= (3,11))" 2>/dev/null)
|
|
33
|
+
if [ "$ver" = "True" ]; then
|
|
34
|
+
PYTHON="$p"
|
|
35
|
+
break
|
|
36
|
+
fi
|
|
37
|
+
fi
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
# 没有 Python:尝试自动安装
|
|
41
|
+
if [ -z "$PYTHON" ]; then
|
|
42
|
+
echo " [!] 未检测到 Python 3.11+"
|
|
43
|
+
echo ""
|
|
44
|
+
|
|
45
|
+
# macOS: 尝试用 Homebrew 安装
|
|
46
|
+
if [ "$(uname)" = "Darwin" ]; then
|
|
47
|
+
if command -v brew &>/dev/null; then
|
|
48
|
+
echo " [*] 检测到 Homebrew,正在自动安装 Python 3.12..."
|
|
49
|
+
echo ""
|
|
50
|
+
brew install python@3.12
|
|
51
|
+
PYTHON="$(brew --prefix python@3.12)/bin/python3.12"
|
|
52
|
+
else
|
|
53
|
+
echo " [*] 正在安装 Homebrew(macOS 包管理器)..."
|
|
54
|
+
echo ""
|
|
55
|
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
56
|
+
|
|
57
|
+
# Apple Silicon vs Intel
|
|
58
|
+
if [ -f "/opt/homebrew/bin/brew" ]; then
|
|
59
|
+
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
60
|
+
elif [ -f "/usr/local/bin/brew" ]; then
|
|
61
|
+
eval "$(/usr/local/bin/brew shellenv)"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if command -v brew &>/dev/null; then
|
|
65
|
+
echo ""
|
|
66
|
+
echo " [*] Homebrew 安装完成,正在安装 Python..."
|
|
67
|
+
brew install python@3.12
|
|
68
|
+
PYTHON="$(brew --prefix python@3.12)/bin/python3.12"
|
|
69
|
+
fi
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Linux: 尝试用系统包管理器
|
|
73
|
+
elif [ -f /etc/os-release ]; then
|
|
74
|
+
if command -v apt-get &>/dev/null; then
|
|
75
|
+
echo " [*] 正在通过 apt 安装 Python 3.12..."
|
|
76
|
+
sudo apt-get update -qq && sudo apt-get install -y -qq python3.12 python3.12-venv python3-pip
|
|
77
|
+
PYTHON="python3.12"
|
|
78
|
+
elif command -v dnf &>/dev/null; then
|
|
79
|
+
echo " [*] 正在通过 dnf 安装 Python 3.12..."
|
|
80
|
+
sudo dnf install -y python3.12
|
|
81
|
+
PYTHON="python3.12"
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# 最终检查
|
|
86
|
+
if [ -z "$PYTHON" ] || ! command -v "$PYTHON" &>/dev/null; then
|
|
87
|
+
echo ""
|
|
88
|
+
echo " [!] Python 自动安装失败,请手动安装:"
|
|
89
|
+
echo " macOS: brew install python@3.12"
|
|
90
|
+
echo " Ubuntu: sudo apt install python3.12 python3.12-venv"
|
|
91
|
+
echo " 或访问: https://www.python.org/downloads/"
|
|
92
|
+
echo ""
|
|
93
|
+
read -p " 按回车退出..." _
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo " [1/3] 创建虚拟环境 (使用 $PYTHON)..."
|
|
99
|
+
"$PYTHON" -m venv .venv || {
|
|
100
|
+
echo " [!] 创建失败"
|
|
101
|
+
read -p " 按回车退出..." _
|
|
102
|
+
exit 1
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
echo " [2/3] 安装依赖..."
|
|
106
|
+
.venv/bin/pip install -q -r requirements.txt || {
|
|
107
|
+
echo " [!] 安装失败"
|
|
108
|
+
read -p " 按回车退出..." _
|
|
109
|
+
exit 1
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
echo " [3/3] 环境初始化完成!"
|
|
113
|
+
echo ""
|
|
114
|
+
|
|
115
|
+
CMD=".venv/bin/python main.py"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# ── 启动 Rich 控制台 / 执行指定命令 ──────────────────
|
|
119
|
+
if [ "$#" -gt 0 ]; then
|
|
120
|
+
$CMD "$@"
|
|
121
|
+
else
|
|
122
|
+
$CMD
|
|
123
|
+
echo ""
|
|
124
|
+
read -p " 按回车关闭窗口..." _
|
|
125
|
+
fi
|