232 lines
8.3 KiB
Plaintext
232 lines
8.3 KiB
Plaintext
|
|
/**
|
|
* @notes 生成小程序码
|
|
* @param array $params
|
|
* @return array|false
|
|
*/
|
|
public static function generateMiniProgramQrcode(array $params)
|
|
{
|
|
try {
|
|
$diagnosisId = $params['diagnosis_id'];
|
|
$patientId = $params['patient_id'];
|
|
$shareUserId = $params['share_user_id'];
|
|
|
|
// 获取小程序配置
|
|
$config = self::getMiniProgramConfig();
|
|
if (!$config) {
|
|
throw new \Exception('小程序配置未设置');
|
|
}
|
|
|
|
// 构建小程序路径和参数
|
|
$page = 'pages/order/monad/monad';
|
|
$scene = "id={$diagnosisId}&share_user={$shareUserId}";
|
|
|
|
// 调用微信接口生成小程序码
|
|
$qrcodeUrl = self::generateWxQrcode($config, $page, $scene);
|
|
|
|
if (!$qrcodeUrl) {
|
|
throw new \Exception('生成小程序码失败: ' . self::getError());
|
|
}
|
|
|
|
return [
|
|
'qrcode_url' => $qrcodeUrl,
|
|
'diagnosis_id' => $diagnosisId,
|
|
'patient_id' => $patientId
|
|
];
|
|
} catch (\Exception $e) {
|
|
self::setError($e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notes 获取小程序配置
|
|
* @return array|null
|
|
*/
|
|
private static function getMiniProgramConfig(): ?array
|
|
{
|
|
try {
|
|
// 从配置表获取小程序配置
|
|
$appId = \app\common\service\ConfigService::get('mnp_setting', 'app_id', '');
|
|
$appSecret = \app\common\service\ConfigService::get('mnp_setting', 'app_secret', '');
|
|
|
|
if (empty($appId) || empty($appSecret)) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'app_id' => $appId,
|
|
'app_secret' => $appSecret
|
|
];
|
|
} catch (\Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notes 生成微信小程序码
|
|
* @param array $config
|
|
* @param string $page
|
|
* @param string $scene
|
|
* @return string|false
|
|
*/
|
|
private static function generateWxQrcode(array $config, string $page, string $scene)
|
|
{
|
|
try {
|
|
// 记录请求参数
|
|
\think\facade\Log::info('开始生成小程序码', [
|
|
'page' => $page,
|
|
'scene' => $scene,
|
|
'app_id' => $config['app_id']
|
|
]);
|
|
|
|
// 获取access_token
|
|
$accessToken = self::getWxAccessToken($config['app_id'], $config['app_secret']);
|
|
if (!$accessToken) {
|
|
throw new \Exception('获取access_token失败: ' . self::getError());
|
|
}
|
|
|
|
// 调用微信接口生成小程序码
|
|
$url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={$accessToken}";
|
|
|
|
$data = [
|
|
'scene' => $scene,
|
|
'page' => $page,
|
|
'check_path' => false,
|
|
'env_version' => 'release',
|
|
'width' => 280
|
|
];
|
|
|
|
\think\facade\Log::info('调用微信API生成小程序码', ['data' => $data]);
|
|
|
|
$response = self::httpPost($url, json_encode($data));
|
|
|
|
// 检查是否返回错误(JSON格式)
|
|
$result = json_decode($response, true);
|
|
if (is_array($result) && isset($result['errcode']) && $result['errcode'] != 0) {
|
|
$errorMsg = "生成小程序码失败: errcode={$result['errcode']}, errmsg=" . ($result['errmsg'] ?? '未知错误');
|
|
\think\facade\Log::error($errorMsg);
|
|
|
|
// 如果是AccessToken错误,清除缓存
|
|
if ($result['errcode'] == 40001 || $result['errcode'] == 42001) {
|
|
$cacheKey = 'wx_access_token_' . $config['app_id'];
|
|
cache($cacheKey, null);
|
|
\think\facade\Log::info('已清除无效的AccessToken缓存');
|
|
}
|
|
|
|
throw new \Exception($errorMsg);
|
|
}
|
|
|
|
// 如果不是JSON错误,说明返回的是图片二进制数据
|
|
// 保存图片
|
|
$filename = 'qrcode_' . $config['app_id'] . '_' . md5($scene) . '_' . time() . '.png';
|
|
$savePath = 'uploads/qrcode/' . date('Ymd') . '/';
|
|
$fullPath = public_path() . $savePath;
|
|
|
|
if (!is_dir($fullPath)) {
|
|
mkdir($fullPath, 0755, true);
|
|
}
|
|
|
|
$saved = file_put_contents($fullPath . $filename, $response);
|
|
|
|
if (!$saved) {
|
|
throw new \Exception('保存二维码图片失败');
|
|
}
|
|
|
|
$qrcodeUrl = request()->domain() . '/' . $savePath . $filename;
|
|
\think\facade\Log::info('小程序码生成成功', ['url' => $qrcodeUrl]);
|
|
|
|
return $qrcodeUrl;
|
|
} catch (\Exception $e) {
|
|
\think\facade\Log::error('generateWxQrcode异常: ' . $e->getMessage());
|
|
self::setError($e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notes 获取微信access_token
|
|
* @param string $appId
|
|
* @param string $appSecret
|
|
* @return string|false
|
|
*/
|
|
private static function getWxAccessToken(string $appId, string $appSecret)
|
|
{
|
|
try {
|
|
// 尝试从缓存获取
|
|
$cacheKey = 'wx_access_token_' . $appId;
|
|
$accessToken = cache($cacheKey);
|
|
|
|
if ($accessToken) {
|
|
return $accessToken;
|
|
}
|
|
|
|
// 从微信服务器获取
|
|
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appId}&secret={$appSecret}";
|
|
$response = self::httpGet($url);
|
|
|
|
// 记录原始响应用于调试
|
|
\think\facade\Log::info('微信AccessToken响应: ' . $response);
|
|
|
|
$result = json_decode($response, true);
|
|
|
|
// 检查是否有错误
|
|
if (isset($result['errcode']) && $result['errcode'] != 0) {
|
|
$errorMsg = "获取AccessToken失败: errcode={$result['errcode']}, errmsg=" . ($result['errmsg'] ?? '未知错误');
|
|
\think\facade\Log::error($errorMsg);
|
|
throw new \Exception($errorMsg);
|
|
}
|
|
|
|
if (isset($result['access_token'])) {
|
|
// 缓存access_token,有效期7000秒(微信官方7200秒,提前200秒过期)
|
|
cache($cacheKey, $result['access_token'], 7000);
|
|
\think\facade\Log::info('AccessToken获取成功并已缓存');
|
|
return $result['access_token'];
|
|
}
|
|
|
|
throw new \Exception('获取access_token失败: 响应中没有access_token字段');
|
|
} catch (\Exception $e) {
|
|
\think\facade\Log::error('getWxAccessToken异常: ' . $e->getMessage());
|
|
self::setError($e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notes HTTP GET请求
|
|
* @param string $url
|
|
* @return string|false
|
|
*/
|
|
private static function httpGet(string $url)
|
|
{
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
$response = curl_exec($ch);
|
|
curl_close($ch);
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* @notes HTTP POST请求
|
|
* @param string $url
|
|
* @param string $data
|
|
* @return string|false
|
|
*/
|
|
private static function httpPost(string $url, string $data)
|
|
{
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
$response = curl_exec($ch);
|
|
curl_close($ch);
|
|
return $response;
|
|
}
|