267 lines
8.8 KiB
PHP
267 lines
8.8 KiB
PHP
<?php
|
||
|
||
namespace app\api\controller;
|
||
|
||
use app\common\service\wechat\WeChatConfigService;
|
||
use EasyWeChat\MiniApp\Application;
|
||
use think\facade\Log;
|
||
|
||
class WxCodeController extends BaseApiController
|
||
{
|
||
public function getWxacode()
|
||
{
|
||
try {
|
||
// 1. 获取参数
|
||
$scene = $this->request->param('scene', 'default');
|
||
$url = $this->request->param('page', 'pages/index/index');
|
||
$width = $this->request->param('width', 430);
|
||
// 'env_version' => 'trial',
|
||
// 'env_version' => 'develop'
|
||
$envVersion = $this->request->param('env_version', 'develop');
|
||
|
||
// 2. 参数验证
|
||
if (strlen($scene) > 32) {
|
||
return $this->fail('场景值不能超过32个字符');
|
||
}
|
||
|
||
// 3. 获取微信配置
|
||
$config = WeChatConfigService::getMnpConfig();
|
||
|
||
if (empty($config['app_id']) || empty($config['secret'])) {
|
||
return $this->fail('微信小程序配置不完整');
|
||
}
|
||
|
||
// 4. 创建微信小程序应用实例
|
||
$app = new Application($config);
|
||
|
||
// 5. 使用新的方式调用:通过 createClient() 调用API
|
||
$response = $app->createClient()->postJson('/wxa/getwxacodeunlimit', [
|
||
'scene' => 'registerSource=2' , // 0普通 1电梯 2充电宝
|
||
'page' => $url,
|
||
'width' => $width,
|
||
'auto_color' => false,
|
||
'line_color' => ['r' => 0, 'g' => 0, 'b' => 0],
|
||
'is_hyaline' => false,
|
||
'env_version' => $envVersion,
|
||
'check_path' => false,
|
||
]);
|
||
|
||
// 6. 处理响应结果
|
||
if ($this->isImageResponse($response)) {
|
||
// 保存到文件并返回URL
|
||
return $this->saveQrCodeToFile($response, $scene, $url);
|
||
} else {
|
||
// 处理错误响应
|
||
return $this->handleErrorResponse($response);
|
||
}
|
||
|
||
} catch (\Throwable $e) {
|
||
Log::error('生成小程序码异常:' . $e->getMessage());
|
||
Log::error('异常追踪:' . $e->getTraceAsString());
|
||
return $this->fail('生成失败:' . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 判断是否为图片响应
|
||
*/
|
||
private function isImageResponse($response): bool
|
||
{
|
||
$contentType = $response->getHeaderLine('content-type');
|
||
return strpos($contentType, 'image/') !== false
|
||
|| strpos($contentType, 'application/octet-stream') !== false;
|
||
}
|
||
|
||
/**
|
||
* 保存二维码到文件
|
||
*/
|
||
private function saveQrCodeToFile($response, $scene, $page)
|
||
{
|
||
try {
|
||
// 生成文件名
|
||
$filename = 'wxacode_' . date('YmdHis') . '_' . substr(md5($scene . $page), 0, 8) . '.png';
|
||
|
||
// 保存目录
|
||
$saveDir = public_path() . 'uploads/wxqrcode/';
|
||
|
||
// 确保目录存在
|
||
if (!is_dir($saveDir)) {
|
||
mkdir($saveDir, 0755, true);
|
||
}
|
||
|
||
$savePath = $saveDir . $filename;
|
||
|
||
// 保存文件(新版本方式)
|
||
$content = $response->getContent();
|
||
file_put_contents($savePath, $content);
|
||
|
||
// 验证文件
|
||
if (!file_exists($savePath) || filesize($savePath) === 0) {
|
||
throw new \Exception('文件保存失败或为空');
|
||
}
|
||
|
||
// 返回结果
|
||
$fileUrl = request()->domain() . '/uploads/wxqrcode/' . $filename;
|
||
|
||
return $this->success('生成成功', [
|
||
'url' => $fileUrl,
|
||
'path' => '/uploads/wxqrcode/' . $filename,
|
||
'filename' => $filename,
|
||
'file_size' => filesize($savePath),
|
||
'scene' => $scene,
|
||
'page' => $page,
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('保存文件失败:' . $e->getMessage());
|
||
|
||
// 备用方案:返回Base64
|
||
$content = $response->getContent();
|
||
$base64 = 'data:image/png;base64,' . base64_encode($content);
|
||
|
||
return $this->success('生成成功(Base64格式)', [
|
||
'base64' => $base64,
|
||
'scene' => $scene,
|
||
'page' => $page,
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理错误响应
|
||
*/
|
||
private function handleErrorResponse($response)
|
||
{
|
||
$content = $response->getContent();
|
||
$data = json_decode($content, true);
|
||
|
||
if (JSON_ERROR_NONE === json_last_error() && isset($data['errcode'])) {
|
||
$errorMsg = $this->getWeChatErrorMessage($data['errcode']);
|
||
Log::error('微信接口错误:' . $errorMsg . ',数据:' . $content);
|
||
return $this->fail('微信接口错误:' . $errorMsg);
|
||
}
|
||
|
||
Log::error('未知错误响应:' . $content);
|
||
return $this->fail('生成失败:未知的响应格式');
|
||
}
|
||
|
||
/**
|
||
* 获取微信错误信息
|
||
*/
|
||
private function getWeChatErrorMessage($errcode)
|
||
{
|
||
$errorMap = [
|
||
-1 => '系统繁忙,请稍候再试',
|
||
40001 => 'AppSecret错误或access_token无效',
|
||
40002 => '无效的凭证类型',
|
||
40013 => '无效的AppID',
|
||
41030 => '页面路径不存在或有权限限制',
|
||
45009 => '接口调用频率超限',
|
||
85064 => '找不到该小程序',
|
||
85065 => '小程序未发布或已下架',
|
||
86004 => '无效的scene参数',
|
||
87009 => '无效的页面路径',
|
||
9401020 => '小程序未发布',
|
||
];
|
||
|
||
return $errorMap[$errcode] ?? ('错误码:' . $errcode);
|
||
}
|
||
|
||
/**
|
||
* 测试版本 - 直接输出图片
|
||
*/
|
||
public function testGenerate()
|
||
{
|
||
try {
|
||
$scene = $this->request->param('scene', 'test');
|
||
$page = $this->request->param('page', 'pages/index/index');
|
||
|
||
$config = WeChatConfigService::getMnpConfig();
|
||
$app = new Application($config);
|
||
|
||
$response = $app->createClient()->postJson('/wxa/getwxacodeunlimit', [
|
||
'scene' => $scene,
|
||
'page' => $page,
|
||
'width' => 430,
|
||
]);
|
||
|
||
// 直接输出图片
|
||
header('Content-Type: ' . $response->getHeaderLine('content-type'));
|
||
echo $response->getContent();
|
||
exit;
|
||
|
||
} catch (\Exception $e) {
|
||
return json([
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString(),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查配置和环境
|
||
*/
|
||
public function checkEnv()
|
||
{
|
||
try {
|
||
$config = WeChatConfigService::getMnpConfig();
|
||
|
||
$checks = [
|
||
'config_exists' => !empty($config),
|
||
'app_id_exists' => !empty($config['app_id']),
|
||
'secret_exists' => !empty($config['secret']),
|
||
'class_exists' => class_exists('EasyWeChat\MiniApp\Application'),
|
||
'createClient_method' => method_exists('EasyWeChat\MiniApp\Application', 'createClient'),
|
||
];
|
||
|
||
// 测试创建实例
|
||
if ($checks['class_exists']) {
|
||
$app = new Application($config);
|
||
$checks['instance_created'] = true;
|
||
|
||
// 测试获取access_token
|
||
try {
|
||
$accessToken = $app->getAccessToken()->getToken();
|
||
$checks['access_token_ok'] = !empty($accessToken);
|
||
} catch (\Exception $e) {
|
||
$checks['access_token_error'] = $e->getMessage();
|
||
}
|
||
}
|
||
|
||
return $this->success('环境检查', [
|
||
'checks' => $checks,
|
||
'config' => [
|
||
'app_id' => $config['app_id'] ?? '未设置',
|
||
'secret_set' => !empty($config['secret']),
|
||
],
|
||
'suggestions' => $this->getSuggestions($checks),
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
return $this->fail('检查失败:' . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
private function getSuggestions($checks)
|
||
{
|
||
$suggestions = [];
|
||
|
||
if (!$checks['config_exists']) {
|
||
$suggestions[] = '配置不存在,请检查 WeChatConfigService::getMnpConfig()';
|
||
}
|
||
|
||
if (!$checks['app_id_exists']) {
|
||
$suggestions[] = '请在.env中设置 WECHAT_MINI_PROGRAM_APPID';
|
||
}
|
||
|
||
if (!$checks['secret_exists']) {
|
||
$suggestions[] = '请在.env中设置 WECHAT_MINI_PROGRAM_SECRET';
|
||
}
|
||
|
||
if (!$checks['class_exists']) {
|
||
$suggestions[] = 'EasyWeChat未安装或版本不兼容,请执行:composer require overtrue/wechat';
|
||
}
|
||
|
||
return $suggestions;
|
||
}
|
||
} |