更新
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
/**
|
||||
* 向DiagnosisLogic.php添加小程序二维码相关方法
|
||||
*/
|
||||
|
||||
$file = __DIR__ . '/app/adminapi/logic/tcm/DiagnosisLogic.php';
|
||||
|
||||
// 读取文件内容
|
||||
$content = file_get_contents($file);
|
||||
|
||||
// 检查是否已经存在这些方法
|
||||
if (strpos($content, 'generateMiniProgramQrcode') !== false) {
|
||||
echo "方法已存在,无需添加\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// 要添加的方法
|
||||
$methods = <<<'PHP'
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
PHP;
|
||||
|
||||
// 在最后一个}之前插入方法
|
||||
$lastBrace = strrpos($content, '}');
|
||||
if ($lastBrace !== false) {
|
||||
$content = substr($content, 0, $lastBrace) . $methods . "\n}";
|
||||
} else {
|
||||
echo "错误:找不到类的结束括号\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 写回文件
|
||||
file_put_contents($file, $content);
|
||||
|
||||
echo "✓ 方法添加成功\n";
|
||||
echo "已添加以下方法:\n";
|
||||
echo " - generateMiniProgramQrcode()\n";
|
||||
echo " - getMiniProgramConfig()\n";
|
||||
echo " - generateWxQrcode()\n";
|
||||
echo " - getWxAccessToken()\n";
|
||||
echo " - httpGet()\n";
|
||||
echo " - httpPost()\n";
|
||||
@@ -272,4 +272,15 @@ class DiagnosisController extends BaseAdminController
|
||||
$result = DiagnosisLogic::getDoctors();
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成小程序码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function generateMiniProgramQrcode()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->post()->goCheck('generateQrcode');
|
||||
$result = DiagnosisLogic::generateMiniProgramQrcode($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,4 +800,252 @@ class DiagnosisLogic extends BaseLogic
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, int $retryCount = 0)
|
||||
{
|
||||
try {
|
||||
// 记录请求参数
|
||||
\think\facade\Log::info('开始生成小程序码', [
|
||||
'page' => $page,
|
||||
'scene' => $scene,
|
||||
'app_id' => $config['app_id'],
|
||||
'retry_count' => $retryCount
|
||||
]);
|
||||
|
||||
// 获取access_token(如果是重试,强制刷新)
|
||||
$forceRefresh = $retryCount > 0;
|
||||
|
||||
$accessToken = self::getWxAccessToken($config['app_id'], $config['app_secret'], $forceRefresh);
|
||||
|
||||
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
|
||||
];
|
||||
|
||||
|
||||
$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) && $retryCount < 1) {
|
||||
\think\facade\Log::info('AccessToken失效,自动重试(第' . ($retryCount + 1) . '次)');
|
||||
|
||||
// 清除缓存
|
||||
$cacheKey = 'wx_access_token_' . $config['app_id'];
|
||||
cache($cacheKey, null);
|
||||
|
||||
// 递归调用,重试一次
|
||||
return self::generateWxQrcode($config, $page, $scene, $retryCount + 1);
|
||||
}
|
||||
|
||||
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, 'retry_count' => $retryCount]);
|
||||
|
||||
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
|
||||
* @param bool $forceRefresh 是否强制刷新(不使用缓存)
|
||||
* @return string|false
|
||||
*/
|
||||
private static function getWxAccessToken(string $appId, string $appSecret, bool $forceRefresh = false)
|
||||
{
|
||||
try {
|
||||
$cacheKey = 'wx_access_token_' . $appId;
|
||||
|
||||
// 如果不是强制刷新,尝试从缓存获取
|
||||
if (!$forceRefresh) {
|
||||
$accessToken = cache($cacheKey);
|
||||
|
||||
if ($accessToken) {
|
||||
\think\facade\Log::info('使用缓存的AccessToken');
|
||||
return $accessToken;
|
||||
}
|
||||
} else {
|
||||
\think\facade\Log::info('强制刷新AccessToken,清除缓存');
|
||||
cache($cacheKey, null);
|
||||
}
|
||||
|
||||
// 从微信服务器获取
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
@@ -69,6 +69,14 @@ class DiagnosisValidate extends BaseValidate
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
public function sceneGenerateQrcode()
|
||||
{
|
||||
return $this->only(['diagnosis_id', 'patient_id', 'share_user_id'])
|
||||
->append('diagnosis_id', 'require')
|
||||
->append('patient_id', 'require')
|
||||
->append('share_user_id', 'require');
|
||||
}
|
||||
|
||||
protected function checkDiagnosis($value)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
namespace app\api\controller;
|
||||
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
use app\adminapi\logic\ConfigLogic;
|
||||
|
||||
/**
|
||||
* 中医诊断控制器(供小程序调用)
|
||||
@@ -27,7 +28,7 @@ class TcmController extends BaseApiController
|
||||
* @notes 不需要登录的方法
|
||||
* @var array
|
||||
*/
|
||||
public array $notNeedLogin = ['getPatientSignature'];
|
||||
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict'];
|
||||
|
||||
/**
|
||||
* @notes 获取患者签名(供小程序调用)
|
||||
@@ -50,4 +51,44 @@ class TcmController extends BaseApiController
|
||||
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取诊单详情(供小程序调用)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function diagnosisDetail()
|
||||
{
|
||||
$id = $this->request->get('id');
|
||||
|
||||
if (!$id) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
try {
|
||||
$result = DiagnosisLogic::detail(['id' => $id]);
|
||||
return $this->data($result);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取字典数据(供小程序调用)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getDict()
|
||||
{
|
||||
$type = $this->request->get('type', '');
|
||||
|
||||
if (!$type) {
|
||||
return $this->fail('字典类型不能为空');
|
||||
}
|
||||
|
||||
try {
|
||||
$data = ConfigLogic::getDictByType($type);
|
||||
return $this->data($data);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class WechatUserService
|
||||
$unionid = $this->unionid;
|
||||
|
||||
$user = User::alias('u')
|
||||
->field('u.id,u.sn,u.mobile,u.nickname,u.avatar,u.mobile,u.is_disable,u.is_new_user')
|
||||
->field('u.id,u.sn,u.mobile,u.nickname,u.avatar,u.mobile,u.is_disable,u.is_new_user,au.openid')
|
||||
->join('user_auth au', 'au.user_id = u.id')
|
||||
->where(function ($query) use ($openid, $unionid) {
|
||||
$query->whereOr(['au.openid' => $openid]);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* 清除微信AccessToken缓存脚本
|
||||
* 用于解决 AccessToken 失效问题
|
||||
*/
|
||||
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// 初始化应用
|
||||
$app = new think\App();
|
||||
$app->initialize();
|
||||
|
||||
echo "=== 清除微信AccessToken缓存 ===\n\n";
|
||||
|
||||
// 获取小程序配置
|
||||
$appId = \app\common\service\ConfigService::get('mnp_setting', 'app_id', '');
|
||||
|
||||
if (empty($appId)) {
|
||||
echo "✗ 错误: 未配置小程序AppID\n";
|
||||
echo "请先在后台配置小程序信息\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "小程序AppID: {$appId}\n";
|
||||
|
||||
// 清除缓存
|
||||
$cacheKey = 'wx_access_token_' . $appId;
|
||||
$oldToken = cache($cacheKey);
|
||||
|
||||
if ($oldToken) {
|
||||
echo "旧Token: {$oldToken}\n";
|
||||
cache($cacheKey, null);
|
||||
echo "✓ 已清除缓存的AccessToken\n";
|
||||
} else {
|
||||
echo "✓ 缓存中没有AccessToken\n";
|
||||
}
|
||||
|
||||
// 尝试重新获取
|
||||
echo "\n=== 尝试重新获取AccessToken ===\n\n";
|
||||
|
||||
$appSecret = \app\common\service\ConfigService::get('mnp_setting', 'app_secret', '');
|
||||
|
||||
if (empty($appSecret)) {
|
||||
echo "✗ 错误: 未配置小程序AppSecret\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appId}&secret={$appSecret}";
|
||||
|
||||
$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);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
echo "HTTP状态码: {$httpCode}\n";
|
||||
echo "响应内容: {$response}\n\n";
|
||||
|
||||
$result = json_decode($response, true);
|
||||
|
||||
if (isset($result['access_token'])) {
|
||||
echo "✓ AccessToken获取成功!\n";
|
||||
echo "Token: {$result['access_token']}\n";
|
||||
echo "有效期: {$result['expires_in']}秒\n";
|
||||
|
||||
// 缓存新Token
|
||||
cache($cacheKey, $result['access_token'], 7000);
|
||||
echo "\n✓ 已缓存新的AccessToken\n";
|
||||
|
||||
echo "\n=== 操作完成 ===\n";
|
||||
echo "现在可以重新尝试生成小程序码了\n";
|
||||
} else {
|
||||
echo "✗ AccessToken获取失败!\n";
|
||||
if (isset($result['errcode'])) {
|
||||
echo "错误码: {$result['errcode']}\n";
|
||||
echo "错误信息: {$result['errmsg']}\n\n";
|
||||
|
||||
// 常见错误提示
|
||||
switch ($result['errcode']) {
|
||||
case 40013:
|
||||
echo "提示: AppID无效,请检查配置\n";
|
||||
break;
|
||||
case 40125:
|
||||
echo "提示: AppSecret无效,请检查配置或重置AppSecret\n";
|
||||
break;
|
||||
case 40164:
|
||||
echo "提示: IP不在白名单中,请在微信公众平台添加服务器IP\n";
|
||||
break;
|
||||
default:
|
||||
echo "请参考微信官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/usability/PublicErrno.html\n";
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user