CI框架微信菜单管理核心是access_token的存储、续期与并发控制,需文件/数据库单点存储+时间戳校验+flock写锁;菜单JSON须严格符合字段规则并用JSON_UNESCAPED_UNICODE;推荐使用EasyWeChat替代手写cURL。

access_token 必须持久化存储并加锁刷新
微信要求 access_token 2 小时过期,且重复获取会作废前一个。CI 默认无跨请求共享内存,若多个请求同时发现 token 过期,会并发调用微信接口,导致部分请求拿到无效 token,菜单创建返回 {"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest"}。
正确做法是:用文件或数据库做“单点存储 + 时间戳校验 + 写锁”:
- 把
access_token和expires_in(通常 7200)一起存进cache/wechat_access_token.php,带 PHP 标签和返回语句,方便 include 安全读取 - 读取时先检查
filemtime()是否超时,超时则尝试加flock()获取写锁;只有第一个抢到锁的进程去调用微信 API 刷新,其余等待后直接读新值 - 不要用 CI 的
$this->cache->file->save(),它不保证原子写,高并发下可能写一半被读
菜单 JSON 构造必须严格符合微信字段规则
微信对菜单结构极其敏感:button 最多 3 个,每个 sub_button 最多 5 个,name 超长自动截断但不报错,容易误以为成功——实际后台根本没生效。
构造时注意三点:
- 一级菜单若含
sub_button,自身不能带key或url;否则微信返回{"errcode":40018,"errmsg":"invalid button name size"}(其实是字段冲突) -
type值必须小写且合法:如"click"、"view"、"miniprogram";"CLICK"或"link"都会静默失败 - 中文名务必用
JSON_UNESCAPED_UNICODE,否则微信解析出乱码,菜单显示为空白
示例片段(CI model 中):
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
$menu = [
'button' => [
[
'type' => 'view',
'name' => '官网',
'url' => 'https://example.com'
],
[
'name' => '服务',
'sub_button' => [
['type' => 'click', 'name' => '帮助', 'key' => 'HELP'],
['type' => 'view', 'name' => '订单', 'url' => 'https://example.com/order']
]
]
]
];
$json = json_encode($menu, JSON_UNESCAPED_UNICODE);
CI 3.x 下推荐用 EasyWeChat 替代手撸 cURL
原生 cURL 容易漏掉 SSL 配置、超时控制、错误重试,而 overtrue/wechat 已内置 token 管理、签名生成、异常封装,与 CI 兼容良好。
关键配置步骤:
- 执行
composer require overtrue/wechat:~4.4(适配 PHP 7.4+,CI3.1.11 可用) - 在
application/config/easywechat.php中定义配置项,token、app_id、secret从数据库读取,避免硬编码 - 控制器中调用:
$this->wechat->menu->create(config_item('menu')),失败时抛EasyWeChat\Kernel\Exceptions\RuntimeException,可 catch 后记录日志
注意:EasyWeChat 默认缓存走 sys_get_temp_dir(),CI 项目建议显式设为 WRITEPATH . 'cache/wechat/',确保目录可写且不被 Web 直接访问。
菜单发布后不生效?别只盯着代码
常见真因不是接口调用失败,而是:
- 公众号类型为“订阅号(未认证)”,**不支持自定义菜单**——只能看到“查看历史消息”,此时调用 create 接口会返回
{"errcode":40164,"errmsg":"invalid ip"}(误导性错误) - 测试用的微信号没关注该公众号,或关注的是测试号而非正式号,导致看不到菜单
- 微信客户端强缓存,需用非微信浏览器访问
https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index清除调试缓存,或换设备/扫码关注再试 - 菜单里含
miniprogram类型,但小程序 appid 未绑定公众号,或版本未发布
最省事验证方式:用 curl 手动请求 https://api.weixin.qq.com/cgi-bin/menu/get?access_token=xxx,看返回是否为你刚提交的结构——绕过所有框架层,直击微信响应。


















