更新
This commit is contained in:
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user