通达OA 升级后,旧版 include_once("/inc/conn.php") 的 exequery($connection,$query) 经常报参数错误,新版变成 exequery(TD::conn(),$query)。我正想让 Claude Code 对比两段调用链,官方额度先撑不住了,于是直接把 Claude Code 接到 TaoToken。在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建 API Key,Base URL 填 https://taotoken.net/api,重启后继续排查,整个过程不到十分钟。下面按实际操作顺序写,重点在配置步骤和两段代码的差异分析。最后确认 TD::conn() 内部如何拿到连接、$connection 是否继续保留,都靠同一个会话完成,中间没有被额度打断,也没有在多个 Key 之间来回切换。
1. 先别急着改 PHP:把 Claude Code 接到 TaoToken
1.1 准备三样东西:Key、Base URL、模型 ID
接入只需要三样东西,都在官网上能找到。先打开 TaoToken 注册账号,进入控制台创建 API Key,复制下来保存好,这就是等下要填进 Claude Code 的凭证。官网的模型广场会列出当前可用的模型 ID,你打算用哪个模型,就把对应的模型 ID 记下来,后面 settings.json 里要写。
Base URL 和官网地址不是一回事:官网落地页只用来注册、创建 Key、看模型广场、看用量;真正填进 Claude Code 的接口地址是 https://taotoken.net/api,末尾不要加 /v1。很多从 OpenAI 风格工具转过来的开发者会顺手补一个 /v1,结果请求直接 404,这个问题下面会单独讲。统一 API 通道的意义在于,Claude Code 不需要知道模型服务部署在哪,它只需要一个固定的 Base URL 和一把 Key。
1.2 settings.json 里写入环境变量
Claude Code 支持两种方式指定模型服务:临时用 export 设置环境变量,长期用 ~/.claude/settings.json 的 env 字段。我建议写进 settings.json,这样每次打开 Claude Code 都是同一个配置,不用反复 export,也不会出现某个终端窗口忘记设置导致请求又打到官方接口的情况。环境变量适合快速验证,配置文件适合稳定复现,两种方式指向同一个 Base URL。
{
"env": {
"ANTHROPIC_BASE_URL": "https://taotoken.net/api",
"ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY",
"ANTHROPIC_MODEL": "YOUR_MODEL_ID"
}
}
把 YOUR_API_KEY 替换成你在 TaoToken 创建的那串 Key,YOUR_MODEL_ID 替换成模型广场上你选的模型 ID。这三个环境变量分别告诉 Claude Code:请求发到哪个地址、用什么凭证认证、默认用哪个模型。设置完保存文件,重启 Claude Code 让配置生效。
1.3 用一段 prompt 验证通道是不是真的通了
配置好后先别急着分析通达OA代码,随便问一句,比如「你现在用的 API Base URL 是什么」。如果 Claude Code 回答的是 https://taotoken.net/api,说明通道已经打通。要是它拒绝回答或者超时,优先检查两处:一是 YOUR_API_KEY 是不是真的替换成了官网拿到的 Key,二是 Base URL 末尾是不是不小心多加了一个 /v1。这两处问题在接入类似统一 API 通道时出现频率最高,检查顺序也是先 Key 后地址。
确认通道通之后,再开始处理通达OA那两段连接代码。这样后面所有分析、贴报错、改代码的操作,才是在同一个可用的模型通道上进行的。通道验证这一步不要跳过,它能把「模型没配好」和「代码本身有问题」这两类错误隔离开,省掉后面大量无效排查。
2. 通达OA 新旧两段数据库连接代码差异拆解
2.1 旧版:include conn.php 背后靠的是全局 $connection
通达OA 的旧版连接方式长这样:
include_once("/inc/conn.php");
$cursor = exequery($connection, $query);
第一行把 /inc/conn.php 引进来。这个文件的主要作用就是建立数据库连接,然后把连接句柄塞进全局变量 $connection。第二行执行查询时,exequery 的第一个参数要求传入一个有效的连接资源,也就是 $connection。这套写法在上线多年的旧系统里很常见,好处是简单,include 进来就能用;坏处是全局变量满天飞。只要某个文件忘了 include,或者 $connection 被其他地方覆盖,查询就会报「Undefined variable: connection」或参数错误。
2.2 新版:include utility_all.php 后直接 TD::conn()
新版连接方式把连接的动作收进了类里:
include_once("/inc/utility_all.php");
$cursor = exequery(TD::conn(), $query);
utility_all.php 是通达OA 新版的工具集,TD 类封装了数据库操作。TD::conn() 是一个静态方法,调用时返回一个连接句柄,不需要提前声明全局变量。exequery 的参数从「外面传进来的 $connection」变成了「TD::conn() 现场拿到的连接」。调用方不再关心连接是怎么初始化的,也不用管 $connection 是否已经被赋值。两段代码表面上只是参数来源不同,实际上是一个从「全局共享」到「类方法封装」的演变。
2.3 两段代码的调用链差异
两者的差别可以总结成一张表:
| 对比项 | 旧版 | 新版 |
|---|---|---|
| 引入文件 | include_once("/inc/conn.php") | include_once("/inc/utility_all.php") |
| 连接来源 | 全局变量 $connection | TD::conn() 静态方法 |
| 依赖关系 | 依赖全局状态 | 依赖 TD 类加载 |
| 调用链 | include → 初始化 $connection → exequery($connection,$query) | include → TD::conn() → exequery(TD::conn(),$query) |
| 典型报错 | Undefined variable: connection | Class "TD" not found / 连接失败 |
旧版的关键在「先有全局变量,再传参」;新版的关键在「先调用静态方法,再传返回值」。理解到这个层面,后面让 Claude Code 判断 $connection 要不要保留,就有明确依据了。两段代码之间的切换,核心不是改一行 exequery,而是把整套连接初始化逻辑从全局变量迁移到类方法。
3. 让 Claude Code 分析 TD::conn() 内部实现和 $connection 去留
3.1 把两段代码连同报错贴给 Claude Code
现在把问题交给 Claude Code。我用的 prompt 是这样的:
请对比下面两段通达OA数据库连接代码的调用链差异:
旧版:
include_once("/inc/conn.php");
$cursor = exequery($connection, $query);
新版:
include_once("/inc/utility_all.php");
$cursor = exequery(TD::conn(), $query);
回答这几点:
1. 两段代码在连接获取方式上的本质区别;
2. 如果项目里还有其他地方在用 $connection,新版写法下还需要保留它吗;
3. 怎样读 /inc/utility_all.php 才能确认 TD::conn() 内部拿到的连接和旧版 $connection 是不是同一个。
Claude Code 会先从调用链入手,告诉你说旧版是「文件加载时建立全局连接,查询时传入」;新版是「查询时临时调用 TD::conn() 返回连接」。接着它会建议你用 ripgrep 或直接打开 utility_all.php 查看 TD::conn() 的实现。这个过程中 Claude Code 只做代码读取和静态分析,不需要连上通达OA的数据库执行任何 SQL,生产环境更安全。
3.2 从调用链判断是否需要保留 $connection
是否需要保留 $connection,取决于项目里还有没有其他地方引用它。如果整个项目只有 exequery 这一处在用,迁移到新版后可以完全删掉 $connection 相关代码;如果其他地方还在用,比如事务提交、日志记录、其他业务函数,那 $connection 就必须保留,否则那些地方会变成未定义变量。
让 Claude Code 做这件事时,只让它读代码、统计引用位置,把结果列出来。我给它指定的是只读项目目录,不让它碰生产数据库。它扫描后发现旧版文件的 conn.php 里确实定义了 $connection,但新版业务代码已经不再引用这个全局变量,倒是还有两个历史模块的公共函数使用了 $connection。所以最终结论是:迁移到新版后,$connection 的初始化逻辑可以移除,但这两个历史模块必须同步改掉引用。
3.3 让 Claude Code 读 utility_all.php 确认静态方法实现
TD::conn() 内部拿到的连接,和旧版 $connection 是不是同一个,这直接关系到事务是否一致。让 Claude Code 打开 /inc/utility_all.php,找到 TD::conn() 的实现。常见的实现有三种:每次调用都新建连接;用静态变量缓存同一个连接;读取全局配置再初始化连接。
如果是第二种,TD::conn() 多次调用返回的是同一个连接句柄,这时候混用新旧两种写法问题不大;如果是第一种,每次调用都是新连接,那旧代码里 $connection 维护的事务,和新代码里 TD::conn() 拿到的连接不是一回事,事务就可能出现「这边提交了那边看不到」的情况。让 Claude Code 把 TD::conn() 的实现读出来,配合项目里是否还在用 $connection 的统计结果,再决定怎么改。这一步是把「能用」升级到「敢改」的关键。
4. 迁移过程中最常见的连接报错与排障
4.1 exequery() expects parameter 1 to be resource
这种报错最常见的原因不是 exequery 本身,而是 $connection 没被定义。旧版文件里如果没有 include_once("/inc/conn.php"),$connection 就是空的,exequery 收到第一个参数为空自然就报错。解决办法是补上 include,或者直接改成新版写法 exequery(TD::conn(), $query)。如果项目里已经统一使用新版写法,那就没必要再把 conn.php 引进来,避免两个连接来源同时存在。
4.2 类 "TD" not found
新版写法也会报这个错。原因通常是 utility_all.php 没有被正确加载。include_once 的路径是相对路径,如果当前工作目录和通达OA的安装目录不一致,或者文件被改名、移动过,TD 类就不会进入自动加载列表。让 Claude Code 顺着 include_once("/inc/utility_all.php") 的路径查一遍,确认文件真实存在、路径写法与入口文件相匹配,问题就能定位。还有一种是 TD 类定义了命名空间,而调用处没有 use,也会出现类似的类找不到报错。
4.3 混用新旧写法导致连接句柄不一致
最隐蔽的坑是把两套写法混在同一个业务流程里。比如一个模块用旧版 include conn.php 拿 $connection,另一个模块用新版 TD::conn(),两边各查各的。表面上看查询都能返回结果,一旦涉及事务,两个连接之间无法共享同一套事务边界。让 Claude Code 在项目里搜索 $connection 和 TD::conn() 的所有出现位置,标出哪些文件还在混用,再逐个统一。统一之后再跑一遍原有业务流程,重点观察事务提交和回滚是否符合预期。
5. 排查真实通达OA问题时如何用同一把 Key 持续消耗 Token
5.1 一个会话里反复改 PHP、贴报错会吃掉大量 Token
真实排查不是一次对话就能结束的:先让 Claude Code 对比两段代码,它输出一长串调用链分析;你把某个文件内容整个贴进去,它又要重新读一遍上下文;改完代码再问它语法对不对,又是一轮新输出。涉及通达OA这种老代码库时,一个下午就可能跑掉几百万 Token。官方额度在这种场景下根本不够用,多开几个会话就得切换 Key、切换账号。
TaoToken 的兼容通道带来的实际价值,就是让 Claude Code 始终指向同一个固定接口,同一把 Key 可以在一个长会话里连续消耗,不用担心额度中断。我配好之后,从下午三点半一直排查到六点,中间反复贴了四次不同文件的代码,Claude Code 的上下文始终保持连贯,没有因为额度问题被迫重开会话。同一把 Key 解决了「多 Key 切换」和「官方额度限制」两个问题,排查效率高了不少。
5.2 回官网控制台核对本次调用的用量
排查完一轮,回到 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 登录控制台,找到用量记录,能看到刚才那个会话消耗的 Token 总数和请求次数。这一步很重要,它能帮你确认模型调用是否真的走的是 TaoToken 通道,而不是还残留着某个旧配置在调用官方接口。如果你还没创建 API Key,现在打开官网注册,创建 Key,按第 1 节的 settings.json 配置好,再跑一次上面的对比 prompt,回控制台看这次调用有没有记账。记上了,就说明整个链路已经完整跑通。




被折叠的 条评论
为什么被折叠?



