畅捷通Helper 工具库:通用函数设计与最佳实践
一、sync_helper.php 总览sync_helper.php是整个项目的基础设施层提供了 Token 生命周期管理、API 调用封装、数据库连接、日志记录等通用功能。所有 6 个同步模块都依赖它无需重复实现。二、配置管理2.1 config.json 结构{ appKey: IERNZ8N49YRWRM6OBPCDQKE0WY9AK7UZ, appSecret: YOUR_APP_SECRET, certificate: YOUR_CERTIFICATE, secretKey: YOUR_SECRET_KEY, openToken: eyJhbG..., refreshToken: def502..., token_expiry: 1718640000, refresh_expiry: 1739404800, db: { host: 127.0.0.1, port: 3306, name: sq_t1, user: root, pass: your_password } }2.2 动态更新配置function loadConfig() { $json file_get_contents(__DIR__ . /config.json); return json_decode($json, true); } function saveConfig($config) { file_put_contents( __DIR__ . /config.json, json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) ); }设计要点getValidToken()使用引用传递$config在刷新 Token 后直接更新配置数组再调用saveConfig()持久化。外部调用方无需关心 Token 更新细节。三、Token 生命周期管理3.1 智能刷新策略3.2 关键实现function getValidToken($config) { $now time(); $tokenExpiry $config[token_expiry] ?? 0; // 提前 10 分钟刷新避免边缘情况 if (!empty($config[openToken]) $now ($tokenExpiry - 600)) { return $config[openToken]; } // 优先使用 refreshToken减少 API 调用 if (!empty($config[refreshToken]) $now ($config[refresh_expiry] ?? 0)) { try { $newToken refreshToken($config); if ($newToken) return $newToken; } catch (Exception $e) { logMsg(refreshToken 失败走完整流程: . $e-getMessage()); } } // 完整流程 return fullTokenFlow($config); }四、AES 解密实现4.1 算法参数参数值算法AES-128-ECB密钥长度128 bit (16 byte)填充方式PKCS5 Padding输入格式Base64 编码密文输出格式JSON含 bizContent.appTicket4.2 完整解密函数function decryptAppTicket($encryptMsg, $secretKey) { // 1. Base64 解码 $decoded base64_decode($encryptMsg); // 2. AES-128-ECB 解密 $decrypted openssl_decrypt( $decoded, AES-128-ECB, $secretKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING ); if ($decrypted false) { // 降级尝试使用 PKCS7 填充模式 $decrypted openssl_decrypt( $decoded, AES-128-ECB, $secretKey, OPENSSL_RAW_DATA ); } if ($decrypted false) { throw new Exception(AES 解密失败); } // 3. 手动去除 PKCS5 尾部填充 $pad ord($decrypted[strlen($decrypted) - 1]); if ($pad 0 $pad 16) { $decrypted substr($decrypted, 0, -$pad); } // 4. 解析 JSON 提取 appTicket $data json_decode($decrypted, true); if (!isset($data[bizContent][appTicket])) { throw new Exception(appTicket 缺失); } return $data[bizContent][appTicket]; }4.3 多密钥兼容机制五、API 调用封装5.1 通用 HTTP 调用function callApi($url, $data, $headers []) { $ch curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST true, CURLOPT_POSTFIELDS is_array($data) ? json_encode($data) : $data, CURLOPT_HTTPHEADER $headers, CURLOPT_RETURNTRANSFER true, CURLOPT_TIMEOUT 60, CURLOPT_CONNECTTIMEOUT 10, CURLOPT_SSL_VERIFYPEER false, // 内网环境 ]); $response curl_exec($ch); $httpCode curl_getinfo($ch, CURLINFO_HTTP_CODE); $error curl_error($ch); curl_close($ch); if ($error) { throw new Exception(cURL 错误: {$error}); } $data json_decode($response, true); if ($httpCode 400) { throw new Exception(HTTP {$httpCode}: . ($data[message] ?? $response)); } return $data; }5.2 T API 专用封装function callTplusApi($appKey, $appSecret, $openToken, $url, $body) { $headers [ Content-Type: application/json, appKey: {$appKey}, appSecret: {$appSecret}, openToken: {$openToken}, ]; return callApi($url, $body, $headers); } 自动在 Header 中注入appKey、appSecret、openToken三个认证头调用方无需手动拼接。六、数据处理工具6.1 extractRows - 兼容多种响应格式不同 API 返回格式各异extractRows()统一处理function extractRows($res) { // 格式1: Data 键直接包含数组Query 接口 if (isset($res[Data]) is_array($res[Data])) { return $res[Data]; } // 格式2: result.Data某些接口有 result 包裹 if (isset($res[result][Data]) is_array($res[result][Data])) { return $res[result][Data]; } // 格式3: 直接是数组列表 if (is_array($res) isset($res[0])) { return $res; } return []; }6.2 toBool - 布尔值标准化畅捷通 API 返回的布尔值可能是true/falseJSON、true/false字符串、1/0整数function toBool($val) { if (is_bool($val)) return $val; if (is_string($val)) return strtolower($val) true || $val 1; if (is_numeric($val)) return intval($val) 1; return (bool) $val; }七、数据库连接7.1 PDO 单例function getDB($dbConfig) { static $pdo null; if ($pdo null) { $dsn mysql:host{$dbConfig[host]};port{$dbConfig[port]};dbname{$dbConfig[name]};charsetutf8mb4; $pdo new PDO($dsn, $dbConfig[user], $dbConfig[pass], [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES false, ]); } return $pdo; }static $pdo null确保同一请求内只创建一次数据库连接避免重复连接开销。八、日志系统function logMsg($msg) { $time date(Y-m-d H:i:s); echo [{$time}] {$msg}\n; // 也可扩展写入文件 }输出格式[2026-06-17 08:09:45] 开始同步存货QueryPage 分页... [2026-06-17 08:10:32] 存货 QueryPage 第1页: 写入 200 条, 累计 200 / 7988九、设计原则总结原则实现方式单一职责Token管理/API调用/数据处理各司其职DRY6 个同步模块复用同一套 helper容错性多密钥兼容、双重解密模式、降级重试可观测性每步操作均有日志输出幂等性INSERT ON DUPLICATE KEY UPDATE 保证重复执行安全安全性敏感信息存 config.json不硬编码