chore: 添加静态资源文件和版本配置文件
添加大量图片、字体、音频等静态资源文件,包括用户界面图片、模板文件和安装界面素材 添加系统版本配置文件(.version)和常量定义文件(.constant) 更新供应商依赖文件,包括Symfony VarDumper组件和相关许可证文件 添加验证码配置文件(captcha.php)和.gitignore规则
This commit is contained in:
534
app/services/wechat/RoutineServices.php
Normal file
534
app/services/wechat/RoutineServices.php
Normal file
@@ -0,0 +1,534 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\services\BaseServices;
|
||||
use app\dao\wechat\WechatUserDao;
|
||||
use app\services\other\QrcodeServices;
|
||||
use app\services\user\LoginServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\services\user\UserVisitServices;
|
||||
use crmeb\services\CacheService;
|
||||
use crmeb\services\template\Template;
|
||||
use crmeb\services\wechat\MiniProgram;
|
||||
use crmeb\services\wechat\WechatResponse;
|
||||
use think\annotation\Inject;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class RoutineServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatUserDao
|
||||
*/
|
||||
class RoutineServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $sessionKey = 'eb_routine_api_code_';
|
||||
|
||||
/**
|
||||
* @var WechatUserDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatUserDao $dao;
|
||||
|
||||
/**
|
||||
* 处理小程序消息推送
|
||||
* @return mixed
|
||||
*/
|
||||
public function serve()
|
||||
{
|
||||
ob_clean();
|
||||
return MiniProgram::serve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回用户信息的缓存key,返回是否强制绑定手机号
|
||||
* @param $code
|
||||
* @param $spread_uid
|
||||
* @param $spread_code
|
||||
* @return array
|
||||
*/
|
||||
public function authType($code, $spread_uid, $spread_code)
|
||||
{
|
||||
$userInfoConfig = $this->getUserInfoByCode((string)$code);
|
||||
$userInfo['unionId'] = $userInfoConfig['unionid'] ?? '';
|
||||
$userInfo['openid'] = $userInfoConfig['openid'];
|
||||
$userInfo['spread_uid'] = $spread_uid;
|
||||
$userInfo['spread_code'] = $spread_code;
|
||||
$userInfo['session_key'] = $userInfoConfig['session_key'] ?? '';
|
||||
$userInfo['login_type'] = 'routine';
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = $this->routineOauth($userInfo);
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, $userType);
|
||||
$userInfoKey = md5($openid . '_' . time() . '_rouine');
|
||||
CacheService::setTokenBucket($userInfoKey, $createData, 7200);
|
||||
$bindPhone = false;
|
||||
if (sys_config('store_user_mobile') && (($user && $user['phone'] == '') || !$user)) $bindPhone = true;
|
||||
return ['bindPhone' => $bindPhone, 'key' => $userInfoKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据缓存获取token
|
||||
* @param $key
|
||||
* @return array
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function authLogin($key)
|
||||
{
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = CacheService::getTokenBucket($key);
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
//写入用户信息
|
||||
$user = $wechatUserServices->wechatOauthAfter([$openid, $wechatInfo, $spread_uid, $login_type, $userType]);
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序手机号登录
|
||||
* @param $key
|
||||
* @param $phone
|
||||
* @param $spread_uid
|
||||
* @param $spread_code
|
||||
* @param $code
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function phoneLogin($key, $phone, $spread_uid, $spread_code, $code = '')
|
||||
{
|
||||
if ($code == '') {
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = CacheService::get($key);
|
||||
$wechatInfo['phone'] = $phone;
|
||||
$createData = [$openid, $wechatInfo, $spread_uid, $login_type, $userType];
|
||||
} else {
|
||||
$userInfoConfig = $this->getUserInfoByCode((string)$code);
|
||||
$openid = $userInfoConfig['openid'];
|
||||
$wechatInfo['openid'] = $openid;
|
||||
$wechatInfo['unionid'] = $userInfoConfig['unionid'] ?? '';
|
||||
$wechatInfo['spread_uid'] = $spread_uid;
|
||||
$wechatInfo['spread_code'] = $spread_code;
|
||||
$wechatInfo['session_key'] = $userInfoConfig['session_key'] ?? '';
|
||||
$wechatInfo['phone'] = $phone;
|
||||
$createData = [$openid, $wechatInfo, $spread_uid, 'routine', 'routine'];
|
||||
}
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
//写入用户信息
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序绑定手机号
|
||||
* @param $code
|
||||
* @param $iv
|
||||
* @param $encryptedData
|
||||
* @return mixed
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function bindingPhone($code, $iv, $encryptedData)
|
||||
{
|
||||
$userInfoConfig = $this->getUserInfoByCode((string)$code);
|
||||
$userInfo = $this->encryptorUserInfo($userInfoConfig, $iv, $encryptedData);
|
||||
if (!$userInfo || !isset($userInfo['purePhoneNumber'])) {
|
||||
throw new ValidateException('获取用户信息失败');
|
||||
}
|
||||
|
||||
$openid = $userInfoConfig['openid'];
|
||||
$wechatInfo['openid'] = $openid;
|
||||
$wechatInfo['unionid'] = $userInfoConfig['unionid'] ?? '';
|
||||
$wechatInfo['session_key'] = $userInfoConfig['session_key'] ?? '';
|
||||
$wechatInfo['phone'] = $userInfo['purePhoneNumber'];
|
||||
$createData = $this->routineOauth($wechatInfo);
|
||||
$wechatInfo = $createData[1] ?? [];
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
//写入用户信息
|
||||
$user = $wechatUserServices->wechatOauthAfter([$openid, $wechatInfo, 0, 'routine', 'routine']);
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取小程序订阅消息id
|
||||
* @return mixed
|
||||
*/
|
||||
public function temlIds()
|
||||
{
|
||||
$temlIdsName = Config::get('template.stores.subscribe.template_id', []);
|
||||
$temlIdsList = CacheService::handler('TEMPLATE')->remember('TEML_IDS_LISTS', function () use ($temlIdsName) {
|
||||
$temlId = [];
|
||||
$templdata = new Template('subscribe');
|
||||
foreach ($temlIdsName as $key => $item) {
|
||||
$temlId[strtolower($key)] = $templdata->getTempId($item);
|
||||
}
|
||||
return $temlId;
|
||||
});
|
||||
return $temlIdsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序直播列表
|
||||
* @param $pgae
|
||||
* @param $limit
|
||||
* @return mixed
|
||||
*/
|
||||
public function live($page, $limit)
|
||||
{
|
||||
$list = CacheService::get('WECHAT_LIVE_LIST_' . $page . '_' . $limit, function () use ($page, $limit) {
|
||||
$list = MiniProgram::getLiveInfo((int)$page, (int)$limit);
|
||||
foreach ($list as &$item) {
|
||||
$item['_start_time'] = date('m-d H:i', $item['start_time']);
|
||||
}
|
||||
return $list;
|
||||
}, 600) ?: [];
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过code获取授权信息
|
||||
* @param string $code
|
||||
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
|
||||
*/
|
||||
public function getUserInfoByCode(string $code)
|
||||
{
|
||||
if (!$code)
|
||||
throw new ValidateException('授权失败,参数有误');
|
||||
try {
|
||||
$userInfoConfig = MiniProgram::getUserInfo($code);
|
||||
} catch (\Exception $e) {
|
||||
throw new ValidateException('授权失败,请检查您的配置!:' . $e->getMessage() . 'line' . $e->getLine());
|
||||
}
|
||||
|
||||
$userInfoConfig = new WechatResponse($userInfoConfig);
|
||||
if (!isset($userInfoConfig['openid']) || !$userInfoConfig['openid']) {
|
||||
throw new ValidateException('openid获取失败');
|
||||
}
|
||||
return $userInfoConfig->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密获取用户信息
|
||||
* @param $userInfoConfig
|
||||
* @param $iv
|
||||
* @param $encryptedData
|
||||
* @return mixed
|
||||
*/
|
||||
public function encryptorUserInfo($userInfoConfig, $iv, $encryptedData)
|
||||
{
|
||||
if (!$userInfoConfig)
|
||||
throw new ValidateException('授权失败,参数有误');
|
||||
$session_key = $userInfoConfig['session_key'] ?? '';
|
||||
if (!$session_key) {
|
||||
throw new ValidateException('获取session_key失败,参数有误');
|
||||
}
|
||||
try {
|
||||
//解密获取用户信息
|
||||
$userInfo = MiniProgram::decryptData($session_key, $iv, $encryptedData);
|
||||
} catch (\Exception $e) {
|
||||
$userInfo = [];
|
||||
if ($e->getCode() == '-41003') {
|
||||
throw new ValidateException('获取会话密匙失败');
|
||||
}
|
||||
}
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理小程序授权获取用户信息
|
||||
* @param $routine
|
||||
* @return array
|
||||
*/
|
||||
public function routineOauth($routine)
|
||||
{
|
||||
$routineInfo['nickname'] = isset($routine['nickName']) ? filter_emoji($routine['nickName']) : (isset($routine['nickname']) ? filter_emoji($routine['nickname']) : '');//姓名
|
||||
$routineInfo['sex'] = $routine['gender'] ?? '';//性别
|
||||
$routineInfo['language'] = $routine['language'] ?? '';//语言
|
||||
$routineInfo['city'] = $routine['city'] ?? '';//城市
|
||||
$routineInfo['province'] = $routine['province'] ?? "";//省份
|
||||
$routineInfo['country'] = $routine['country'] ?? '';//国家
|
||||
$routineInfo['headimgurl'] = $routine['avatarUrl'] ?? $routine['headimgurl'] ?? sys_config('h5_avatar');//头像
|
||||
$routineInfo['openid'] = $routine['openid'] ?? '';
|
||||
$routineInfo['session_key'] = $routine['session_key'] ?? '';//会话密匙
|
||||
$routineInfo['unionid'] = $routine['unionId'] ?? $routine['unionid'] ?? '';//用户在开放平台的唯一标识符
|
||||
$routineInfo['user_type'] = 'routine';//用户类型
|
||||
$routineInfo['phone'] = $routine['phone'] ?? $routine['purePhoneNumber'] ?? '';
|
||||
$spread_uid = (int)($routine['spread_uid'] ?? 0);//绑定关系uid
|
||||
if (!$spread_uid && isset($routine['spread_code']) && $routine['spread_code']) {
|
||||
//获取是否有扫码进小程序
|
||||
/** @var QrcodeServices $qrcode */
|
||||
$qrcode = app()->make(QrcodeServices::class);
|
||||
$info = $qrcode->get((int)$routine['spread_code']);
|
||||
if ($info) {
|
||||
$spread_uid = $info['third_id'];
|
||||
}
|
||||
}
|
||||
return [$routine['openid'] ?? '', $routineInfo, $spread_uid, $routine['login_type'] ?? 'routine', 'routine'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回信息
|
||||
* @param $user
|
||||
* @param string $userType
|
||||
* @return array
|
||||
*/
|
||||
public function getReturnInfo($user, string $userType = 'routine')
|
||||
{
|
||||
if (!$user || !isset($user['uid']) || !$user['uid']) {
|
||||
throw new ValidateException('获取用户信息失败');
|
||||
}
|
||||
$uid = (int)$user['uid'];
|
||||
$token = $this->createToken($uid, $userType, $user['pwd']);
|
||||
if (!$token) {
|
||||
throw new ValidateException('登录失败!');
|
||||
}
|
||||
/** @var UserVisitServices $visitServices */
|
||||
$visitServices = app()->make(UserVisitServices::class);
|
||||
$visitServices->loginSaveVisit($user);
|
||||
$token['store_user_avatar'] = (int)sys_config('store_user_avatar', 0);
|
||||
$user['store_user_avatar'] = $token['store_user_avatar'];
|
||||
$token['userInfo'] = $user;
|
||||
$token['expires_time'] = $token['params']['exp'] ?? 0;
|
||||
|
||||
// 用户登录成功事件
|
||||
event('user.login', [$uid, app('request')->ip()]);
|
||||
return $token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 小程序授权登录
|
||||
* @param $code
|
||||
* @param $post_cache_key
|
||||
* @param $login_type
|
||||
* @param $spread_spid
|
||||
* @param $spread_code
|
||||
* @param $iv
|
||||
* @param $encryptedData
|
||||
* @return mixed
|
||||
*/
|
||||
public function mp_auth($code, $post_cache_key, $login_type, $spread_spid, $spread_code, $iv, $encryptedData)
|
||||
{
|
||||
$userInfoConfig = $this->getUserInfoByCode((string)$code);
|
||||
$userInfo = $this->encryptorUserInfo($userInfoConfig, $iv, $encryptedData);
|
||||
$userInfo['unionId'] = $userInfoConfig['unionid'] ?? '';
|
||||
$userInfo['openid'] = $userInfoConfig['openid'];
|
||||
$userInfo['spread_uid'] = $spread_spid;
|
||||
$userInfo['spread_code'] = $spread_code;
|
||||
$userInfo['session_key'] = $userInfoConfig['session_key'] ?? '';
|
||||
$userInfo['login_type'] = $login_type;
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = $this->routineOauth($userInfo);
|
||||
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, $userType);
|
||||
if (!$user) {
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
} else {
|
||||
//更新用户信息
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
|
||||
}
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序授权登录
|
||||
* @param $code
|
||||
* @param $spread_uid
|
||||
* @param $spread_code
|
||||
* @param $iv
|
||||
* @param $encryptedData
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function newAuth($code, $spread_uid, $spread_code, $iv, $encryptedData)
|
||||
{
|
||||
$userInfoConfig = $this->getUserInfoByCode((string)$code);
|
||||
$userInfo = $this->encryptorUserInfo($userInfoConfig, $iv, $encryptedData);
|
||||
$userInfo['unionId'] = $userInfoConfig['unionid'] ?? '';
|
||||
$userInfo['openid'] = $userInfoConfig['openid'];
|
||||
$userInfo['spread_uid'] = $spread_uid;
|
||||
$userInfo['spread_code'] = $spread_code;
|
||||
$userInfo['session_key'] = $userInfoConfig['session_key'] ?? '';
|
||||
$userInfo['login_type'] = 'routine';
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = $this->routineOauth($userInfo);
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, $userType);
|
||||
//获取是否强制绑定手机号
|
||||
$storeUserMobile = sys_config('store_user_mobile');
|
||||
if ($storeUserMobile && !$user) {
|
||||
$userInfoKey = md5($openid . '_' . time() . '_rouine');
|
||||
CacheService::setTokenBucket($userInfoKey, $createData, 7200);
|
||||
return ['key' => $userInfoKey];
|
||||
} else if (!$user) {
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
} else {
|
||||
//更新用户信息
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
|
||||
}
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静默授权
|
||||
* @param $spread_code
|
||||
* @param $spread_uid
|
||||
* @return mixed
|
||||
*/
|
||||
public function silenceAuth(string $code, int $spread_code, int $spread_uid, bool $notLogin = false)
|
||||
{
|
||||
$userInfoConfig = $this->getUserInfoByCode($code);
|
||||
$routineInfo = [];
|
||||
$routineInfo['unionid'] = $userInfoConfig['unionid'] ?? '';
|
||||
$routineInfo['openid'] = $userInfoConfig['openid'];
|
||||
$routineInfo['spread_uid'] = $spread_uid;
|
||||
$routineInfo['spread_code'] = $spread_code;
|
||||
$routineInfo['headimgurl'] = sys_config('h5_avatar');
|
||||
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = $this->routineOauth($routineInfo);
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, $userType);
|
||||
if (!$user) {
|
||||
//获取是否强制绑定手机号
|
||||
$storeUserMobile = sys_config('store_user_mobile');
|
||||
if ($notLogin || $storeUserMobile) {
|
||||
$userInfoKey = md5($openid . '_' . time() . '_routine');
|
||||
CacheService::setTokenBucket($userInfoKey, $createData, 7200);
|
||||
return ['auth_login' => 1, 'key' => $userInfoKey];
|
||||
} else {
|
||||
//写入用户信息
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
}
|
||||
} else {
|
||||
//更新用户信息
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], ['spread_uid' => $spread_uid]]);
|
||||
}
|
||||
return $this->getReturnInfo($user);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号登录 静默授权绑定关系
|
||||
* @param $code
|
||||
* @param $spread_uid
|
||||
* @return mixed
|
||||
*/
|
||||
public function silenceAuthBindingPhone($code, $spread_code, $spread_uid, $phone)
|
||||
{
|
||||
$userInfoConfig = $this->getUserInfoByCode((string)$code);
|
||||
$routineInfo = [];
|
||||
$routineInfo['unionid'] = $userInfoConfig['unionid'] ?? '';
|
||||
$openid = $userInfoConfig['openid'];
|
||||
$routineInfo['openid'] = $openid;
|
||||
$routineInfo['spread_uid'] = $spread_uid;
|
||||
$routineInfo['spread_code'] = $spread_code;
|
||||
$routineInfo['headimgurl'] = sys_config('h5_avatar');
|
||||
$routineInfo['phone'] = $phone;
|
||||
$createData = $this->routineOauth($routineInfo);
|
||||
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
//写入用户信息
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号登录 静默授权绑定关系
|
||||
* @param $code
|
||||
* @param $iv
|
||||
* @param $encryptedData
|
||||
* @param $spread_code
|
||||
* @param $spread_uid
|
||||
* @param $key
|
||||
* @return array
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function authBindingPhone($code, $iv, $encryptedData, $spread_code, $spread_uid, $key = '')
|
||||
{
|
||||
$wechatInfo = [];
|
||||
$userType = $login_type = 'routine';
|
||||
if ($key) {
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = CacheService::getTokenBucket($key);
|
||||
}
|
||||
$userInfoConfig = $this->getUserInfoByCode((string)$code);
|
||||
$userInfo = $this->encryptorUserInfo($userInfoConfig, $iv, $encryptedData);
|
||||
if (!$userInfo || !isset($userInfo['purePhoneNumber'])) {
|
||||
throw new ValidateException('获取用户信息失败');
|
||||
}
|
||||
$openid = $userInfoConfig['openid'];
|
||||
$wechatInfo['openid'] = $openid;
|
||||
$wechatInfo['unionid'] = $userInfoConfig['unionid'] ?? '';
|
||||
$wechatInfo['spread_uid'] = $spread_uid;
|
||||
$wechatInfo['spread_code'] = $spread_code;
|
||||
$wechatInfo['session_key'] = $userInfoConfig['session_key'] ?? '';
|
||||
$wechatInfo['phone'] = $userInfo['purePhoneNumber'];
|
||||
$createData = $this->routineOauth($wechatInfo);
|
||||
$wechatInfo = $createData[1] ?? [];
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
//写入用户信息
|
||||
$user = $wechatUserServices->wechatOauthAfter([$openid, $wechatInfo, $spread_uid, $login_type, $userType]);
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* @param $uid
|
||||
* @param array $data
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function updateUserInfo($uid, array $data)
|
||||
{
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$user = $userServices->getUserInfo($uid);
|
||||
if (!$user) {
|
||||
throw new ValidateException('数据不存在');
|
||||
}
|
||||
$userInfo = [];
|
||||
$userInfo['nickname'] = filter_emoji($data['nickName'] ?? '');//姓名
|
||||
$userInfo['sex'] = $data['gender'] ?? '';//性别
|
||||
$userInfo['language'] = $data['language'] ?? '';//语言
|
||||
$userInfo['city'] = $data['city'] ?? '';//城市
|
||||
$userInfo['province'] = $data['province'] ?? '';//省份
|
||||
$userInfo['country'] = $data['country'] ?? '';//国家
|
||||
$userInfo['headimgurl'] = $data['avatarUrl'] ?? '';//头像
|
||||
$userInfo['is_complete'] = 1;
|
||||
/** @var LoginServices $loginService */
|
||||
$loginService = app()->make(LoginServices::class);
|
||||
$loginService->updateUserInfo($userInfo, $user);
|
||||
//更新用户信息
|
||||
if (!$this->dao->update(['uid' => $user['uid'], 'user_type' => 'routine'], $userInfo)) {
|
||||
throw new ValidateException('更新失败');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
278
app/services/wechat/WechatCardServices.php
Normal file
278
app/services/wechat/WechatCardServices.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
|
||||
use app\dao\wechat\WechatCardDao;
|
||||
use app\services\BaseServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\services\DownloadImageService;
|
||||
use crmeb\services\FormBuilder;
|
||||
use crmeb\services\SystemConfigService;
|
||||
use crmeb\services\wechat\OfficialAccount;
|
||||
use think\annotation\Inject;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 微信卡券
|
||||
* Class WechatCardServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatCardDao
|
||||
*/
|
||||
class WechatCardServices extends BaseServices
|
||||
{
|
||||
protected array $code_type = [
|
||||
'CODE_TYPE_NONE' => '文本',
|
||||
'CODE_TYPE_BARCODE' => '一维码',
|
||||
'CODE_TYPE_QRCODE' => '二维码',
|
||||
'CODE_TYPE_ONLY_QRCODE' => '仅显示二维码',
|
||||
'CODE_TYPE_ONLY_BARCODE' => '仅显示一维码',
|
||||
'CODE_TYPE_NONE' => '不显示任何码型'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var FormBuilder
|
||||
*/
|
||||
#[Inject]
|
||||
protected FormBuilder $builder;
|
||||
|
||||
/**
|
||||
* @var WechatCardDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatCardDao $dao;
|
||||
|
||||
|
||||
/**
|
||||
* 获取微信卡券列表
|
||||
* @param array $where 查询条件
|
||||
* @return array
|
||||
*/
|
||||
public function getList(array $where)
|
||||
{
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$count = $this->dao->count($where);
|
||||
$list = $this->dao->getList($where, $page, $limit);
|
||||
return compact('count', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信会员卡信息
|
||||
* @return array
|
||||
*/
|
||||
public function getInfo()
|
||||
{
|
||||
if (!sys_config('wechat_appid') || !sys_config('wechat_appsecret')) {
|
||||
throw new AdminException('请先配置公众号的appid、appSecret等参数,且需要开通微信卡券功能');
|
||||
}
|
||||
$data = [];
|
||||
try {
|
||||
$data['color'] = OfficialAccount::getCardColors()['colors'] ?? [];
|
||||
} catch (\Throwable $e) {
|
||||
$data['color'] = [];
|
||||
}
|
||||
if (!$data['color']) {
|
||||
throw new AdminException('请先在公众号后台开通微信卡券功能');
|
||||
}
|
||||
$info = $this->dao->getOne(['card_type' => 'member_card', 'status' => 1, 'is_del' => 0]);
|
||||
$data['info'] = ['custom_cell' => []];
|
||||
$data['selet'] = 0;
|
||||
if ($info) {
|
||||
$info = $info->toArray();
|
||||
$info['custom_cell'] = $info['especial']['custom_cell'] ?? [];
|
||||
$data['info'] = $info;
|
||||
$data['selet'] = $info['background_pic_url'] ? 1 : 0;
|
||||
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信卡券表单
|
||||
* @param array $formData 表单数据
|
||||
* @return array
|
||||
*/
|
||||
public function createForm($formData)
|
||||
{
|
||||
$f[] = $this->builder->input('brand_name', '商户名称', $formData['brand_name'] ?? '')->required('请填写名称');
|
||||
$f[] = $this->builder->input('title', '卡券名称', $formData['title'] ?? '')->required('请填写卡券名称');
|
||||
$f[] = $this->builder->input('service_phone', '电话', $formData['service_phone'] ?? '')->required('请填写电话');
|
||||
$f[] = $this->builder->select('code_type', 'CODE展示类型', $formData['code_type'])->setOptions(FormBuilder::setOptions($this->code_type))->multiple(true)->required('请选择CODE展示类型');
|
||||
$f[] = $this->builder->input('color', '卡券颜色', $formData['color'] ?? '')->required('请填写卡券颜色');
|
||||
$f[] = $this->builder->frameImage('logo_url', 'LOGO', $this->url('admin/widget.images/index', ['fodder' => 'logo_url'], true), $formData['logo_url'] ?? '')->info('建议300*300')->icon('ios-add')->width('960px')->height('550px')->modal(['footer-hide' => true]);
|
||||
$f[] = $this->builder->frameImage('background_pic_url', '背景图', $this->url('admin/widget.images/index', ['fodder' => 'background_pic_url'], true), $formData['background_pic_url'] ?? '')->info('建议1000*600')->icon('ios-add')->width('960px')->height('550px')->modal(['footer-hide' => true]);
|
||||
$f[] = $this->builder->input('notice', '提示', $formData['notice'] ?? '')->required('请填写提示信息');
|
||||
$f[] = $this->builder->input('description', '描述', $formData['description'] ?? '')->required('请填写描述');
|
||||
$f[] = $this->builder->textarea('prerogative', '卡券特权说明', $formData['notice'] ?? '')->info('会员卡特权说明,限制1024汉字')->required('请填写卡券特权说明');
|
||||
return create_form('微信卡券添加', $f, $this->url('/wechat/card/0'), 'post');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加|编辑微信卡券
|
||||
* @param int $id
|
||||
* @param array $data
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function save(array $data)
|
||||
{
|
||||
$card = $this->dao->getOne(['card_type' => 'member_card', 'status' => 1, 'is_del' => 0]);
|
||||
if ($card) {
|
||||
[$cardInfo, $especial] = $this->wxCreate($data, $card['card_id']);
|
||||
unset($data['custom_cell']);
|
||||
$data['especial'] = $especial;
|
||||
if (!$this->dao->update($card['id'], $data)) {
|
||||
throw new AdminException('修改失败');
|
||||
}
|
||||
} else {
|
||||
[$cardInfo, $especial] = $this->wxCreate($data);
|
||||
$this->activateUserForm($cardInfo['card_id']);
|
||||
unset($data['custom_cell']);
|
||||
$data['card_id'] = $cardInfo['card_id'];
|
||||
$data['especial'] = $especial;
|
||||
if (!$this->dao->save($data)) {
|
||||
throw new AdminException('添加失败');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信卡券添加|编辑
|
||||
* @param array $card
|
||||
* @param string $card_id
|
||||
* @param string $card_type
|
||||
* @return mixed
|
||||
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
|
||||
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function wxCreate(array $card, string $card_id = '', string $card_type = 'member_card')
|
||||
{
|
||||
if (!sys_config('wechat_appid') || !sys_config('wechat_appsecret')) {
|
||||
throw new AdminException('请先配置公众号的appid、appSecret等参数,且需要开通微信卡券功能');
|
||||
}
|
||||
$baseUrl = sys_config('site_url');
|
||||
try {
|
||||
$logo = root_path() . 'public' . app()->make(DownloadImageService::class)->downloadImage($card['logo_url'])['path'];
|
||||
$background = $card['background_pic_url'] ? (root_path() . 'public' . app()->make(DownloadImageService::class)->downloadImage($card['background_pic_url'])['path']) : '';
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('添加会员卡券封面图出错误,原因:' . $e->getMessage());
|
||||
$logo = root_path() . 'public' . $card['logo_url'];
|
||||
$background = root_path() . 'public' . $card['background_pic_url'];
|
||||
}
|
||||
$base_info = [
|
||||
'logo_url' => OfficialAccount::temporaryUpload($logo)['media_id'],
|
||||
'brand_name' => $card['brand_name'],
|
||||
'code_type' => 'CODE_TYPE_NONE',
|
||||
'title' => $card['title'],
|
||||
'color' => $card['color'] ?: 'Color010',
|
||||
'notice' => $card['notice'],
|
||||
'service_phone' => $card['service_phone'],
|
||||
'description' => $card['description'],
|
||||
'use_custom_code' => false,
|
||||
'can_share' => true,
|
||||
'can_give_friend' => false,
|
||||
'get_limit' => 1,
|
||||
'date_info' => ['type' => 'DATE_TYPE_PERMANENT'],
|
||||
'sku' => ['quantity' => 50000000],
|
||||
'promotion_url' => $baseUrl . '/pages/users/user_spread_user/index',
|
||||
'promotion_url_name' => '推荐给朋友',
|
||||
'promotion_url_sub_title' => ''
|
||||
];
|
||||
if ($card['center_title']) {
|
||||
$base_info = array_merge($base_info, [
|
||||
'center_title' => $card['center_title'],
|
||||
'center_sub_title ' => $card['center_sub_title'],
|
||||
'center_url' => $card['center_url'],
|
||||
]);
|
||||
}
|
||||
$integral = SystemConfigService::more(['integral_max_num', 'integral_ratio', 'order_give_integral']);
|
||||
$especial = [
|
||||
// 'name' => $card['title'],//入口名称
|
||||
// 'tips' => $card['description'],//入口提示语
|
||||
// 'url' => $baseUrl,//入口跳转地址
|
||||
'supply_bonus' => true,//显示积分
|
||||
'bonus_rule' => [
|
||||
'cost_money_unit' => 100,//消费金额。以分为单位
|
||||
'increase_bonus' => $integral['order_give_integral'],//对应增加的积分
|
||||
'max_increase_bonus' => $integral['integral_max_num'],//用户单次可获取的积分上限
|
||||
'cost_bonus_unit' => 1,//每使用X积分
|
||||
'reduce_money' => bcmul($integral['integral_ratio'], '100', 0),//抵扣xx元,(这里以分为单位)
|
||||
],
|
||||
'supply_balance' => false,//是否支持充值
|
||||
// 'activate_url' => $baseUrl,
|
||||
'bonus_url' => $baseUrl . '/pages/users/user_integral/index',//积分详情跳转链接
|
||||
'prerogative' => $card['prerogative'],
|
||||
// 'auto_activate' => false,
|
||||
'wx_activate' => true,
|
||||
'background_pic_url' => $background ? OfficialAccount::temporaryUpload($background)['media_id'] : '',
|
||||
'custom_field2' => [
|
||||
'name_type' => 'FIELD_NAME_TYPE_TIMS',//消费次数
|
||||
'url' => $baseUrl . '/pages/users/order_list/index'
|
||||
],
|
||||
'custom_field3' => [
|
||||
'name_type' => 'FIELD_NAME_TYPE_COUPON',//优惠券
|
||||
'url' => $baseUrl . '/pages/users/user_coupon/index'
|
||||
],
|
||||
];
|
||||
if ($card['custom_cell']) {
|
||||
$cell_data = [];
|
||||
$i = 1;
|
||||
foreach ($card['custom_cell'] as $item) {
|
||||
$cell_data['custom_cell' . $i] = $item;
|
||||
$i++;
|
||||
}
|
||||
$especial = array_merge($especial, $cell_data);
|
||||
}
|
||||
@unlink($logo);
|
||||
@unlink($background);
|
||||
if ($card_id) {
|
||||
unset($especial['bonus_rule']);
|
||||
unset($base_info['brand_name'], $base_info['sku'], $base_info['use_custom_code']);
|
||||
return [OfficialAccount::updateCard($card_id, $card_type, $base_info, $especial), array_merge($especial, ['custom_cell' => $card['custom_cell']])];
|
||||
} else {
|
||||
return [OfficialAccount::createCard($card_type, $base_info, $especial), array_merge($especial, ['custom_cell' => $card['custom_cell']])];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建会员卡激活表单
|
||||
* @param string $cardId
|
||||
* @return \EasyWeChat\Support\Collection
|
||||
*/
|
||||
public function activateUserForm(string $cardId)
|
||||
{
|
||||
$requireForm = [
|
||||
'required_form' => [
|
||||
'can_modify' => false,
|
||||
'common_field_id_list' => [
|
||||
'USER_FORM_INFO_FLAG_MOBILE'
|
||||
]
|
||||
]
|
||||
];
|
||||
$optionFrom = [
|
||||
'optional_form' => [
|
||||
'can_modify' => false,
|
||||
'common_field_id_list' => [
|
||||
'USER_FORM_INFO_FLAG_BIRTHDAY'
|
||||
]
|
||||
]
|
||||
];
|
||||
return OfficialAccount::cardActivateUserForm($cardId, $requireForm, $optionFrom);
|
||||
}
|
||||
|
||||
}
|
||||
34
app/services/wechat/WechatKeyServices.php
Normal file
34
app/services/wechat/WechatKeyServices.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
|
||||
use app\dao\wechat\WechatKeyDao;
|
||||
use app\services\BaseServices;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
* 微信菜单
|
||||
* Class WechatMenuServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatKeyDao
|
||||
*/
|
||||
class WechatKeyServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WechatKeyDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatKeyDao $dao;
|
||||
|
||||
}
|
||||
34
app/services/wechat/WechatMediaServices.php
Normal file
34
app/services/wechat/WechatMediaServices.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
|
||||
use app\dao\wechat\WechatMediaDao;
|
||||
use app\services\BaseServices;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
* Class WechatMediaServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatMediaDao
|
||||
*/
|
||||
class WechatMediaServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WechatMediaDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatMediaDao $dao;
|
||||
|
||||
|
||||
}
|
||||
79
app/services/wechat/WechatMenuServices.php
Normal file
79
app/services/wechat/WechatMenuServices.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
|
||||
use app\dao\wechat\WechatMenuDao;
|
||||
use app\services\BaseServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\services\wechat\ErrorMessage;
|
||||
use crmeb\services\wechat\OfficialAccount;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
* 微信菜单
|
||||
* Class WechatMenuServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatMenuDao
|
||||
*/
|
||||
class WechatMenuServices extends BaseServices
|
||||
{
|
||||
/**
|
||||
* @var WechatMenuDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatMenuDao $dao;
|
||||
|
||||
/**
|
||||
* 获取微信菜单
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getWechatMenu()
|
||||
{
|
||||
$menus = $this->dao->value(['key' => 'wechat_menus'], 'result');
|
||||
return $menus ? json_decode($menus, true) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存微信菜单
|
||||
* @param array $buttons
|
||||
* @return bool
|
||||
*/
|
||||
public function saveMenu(array $buttons)
|
||||
{
|
||||
try {
|
||||
$res = OfficialAccount::instance()->user()->menuCreate($buttons);
|
||||
if ($res['errcode'] !== 0) {
|
||||
throw new AdminException($res['errmsg']);
|
||||
}
|
||||
if ($this->dao->count(['key' => 'wechat_menus'])) {
|
||||
$this->dao->update('wechat_menus', ['result' => json_encode($buttons), 'add_time' => time()], 'key');
|
||||
} else {
|
||||
$this->dao->save(['key' => 'wechat_menus', 'result' => json_encode($buttons), 'add_time' => time()]);
|
||||
}
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
if (strstr($e->getMessage(), 'Request AccessToken fail. response')) {
|
||||
$msgData = str_replace('Request AccessToken fail. response: ', '', $e->getMessage());
|
||||
$msgData = json_decode($msgData, true);
|
||||
$errcode = $msgData['errcode'] ?? 0;
|
||||
if ($errcode == 40164) {
|
||||
throw new AdminException('您得ip不再白名单中,请前往腾讯微信公众平台添加ip白名单');
|
||||
}
|
||||
}
|
||||
if (strstr($e->getMessage(), 'invalid weapp appid')) {
|
||||
throw new AdminException('您填写得appid无效,请检查');
|
||||
}
|
||||
throw new AdminException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
101
app/services/wechat/WechatMessageServices.php
Normal file
101
app/services/wechat/WechatMessageServices.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
|
||||
use app\dao\wechat\WechatMessageDao;
|
||||
use app\services\BaseServices;
|
||||
use think\annotation\Inject;
|
||||
use think\exception\ValidateException;
|
||||
use crmeb\services\CacheService;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class WechatMenuServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatMessageDao
|
||||
*/
|
||||
class WechatMessageServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WechatMessageDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatMessageDao $dao;
|
||||
|
||||
/**
|
||||
* @param $result
|
||||
* @param $openid
|
||||
* @param $type
|
||||
* @return \think\Model
|
||||
*/
|
||||
public function setMessage($result, $openid, $type)
|
||||
{
|
||||
if (is_object($result) || is_array($result)) $result = json_encode($result);
|
||||
$add_time = time();
|
||||
$data = compact('result', 'openid', 'type', 'add_time');
|
||||
return $this->dao->save($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $result
|
||||
* @param $openid
|
||||
* @param $type
|
||||
* @param $unique
|
||||
* @param int $cacheTime
|
||||
* @return bool|\think\Model
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public function setOnceMessage($result, $openid, $type, $unique, $cacheTime = 172800)
|
||||
{
|
||||
$cacheName = 'wechat_message_' . $type . '_' . $unique;
|
||||
if (CacheService::has($cacheName)) return true;
|
||||
$res = $this->setMessage($result, $openid, $type);
|
||||
if ($res) CacheService::set($cacheName, 1, $cacheTime);
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信消息前置操作
|
||||
* @param $message
|
||||
* @param $spread_uid
|
||||
*/
|
||||
public function wechatMessageBefore($message, $spread_uid = 0)
|
||||
{
|
||||
//是否开启
|
||||
if (sys_config('create_wechat_user', 1)) {
|
||||
try {
|
||||
/** @var WechatUserServices $wechatUser */
|
||||
$wechatUser = app()->make(WechatUserServices::class);
|
||||
$wechatUser->saveUser($message['FromUserName'], $spread_uid);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('关注公众号生成用户失败,原因:' . $e->getMessage() . $e->getFile() . $e->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
$msgType = $message['MsgType'] ?? '';
|
||||
$event = isset($message['Event']) ?
|
||||
$msgType . (
|
||||
$message['Event'] == 'subscribe' && isset($message['EventKey']) ? '_scan' : ''
|
||||
) . '_' . $message['Event'] : $msgType;
|
||||
$result = json_encode($message);
|
||||
$openid = $message['FromUserName'];
|
||||
$type = strtolower($event);
|
||||
$add_time = time();
|
||||
if (!$this->dao->save(compact('result', 'openid', 'type', 'add_time'))) {
|
||||
throw new ValidateException('更新信息失败');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
128
app/services/wechat/WechatNewsCategoryServices.php
Normal file
128
app/services/wechat/WechatNewsCategoryServices.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\services\BaseServices;
|
||||
use app\dao\wechat\WechatNewsCategoryDao;
|
||||
use app\services\article\ArticleServices;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class UserWechatuserServices
|
||||
* @package app\services\user
|
||||
* @mixin WechatNewsCategoryDao
|
||||
*/
|
||||
class WechatNewsCategoryServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WechatNewsCategoryDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatNewsCategoryDao $dao;
|
||||
|
||||
/**
|
||||
* 获取配置分类
|
||||
* @param array $where
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($where = array())
|
||||
{
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$model = $this->dao->getNewCtae($where);
|
||||
$count = $model->count();
|
||||
$list = $model->page($page, $limit)
|
||||
->select()
|
||||
->each(function ($item) {
|
||||
/** @var ArticleServices $services */
|
||||
$services = app()->make(ArticleServices::class);
|
||||
$new = $services->articleList($item['new_id']);
|
||||
if ($new) $new = $new->toArray();
|
||||
$item['new'] = $new;
|
||||
});
|
||||
return compact('count', 'list');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一条图文
|
||||
* @param int $id
|
||||
* @return array|false|\PDOStatement|string|\think\Model
|
||||
*/
|
||||
public function getWechatNewsItem($id = 0)
|
||||
{
|
||||
if (!$id) return [];
|
||||
$list = $this->dao->getOne(['id' => $id, 'status' => 1], 'cate_name as title,new_id');
|
||||
if ($list) {
|
||||
$list = $list->toArray();
|
||||
/** @var ArticleServices $services */
|
||||
$services = app()->make(ArticleServices::class);
|
||||
$new = $services->articleList($list['new_id']);
|
||||
if ($new) $new = $new->toArray();
|
||||
$list['new'] = $new;
|
||||
}
|
||||
return $list;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送客服消息选择文章列表
|
||||
* @param $where
|
||||
* @return array
|
||||
*/
|
||||
public function list($where)
|
||||
{
|
||||
$list = $this->dao->getNewCtae($where)
|
||||
->page((int)$where['page'], (int)$where['limit'])
|
||||
->select()
|
||||
->each(function ($item) {
|
||||
/** @var ArticleServices $services */
|
||||
$services = app()->make(ArticleServices::class);
|
||||
$item['new'] = $services->articleList($item['new_id']);
|
||||
});
|
||||
return ['list' => $list];
|
||||
}
|
||||
|
||||
/**整理图文资源
|
||||
* @param $wechatNews
|
||||
* @return bool
|
||||
*/
|
||||
public function wechatPush($wechatNews)
|
||||
{
|
||||
/** @var WechatReplyServices $services */
|
||||
$services = app()->make(WechatReplyServices::class);
|
||||
return $services->tidyNews($wechatNews);
|
||||
}
|
||||
|
||||
/**发送的用户
|
||||
* @param $user_ids
|
||||
* @param $column
|
||||
* @param $key
|
||||
* @return array
|
||||
*/
|
||||
public function getWechatUser($user_ids, $column, $key)
|
||||
{
|
||||
/** @var WechatUserServices $services */
|
||||
$services = app()->make(WechatUserServices::class);
|
||||
return $services->getColumnUser($user_ids, $column, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章id
|
||||
* @return array
|
||||
*/
|
||||
public function getNewIds()
|
||||
{
|
||||
return $this->dao->getColumn([], 'new_id');
|
||||
}
|
||||
}
|
||||
95
app/services/wechat/WechatQrcodeCateServices.php
Normal file
95
app/services/wechat/WechatQrcodeCateServices.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\dao\wechat\WechatQrcodeCateDao;
|
||||
use app\services\BaseServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\services\FormBuilder as Form;
|
||||
use think\annotation\Inject;
|
||||
use think\facade\Route as Url;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class WechatQrcodeCateServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatQrcodeCateDao
|
||||
* @method getCateList($where) 分组列表
|
||||
*/
|
||||
class WechatQrcodeCateServices extends BaseServices
|
||||
{
|
||||
/**
|
||||
* @var WechatQrcodeCateDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatQrcodeCateDao $dao;
|
||||
|
||||
/**
|
||||
* 添加编辑分组表单
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @throws \FormBuilder\Exception\FormBuilderException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function createForm($id = 0)
|
||||
{
|
||||
$info = [];
|
||||
$title = $id ? '编辑分组' : '添加分组';
|
||||
if ($id) {
|
||||
$info = $this->dao->get($id);
|
||||
}
|
||||
$f[] = Form::hidden('id', $id);
|
||||
$f[] = Form::input('cate_name', '分组名称:', $info['cate_name'] ?? '')->maxlength(30)->required();
|
||||
return create_form($title, $f, Url::buildUrl('/app/wechat_qrcode/cate/save'), 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param $data
|
||||
* @return bool
|
||||
*/
|
||||
public function saveData($data)
|
||||
{
|
||||
$id = $data['id'];
|
||||
$data['add_time'] = time();
|
||||
if ($id) {
|
||||
unset($data['id']);
|
||||
$res = $this->dao->update($id, $data);
|
||||
} else {
|
||||
$res = $this->dao->save($data);
|
||||
}
|
||||
if (!$res) throw new AdminException('保存失败');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分组
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public function delCate(int $id = 0)
|
||||
{
|
||||
if (!$id) throw new AdminException('参数错误');
|
||||
if ($this->dao->get($id)) {
|
||||
$qrcodeServices = app()->make(WechatQrcodeServices::class);
|
||||
if ($qrcodeServices->count(['cate_id' => $id, 'is_del'=>0])) {
|
||||
throw new AdminException('该分组下有渠道码,暂不能删除');
|
||||
}
|
||||
$res = $this->dao->update($id, ['is_del' => 1]);
|
||||
if (!$res) throw new AdminException('删除失败');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
145
app/services/wechat/WechatQrcodeRecordServices.php
Normal file
145
app/services/wechat/WechatQrcodeRecordServices.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\dao\wechat\WechatQrcodeRecordDao;
|
||||
use app\services\BaseServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class WechatQrcodeRecordServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatQrcodeRecordDao
|
||||
*/
|
||||
class WechatQrcodeRecordServices extends BaseServices
|
||||
{
|
||||
/**
|
||||
* @var WechatQrcodeRecordDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatQrcodeRecordDao $dao;
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param $qid
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function userList($qid)
|
||||
{
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$where['qid'] = $qid;
|
||||
$list = $this->dao->getList($where, $page, $limit, 1);
|
||||
foreach ($list as $k => &$v) {
|
||||
if ($v['user'] === null) {
|
||||
$v['user'] = [
|
||||
'uid' => $v['uid'],
|
||||
'avatar' => sys_config('h5_avatar'),
|
||||
'nickname' => '用户已注销',
|
||||
];
|
||||
}
|
||||
}
|
||||
$count = $this->dao->getDistinctCount($where, 'uid');
|
||||
return compact('list', 'count');
|
||||
}
|
||||
|
||||
/**
|
||||
* 渠道码统计
|
||||
* @param $where
|
||||
* @param $time
|
||||
* @return mixed
|
||||
*/
|
||||
public function qrcodeStatistic($where, $time)
|
||||
{
|
||||
$data['all_follow'] = $this->dao->count($where + ['is_follow' => 1]);
|
||||
$data['all_scan'] = $this->dao->count($where);
|
||||
$data['y_follow'] = $this->dao->count($where + ['is_follow' => 1, 'time' => 'yesterday']);
|
||||
$data['y_scan'] = $this->dao->count($where + ['time' => 'yesterday']);
|
||||
$data['trend'] = $this->getTrend($where['qid'], explode('-', $time));
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额趋势
|
||||
* @param $qid
|
||||
* @param $time
|
||||
* @return array
|
||||
*/
|
||||
public function getTrend($qid, $time)
|
||||
{
|
||||
if (count($time) != 2) throw new AdminException('参数错误');
|
||||
$dayCount = (strtotime($time[1]) - strtotime($time[0])) / 86400 + 1;
|
||||
$data = [];
|
||||
if ($dayCount == 1) {
|
||||
$data = $this->trend($qid, $time, 0);
|
||||
} elseif ($dayCount > 1 && $dayCount <= 31) {
|
||||
$data = $this->trend($qid, $time, 1);
|
||||
} elseif ($dayCount > 31 && $dayCount <= 92) {
|
||||
$data = $this->trend($qid, $time, 3);
|
||||
} elseif ($dayCount > 92) {
|
||||
$data = $this->trend($qid, $time, 30);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额趋势
|
||||
* @param $qid
|
||||
* @param $time
|
||||
* @param $num
|
||||
* @param false $excel
|
||||
* @return array
|
||||
*/
|
||||
public function trend($qid, $time, $num)
|
||||
{
|
||||
if ($num == 0) {
|
||||
$xAxis = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
|
||||
$timeType = '%H';
|
||||
} elseif ($num != 0) {
|
||||
$dt_start = strtotime($time[0]);
|
||||
$dt_end = strtotime($time[1]);
|
||||
while ($dt_start <= $dt_end) {
|
||||
if ($num == 30) {
|
||||
$xAxis[] = date('Y-m', $dt_start);
|
||||
$dt_start = strtotime("+1 month", $dt_start);
|
||||
$timeType = '%Y-%m';
|
||||
} else {
|
||||
$xAxis[] = date('m-d', $dt_start);
|
||||
$dt_start = strtotime("+$num day", $dt_start);
|
||||
$timeType = '%m-%d';
|
||||
}
|
||||
}
|
||||
}
|
||||
$time[1] = date("Y-m-d", strtotime("+1 day", strtotime($time[1])));
|
||||
$follow = array_column($this->dao->getRecordTrend($qid, $time, $timeType, 'add_time', 'count(uid)', 'yes'), 'num', 'days');
|
||||
$scan = array_column($this->dao->getRecordTrend($qid, $time, $timeType, 'add_time', 'count(uid)', 'no'), 'num', 'days');
|
||||
$data = $series = [];
|
||||
foreach ($xAxis as $item) {
|
||||
$data['新增关注'][] = isset($follow[$item]) ? floatval($follow[$item]) : 0;
|
||||
$data['新增参与'][] = isset($scan[$item]) ? floatval($scan[$item]) : 0;
|
||||
}
|
||||
foreach ($data as $key => $item) {
|
||||
$series[] = [
|
||||
'name' => $key,
|
||||
'data' => $item,
|
||||
'type' => 'line',
|
||||
];
|
||||
}
|
||||
return compact('xAxis', 'series');
|
||||
}
|
||||
}
|
||||
313
app/services/wechat/WechatQrcodeServices.php
Normal file
313
app/services/wechat/WechatQrcodeServices.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\dao\wechat\WechatQrcodeDao;
|
||||
use app\services\BaseServices;
|
||||
use app\services\other\QrcodeServices;
|
||||
use crmeb\services\DownloadImageService;
|
||||
use crmeb\services\UploadService;
|
||||
use app\services\system\attachment\SystemAttachmentServices;
|
||||
use app\services\user\label\UserLabelRelationServices;
|
||||
use app\services\user\label\UserLabelServices;
|
||||
use app\services\user\UserServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\services\wechat\message\Image;
|
||||
use crmeb\services\wechat\message\News;
|
||||
use crmeb\services\wechat\message\Text;
|
||||
use crmeb\services\wechat\message\Voice;
|
||||
use crmeb\services\wechat\Messages;
|
||||
use think\facade\Log;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class WechatQrcodeServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatQrcodeDao
|
||||
*/
|
||||
class WechatQrcodeServices extends BaseServices
|
||||
{
|
||||
/**
|
||||
* @var WechatQrcodeDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatQrcodeDao $dao;
|
||||
|
||||
/**
|
||||
* 获取渠道码列表
|
||||
* @param $where
|
||||
* @return array
|
||||
*/
|
||||
public function qrcodeList(array $where)
|
||||
{
|
||||
/** @var UserLabelServices $userLabel */
|
||||
$userLabel = app()->make(UserLabelServices::class);
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->getList($where, $page, $limit);
|
||||
foreach ($list as &$item) {
|
||||
$item['y_follow'] = $item['record']['number'] ?? 0;
|
||||
$item['stop'] = $item['end_time'] ? $item['end_time'] > time() ? 1 : -1 : 0;
|
||||
$item['label_name'] = $userLabel->getColumn([['id', 'in', $item['label_id']]], 'label_name');
|
||||
$item['end_time'] = date('Y-m-d H:i:s', $item['end_time']);
|
||||
unset($item['record']);
|
||||
}
|
||||
$count = $this->dao->count($where);
|
||||
return compact('list', 'count');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
* @param $id
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function qrcodeInfo($id)
|
||||
{
|
||||
$info = $this->dao->get($id);
|
||||
if ($info) {
|
||||
$info = $info->toArray();
|
||||
} else {
|
||||
throw new AdminException('数据不存在');
|
||||
}
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$info['label_id'] = explode(',', $info['label_id']);
|
||||
foreach ($info['label_id'] as &$item) {
|
||||
$item = (int)$item;
|
||||
}
|
||||
/** @var UserLabelServices $userLabelServices */
|
||||
$userLabelServices = app()->make(UserLabelServices::class);
|
||||
$info['label_id'] = $userLabelServices->getLabelList(['ids' => $info['label_id']], ['id', 'label_name']);
|
||||
$info['time'] = $info['continue_time'];
|
||||
$info['content'] = json_decode($info['content'], true);
|
||||
$info['data'] = json_decode($info['data'], true);
|
||||
$info['avatar'] = $userService->value(['uid' => $info['uid']], 'avatar');
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存渠道码数据
|
||||
* @param $id
|
||||
* @param $data
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function saveQrcode($id, $data)
|
||||
{
|
||||
$data['label_id'] = implode(',', $data['label_id']);
|
||||
$data['add_time'] = time();
|
||||
$data['continue_time'] = $data['time'];
|
||||
$data['end_time'] = $data['time'] ? $data['add_time'] + ($data['time'] * 86400) : 0;
|
||||
/** @var WechatReplyServices $replyServices */
|
||||
$replyServices = app()->make(WechatReplyServices::class);
|
||||
$type = $data['type'];
|
||||
if ($data['type'] == 'url') $type = 'text';
|
||||
$content = $data['content'];
|
||||
if ($data['type'] == 'news') $content = $data['content']['list'] ?? [];
|
||||
$method = 'tidy' . ucfirst($type);
|
||||
if ($type == 'image') {
|
||||
/** @var DownloadImageService $download */
|
||||
$download = app()->make(DownloadImageService::class);
|
||||
try {
|
||||
$path = $download->thumb(true)->downloadImage($content['src'])['path'];
|
||||
$content['src'] = $path;
|
||||
$data['data'] = $replyServices->{$method}($content, 0);
|
||||
@unlink(root_path() . 'public' . $path);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('获取图片错误,原因:' . $e->getMessage());
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
} else {
|
||||
$data['data'] = $replyServices->{$method}($content, 0);
|
||||
}
|
||||
$data['content'] = json_encode($data['content']);
|
||||
$data['data'] = json_encode($data['data']);
|
||||
$this->transaction(function () use ($id, $data) {
|
||||
if ($id) {
|
||||
$info = $this->dao->get($id);
|
||||
if (!$info) throw new AdminException('数据不存在');
|
||||
if ($info['image'] == '') $data['image'] = $this->getChannelCode($id);
|
||||
$info = $this->dao->update($id, $data);
|
||||
} else {
|
||||
$info = $this->dao->save($data);
|
||||
$image = $this->getChannelCode($info['id']);
|
||||
$info = $this->dao->update($info['id'], ['image' => $image]);
|
||||
}
|
||||
if (!$info) throw new AdminException('保存失败');
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成渠道码
|
||||
* @param int $id
|
||||
* @return mixed|string
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getChannelCode($id = 0)
|
||||
{
|
||||
/** @var SystemAttachmentServices $systemAttachment */
|
||||
$systemAttachment = app()->make(SystemAttachmentServices::class);
|
||||
$name = 'wechatqrcode_' . $id . '.jpg';
|
||||
$siteUrl = sys_config('site_url');
|
||||
$imageInfo = $systemAttachment->getInfo(['name' => $name]);
|
||||
if (!$imageInfo) {
|
||||
/** @var QrcodeServices $qrCode */
|
||||
$qrCode = app()->make(QrcodeServices::class);
|
||||
//公众号
|
||||
$resCode = $qrCode->getForeverQrcode('wechatqrcode', $id);
|
||||
if ($resCode) {
|
||||
$res = ['res' => $resCode, 'id' => $resCode['id']];
|
||||
} else {
|
||||
$res = false;
|
||||
}
|
||||
if (!$res) throw new AdminException('二维码生成失败');
|
||||
$imageInfo = $this->downloadImage($resCode['url'], $name);
|
||||
$systemAttachment->attachmentAdd($name, $imageInfo['size'], $imageInfo['type'], $imageInfo['att_dir'], $imageInfo['att_dir'], 1, $imageInfo['image_type'], time(), 2);
|
||||
}
|
||||
return strpos($imageInfo['att_dir'], 'http') === false ? $siteUrl . $imageInfo['att_dir'] : $imageInfo['att_dir'];
|
||||
}
|
||||
|
||||
/**下载图片
|
||||
* @param $url
|
||||
* @param $name
|
||||
* @param $type
|
||||
* @param $timeout
|
||||
* @param $w
|
||||
* @param $h
|
||||
* @return array|string
|
||||
*/
|
||||
public function downloadImage($url = '', $name = '', $type = 0, $timeout = 30, $w = 0, $h = 0)
|
||||
{
|
||||
if (!strlen(trim($url))) return '';
|
||||
//TODO 获取远程文件所采用的方法
|
||||
if ($type) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //TODO 跳过证书检查
|
||||
if (stripos($url, "https://") !== FALSE) curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //TODO 从证书中检查SSL加密算法是否存在
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('user-agent:' . $_SERVER['HTTP_USER_AGENT']));
|
||||
if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);//TODO 是否采集301、302之后的页面
|
||||
$content = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
} else {
|
||||
try {
|
||||
ob_start();
|
||||
readfile($url);
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
} catch (\Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
$size = strlen(trim($content));
|
||||
if (!$content || $size <= 2) return '图片流获取失败';
|
||||
$upload_type = sys_config('upload_type', 1);
|
||||
$upload = UploadService::init();
|
||||
if ($upload->to('attach/spread/agent')->stream($content, $name) === false) {
|
||||
return $upload->getError();
|
||||
}
|
||||
$imageInfo = $upload->getUploadInfo();
|
||||
$data['att_dir'] = $imageInfo['dir'];
|
||||
$data['name'] = $imageInfo['name'];
|
||||
$data['size'] = $imageInfo['size'];
|
||||
$data['type'] = $imageInfo['type'];
|
||||
$data['image_type'] = $upload_type;
|
||||
$data['is_exists'] = false;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫码完成后方法
|
||||
* @param $qrcodeInfo
|
||||
* @param $userInfo
|
||||
* @param $spreadInfo
|
||||
* @param int $isFollow
|
||||
* @return mixed
|
||||
*/
|
||||
public function wechatQrcodeRecord($qrcodeInfo, $userInfo, $spreadInfo, $isFollow = 0)
|
||||
{
|
||||
$response = $this->transaction(function () use ($qrcodeInfo, $userInfo, $spreadInfo, $isFollow) {
|
||||
|
||||
//绑定用户标签
|
||||
/** @var UserLabelRelationServices $labelServices */
|
||||
$labelServices = app()->make(UserLabelRelationServices::class);
|
||||
$userLabelArr = $labelServices->getColumn(['uid' => $userInfo['uid']], 'label_id');
|
||||
$labelArr = [];
|
||||
foreach ($qrcodeInfo['label_id'] as $item) {
|
||||
$label_id = $item['id'] ?? $item;
|
||||
if (in_array($label_id, $userLabelArr)) continue;
|
||||
$labelArr[] = [
|
||||
'uid' => $userInfo['uid'],
|
||||
'label_id' => $label_id
|
||||
];
|
||||
}
|
||||
if (count($labelArr)) $labelServices->saveAll($labelArr);
|
||||
|
||||
//增加二维码扫码数量
|
||||
$this->dao->upFollowAndScan($qrcodeInfo['id'], $isFollow);
|
||||
|
||||
//写入扫码记录
|
||||
/** @var WechatQrcodeRecordServices $recordServices */
|
||||
$recordServices = app()->make(WechatQrcodeRecordServices::class);
|
||||
$data['qid'] = $qrcodeInfo['id'];
|
||||
$data['uid'] = $userInfo['uid'];
|
||||
$data['is_follow'] = $isFollow;
|
||||
$data['add_time'] = time();
|
||||
$recordServices->save($data);
|
||||
|
||||
//回复信息内容
|
||||
return $this->replyDataByMessage($qrcodeInfo['type'], $qrcodeInfo['data']);
|
||||
});
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送扫码之后的信息
|
||||
* @param $type
|
||||
* @param $data
|
||||
* @return Image|News|Text|Voice|void
|
||||
*/
|
||||
public function replyDataByMessage($type, $data)
|
||||
{
|
||||
if ($type == 'text') {
|
||||
return Messages::textMessage($data['content']);
|
||||
} else if ($type == 'image') {
|
||||
return Messages::imageMessage($data['media_id']);
|
||||
} else if ($type == 'news') {
|
||||
$title = $data['title'] ?? '';
|
||||
$image = $data['image'] ?? '';
|
||||
$description = $data['synopsis'] ?? '';
|
||||
$url = $data['url'] ?? '';
|
||||
return Messages::newsMessage($title, $description, $url, $image);
|
||||
} else if ($type == 'url') {
|
||||
$title = $data['content'];
|
||||
$image = sys_config('h5_avatar');
|
||||
$description = $data['content'];
|
||||
$url = $data['content'];
|
||||
return Messages::newsMessage($title, $description, $url, $image);
|
||||
} else if ($type == 'voice') {
|
||||
return Messages::voiceMessage($data['media_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
app/services/wechat/WechatReplyKeyServices.php
Normal file
50
app/services/wechat/WechatReplyKeyServices.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\services\BaseServices;
|
||||
use app\dao\wechat\WechatReplyKeyDao;
|
||||
use think\annotation\Inject;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class UserWechatuserServices
|
||||
* @package app\services\user
|
||||
* @mixin WechatReplyKeyDao
|
||||
*/
|
||||
class WechatReplyKeyServices extends BaseServices
|
||||
{
|
||||
/**
|
||||
* @var WechatReplyKeyDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatReplyKeyDao $dao;
|
||||
|
||||
/**
|
||||
* @param array $where
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function getReplyKeyAll(array $where): array
|
||||
{
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->getReplyKeyList($where, $page, $limit);
|
||||
$count = $this->dao->count($where);
|
||||
return compact('list', 'count');
|
||||
}
|
||||
}
|
||||
345
app/services/wechat/WechatReplyServices.php
Normal file
345
app/services/wechat/WechatReplyServices.php
Normal file
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use think\Model;
|
||||
use think\annotation\Inject;
|
||||
use app\services\BaseServices;
|
||||
use app\dao\wechat\WechatReplyDao;
|
||||
use app\services\kefu\KefuServices;
|
||||
use think\db\exception\DbException;
|
||||
use crmeb\services\wechat\Messages;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use think\exception\ValidateException;
|
||||
use crmeb\services\wechat\ErrorMessage;
|
||||
use crmeb\services\wechat\OfficialAccount;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class UserWechatuserServices
|
||||
* @package app\services\user
|
||||
* @mixin WechatReplyDao
|
||||
*/
|
||||
class WechatReplyServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WechatReplyDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatReplyDao $dao;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
* @return string[]
|
||||
* @var string[]
|
||||
*/
|
||||
public function replyType()
|
||||
{
|
||||
return ['text', 'image', 'news', 'voice'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义简单查询总数
|
||||
* @param array $where
|
||||
* @return int
|
||||
*/
|
||||
public function getCount(array $where): int
|
||||
{
|
||||
return $this->dao->getCount($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复杂条件搜索列表
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @return array
|
||||
*/
|
||||
public function getWhereUserList(array $where, string $field): array
|
||||
{
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$list = $this->dao->getListByModel($where, $field, $page, $limit);
|
||||
$count = $this->dao->getCountByWhere($where);
|
||||
return [$list, $count];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return array|Model|null
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function getDataByKey(string $key)
|
||||
{
|
||||
/** @var WechatKeyServices $services */
|
||||
$services = app()->make(WechatKeyServices::class);
|
||||
$data = $services->getOne(['keys' => $key]);
|
||||
$resdata = $this->dao->getOne(['id' => $data['reply_id'] ?? '']);
|
||||
$resdata['data'] = isset($resdata['data']) ? json_decode($resdata['data'], true) : [];
|
||||
$resdata['key'] = $key;
|
||||
return $resdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $key
|
||||
* @param $type
|
||||
* @param int $status
|
||||
* @return bool
|
||||
*/
|
||||
public function redact($data, $id, $key, $type, $status = 1)
|
||||
{
|
||||
$method = 'tidy' . ucfirst($type);
|
||||
if ($id == 'undefined') {
|
||||
$id = 0;
|
||||
}
|
||||
if ((!isset($data['content']) && !isset($data['src'])) || (isset($data['content']) && $data['content'] == '' && isset($data['src']) && $data['src'] == '')) $data = $data['list'][0] ?? [];
|
||||
try {
|
||||
$res = $this->{$method}($data, $id);
|
||||
} catch (\Throwable $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
if (!$res) return false;
|
||||
$arr = [];
|
||||
/** @var WechatKeyServices $keyServices */
|
||||
$keyServices = app()->make(WechatKeyServices::class);
|
||||
$count = $this->dao->getCount(['id' => $id]);
|
||||
if ($count) {
|
||||
$keyServices->delete($id, 'reply_id');
|
||||
$insertData = explode(',', $key);
|
||||
foreach ($insertData as $k => $v) {
|
||||
$arr[$k]['keys'] = $v;
|
||||
$arr[$k]['reply_id'] = $id;
|
||||
}
|
||||
$res = $this->dao->update($id, ['type' => $type, 'data' => json_encode($res), 'status' => $status], 'id');
|
||||
$res1 = $keyServices->saveAll($arr);
|
||||
if (!$res || !$res1) {
|
||||
throw new AdminException('保存失败!');
|
||||
}
|
||||
} else {
|
||||
$reply = $this->dao->save([
|
||||
'type' => $type,
|
||||
'data' => json_encode($res),
|
||||
'status' => $status,
|
||||
]);
|
||||
$insertData = explode(',', $key);
|
||||
foreach ($insertData as $k => $v) {
|
||||
$arr[$k]['keys'] = $v;
|
||||
$arr[$k]['reply_id'] = $reply->id;
|
||||
}
|
||||
$res = $keyServices->saveAll($arr);
|
||||
if (!$res) throw new AdminException('保存失败!');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有关键字
|
||||
* @param array $where
|
||||
* @return array
|
||||
*/
|
||||
public function getKeyAll($where = array())
|
||||
{
|
||||
/** @var WechatReplyKeyServices $replyKeyServices */
|
||||
$replyKeyServices = app()->make(WechatReplyKeyServices::class);
|
||||
$data = $replyKeyServices->getReplyKeyAll($where);
|
||||
/** @var WechatKeyServices $keyServices */
|
||||
$keyServices = app()->make(WechatKeyServices::class);
|
||||
foreach ($data['list'] as &$item) {
|
||||
if ($item['data']) $item['data'] = json_decode($item['data'], true);
|
||||
switch ($item['type']) {
|
||||
case 'text':
|
||||
$item['typeName'] = '文字消息';
|
||||
break;
|
||||
case 'image':
|
||||
$item['typeName'] = '图片消息';
|
||||
break;
|
||||
case 'news':
|
||||
$item['typeName'] = '图文消息';
|
||||
break;
|
||||
case 'voice':
|
||||
$item['typeName'] = '声音消息';
|
||||
break;
|
||||
}
|
||||
$keys = $keyServices->getColumn(['reply_id' => $item['id']], 'keys');
|
||||
$item['key'] = implode(',', $keys);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询一条
|
||||
* @param int $id
|
||||
* @return array|null|Model
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function getKeyInfo(int $id)
|
||||
{
|
||||
$resdata = $this->dao->getOne(['id' => $id]);
|
||||
/** @var WechatKeyServices $keyServices */
|
||||
$keyServices = app()->make(WechatKeyServices::class);
|
||||
$keys = $keyServices->getColumn(['reply_id' => $resdata['id']], 'keys');
|
||||
$resdata['data'] = $resdata['data'] ? json_decode($resdata['data'], true) : [];
|
||||
$resdata['key'] = implode(',', $keys);
|
||||
return $resdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* 整理文本输入的消息
|
||||
* @param $data
|
||||
* @param $key
|
||||
* @return array|bool
|
||||
*/
|
||||
public function tidyText($data, $id)
|
||||
{
|
||||
$res = [];
|
||||
if (!isset($data['content']) || $data['content'] == '') {
|
||||
throw new AdminException('请输入回复信息内容');
|
||||
}
|
||||
$res['content'] = $data['content'];
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 整理图片资源
|
||||
* @param $data
|
||||
* @param $key
|
||||
* @return array|bool|mixed
|
||||
*/
|
||||
public function tidyImage($data, $id)
|
||||
{
|
||||
if (!isset($data['src']) || $data['src'] == '') {
|
||||
throw new AdminException('请上传回复的图片');
|
||||
}
|
||||
$reply = $this->dao->get((int)$id);
|
||||
if ($reply) $reply['data'] = json_decode($reply['data'], true);
|
||||
if ($reply && isset($reply['data']['src']) && $reply['data']['src'] == $data['src']) {
|
||||
$res = $reply['data'];
|
||||
} else {
|
||||
$res = [];
|
||||
// 图片转media
|
||||
$res['src'] = $data['src'];
|
||||
try {
|
||||
$material = OfficialAccount::temporaryUpload(url_to_path($data['src']));
|
||||
} catch (\Throwable $e) {
|
||||
throw new ValidateException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
$res['media_id'] = $material['media_id'];
|
||||
$dataEvent = ['type' => 'image', 'media_id' => $material['media_id'], 'path' => $res['src'], 'url' => $material->url ?? ''];
|
||||
/** @var WechatMediaServices $mateServices */
|
||||
$mateServices = app()->make(WechatMediaServices::class);
|
||||
$mateServices->save($dataEvent);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 整理声音资源
|
||||
* @param $data
|
||||
* @param $key
|
||||
* @return array|bool|mixed
|
||||
*/
|
||||
public function tidyVoice($data, $id)
|
||||
{
|
||||
if (!isset($data['src']) || $data['src'] == '') {
|
||||
throw new AdminException('请上传回复的声音');
|
||||
}
|
||||
$reply = $this->dao->get((int)$id);
|
||||
if ($reply) $reply['data'] = json_decode($reply['data'], true);
|
||||
if ($reply && isset($reply['data']['src']) && $reply['data']['src'] == $data['src']) {
|
||||
$res = $reply['data'];
|
||||
} else {
|
||||
$res = [];
|
||||
// 声音转media
|
||||
$res['src'] = $data['src'];
|
||||
try {
|
||||
$material = OfficialAccount::instance()->material()->uploadVoice(url_to_path($data['src']));
|
||||
} catch (\Throwable $e) {
|
||||
throw new ValidateException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
$res['media_id'] = $material['media_id'];
|
||||
$dataEvent = ['media_id' => $material['media_id'], 'path' => $res['src'], 'type' => 'voice'];
|
||||
/** @var WechatMediaServices $mateServices */
|
||||
$mateServices = app()->make(WechatMediaServices::class);
|
||||
$mateServices->save($dataEvent);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 整理图文资源
|
||||
* @param $data
|
||||
* @param $key
|
||||
* @return bool
|
||||
*/
|
||||
public function tidyNews($data, $id = 0)
|
||||
{
|
||||
// $data = $data['list'][0];
|
||||
if (!count($data)) {
|
||||
throw new AdminException('请选择图文消息');
|
||||
}
|
||||
$siteUrl = sys_config('site_url');
|
||||
if (empty($data['url'])) $data['url'] = $siteUrl . '/pages/extension/news_details/index?id=' . $data['id'];
|
||||
if (count($data['image_input'])) $data['image'] = $data['image_input'][0];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关键字
|
||||
* @param $key
|
||||
* @param string $openId
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function reply($key, string $openId = '')
|
||||
{
|
||||
$res = $this->dao->getKey($key);
|
||||
if (empty($res)) {
|
||||
/** @var KefuServices $services */
|
||||
$services = app()->make(KefuServices::class);
|
||||
$services->replyTransferService($key, $openId);
|
||||
return Messages::transfer();
|
||||
}
|
||||
return $this->replyDataByMessage($res->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键字内容返回对应的内容
|
||||
* @param array $res
|
||||
* @return array
|
||||
*/
|
||||
public function replyDataByMessage(array $res)
|
||||
{
|
||||
$res['data'] = json_decode($res['data'], true);
|
||||
if ($res['type'] == 'text') {
|
||||
return Messages::textMessage($res['data']['content']);
|
||||
} else if ($res['type'] == 'image') {
|
||||
return Messages::imageMessage($res['data']['media_id']);
|
||||
} else if ($res['type'] == 'news') {
|
||||
$title = $res['data']['title'] ?? '';
|
||||
$image = $res['data']['image'] ?? '';
|
||||
$description = $res['data']['synopsis'] ?? '';
|
||||
$url = $res['data']['url'] ?? '';
|
||||
return Messages::newsMessage($title, $description, $url, $image);
|
||||
} else if ($res['type'] == 'voice') {
|
||||
return Messages::voiceMessage($res['data']['media_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
478
app/services/wechat/WechatServices.php
Normal file
478
app/services/wechat/WechatServices.php
Normal file
@@ -0,0 +1,478 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\services\BaseServices;
|
||||
use app\dao\wechat\WechatUserDao;
|
||||
use app\services\user\UserServices;
|
||||
use app\services\user\UserVisitServices;
|
||||
use crmeb\services\CacheService;
|
||||
use crmeb\services\CacheService as Cache;
|
||||
use crmeb\services\wechat\OfficialAccount;
|
||||
use crmeb\services\wechat\Work;
|
||||
use crmeb\utils\Canvas;
|
||||
use EasyWeChat\Kernel\Exceptions\BadRequestException;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use think\annotation\Inject;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\DbException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class WechatServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatUserDao
|
||||
*/
|
||||
class WechatServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WechatUserDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatUserDao $dao;
|
||||
|
||||
/**
|
||||
* 微信公众号服务
|
||||
* @return \think\Response
|
||||
* @throws BadRequestException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function serve()
|
||||
{
|
||||
ob_clean();
|
||||
$res = OfficialAccount::serve();
|
||||
//
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信服务
|
||||
* @return \think\Response
|
||||
* @throws BadRequestException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function workServe()
|
||||
{
|
||||
ob_clean();
|
||||
return Work::serve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号权限配置信息获取
|
||||
* @param $url
|
||||
* @return array
|
||||
*/
|
||||
public function config($url): array
|
||||
{
|
||||
return OfficialAccount::jsSdk($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权信息
|
||||
* @param string $code
|
||||
* @return array
|
||||
*/
|
||||
public function getAuthWechatInfo()
|
||||
{
|
||||
try {
|
||||
$userInfoConfig = OfficialAccount::tokenFromCode();
|
||||
} catch (\Throwable $e) {
|
||||
\think\facade\Log::error(
|
||||
json_encode([
|
||||
'error' => '授权失败:' . $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine()
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
);
|
||||
throw new ValidateException('授权失败');
|
||||
}
|
||||
if (!isset($userInfoConfig['openid']) || !$userInfoConfig['openid']) {
|
||||
throw new ValidateException('openid获取失败');
|
||||
}
|
||||
return $userInfoConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回信息
|
||||
* @param $user
|
||||
* @param string $userType
|
||||
* @return array
|
||||
*/
|
||||
public function getReturnInfo($user, string $userType = 'wechat')
|
||||
{
|
||||
if (!$user || !isset($user['uid']) || !$user['uid']) {
|
||||
throw new ValidateException('获取用户信息失败');
|
||||
}
|
||||
$uid = (int)$user['uid'];
|
||||
$token = $this->createToken($uid, $userType, $user['pwd'] ?? '');
|
||||
if (!$token) {
|
||||
throw new ValidateException('登录失败!');
|
||||
}
|
||||
/** @var UserVisitServices $visitServices */
|
||||
$visitServices = app()->make(UserVisitServices::class);
|
||||
$visitServices->loginSaveVisit($user);
|
||||
$token['store_user_avatar'] = 0;
|
||||
$token['userInfo'] = is_object($user) && method_exists($user, 'toArray') ? $user->toArray() : $user;
|
||||
$token['expires_time'] = $token['params']['exp'] ?? 0;
|
||||
|
||||
// 用户登录成功事件
|
||||
event('user.login', [$uid, app('request')->ip()]);
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号授权登录,返回是否需要绑定手机号token
|
||||
* @param $spread_uid
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws InvalidConfigException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function authLogin($spread_uid = '')
|
||||
{
|
||||
$wechatInfo = OfficialAccount::userFromCode();
|
||||
if (!isset($wechatInfo['nickname'])) {
|
||||
$wechatInfo = OfficialAccount::instance()->user()->get($wechatInfo['openid']);
|
||||
if (!isset($wechatInfo['nickname']))
|
||||
throw new ValidateException('授权失败');
|
||||
if (isset($wechatInfo['tagid_list']))
|
||||
$wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']);
|
||||
} else {
|
||||
if (isset($wechatInfo['privilege'])) unset($wechatInfo['privilege']);
|
||||
}
|
||||
$wechatInfo['user_type'] = 'wechat';
|
||||
$openid = $wechatInfo['openid'];
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
|
||||
$createData = [$openid, $wechatInfo, $spread_uid, 'wechat', 'wechat'];
|
||||
//获取是否强制绑定手机号
|
||||
$storeUserMobile = sys_config('store_user_mobile');
|
||||
if ($storeUserMobile && (($user && $user['phone'] == '') || !$user)) {
|
||||
$userInfoKey = md5($openid . '_' . time() . '_wechat');
|
||||
CacheService::set($userInfoKey, $createData, 7200);
|
||||
return ['bindPhone' => true, 'key' => $userInfoKey];
|
||||
}
|
||||
if (!$user) {
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
} else {
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
|
||||
}
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号授权登陆
|
||||
* @return mixed
|
||||
* @throws DataNotFoundException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DbException|InvalidConfigException
|
||||
*/
|
||||
public function auth($spread_uid, $login_type)
|
||||
{
|
||||
$wechatInfo = $this->getAuthWechatInfo();
|
||||
if (!isset($wechatInfo['nickname'])) {
|
||||
$wechatInfo = OfficialAccount::instance()->user()->get($wechatInfo['openid'])->toArray();
|
||||
if (!isset($wechatInfo['nickname']))
|
||||
throw new ValidateException('授权失败');
|
||||
if (isset($wechatInfo['tagid_list']))
|
||||
$wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']);
|
||||
} else {
|
||||
if (isset($wechatInfo['privilege'])) unset($wechatInfo['privilege']);
|
||||
/** @var WechatUserServices $wechatUser */
|
||||
$wechatUser = app()->make(WechatUserServices::class);
|
||||
if (!$wechatUser->getOne(['openid' => $wechatInfo['openid']])) {
|
||||
$wechatInfo['subscribe'] = 0;
|
||||
}
|
||||
}
|
||||
$wechatInfo['user_type'] = 'wechat';
|
||||
$openid = $wechatInfo['openid'];
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
|
||||
$createData = [$openid, $wechatInfo, $spread_uid, $login_type, 'wechat'];
|
||||
if (!$user) {
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
} else {
|
||||
//更新用户信息
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
|
||||
}
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新公众号授权登录
|
||||
* @param $spread_uid
|
||||
* @param $login_type
|
||||
* @return mixed
|
||||
* @throws DataNotFoundException
|
||||
* @throws InvalidConfigException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function newAuth($spread_uid, $login_type)
|
||||
{
|
||||
$wechatInfo = OfficialAccount::userFromCode();
|
||||
if (!isset($wechatInfo['nickname'])) {
|
||||
$wechatInfo = OfficialAccount::instance()->user()->get($wechatInfo['openid']);
|
||||
if (!isset($wechatInfo['nickname']))
|
||||
throw new ValidateException('授权失败');
|
||||
if (isset($wechatInfo['tagid_list']))
|
||||
$wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']);
|
||||
} else {
|
||||
if (isset($wechatInfo['privilege'])) unset($wechatInfo['privilege']);
|
||||
}
|
||||
$wechatInfo['user_type'] = 'wechat';
|
||||
$openid = $wechatInfo['openid'];
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
|
||||
$createData = [$openid, $wechatInfo, $spread_uid, $login_type, 'wechat'];
|
||||
//获取是否强制绑定手机号
|
||||
$storeUserMobile = sys_config('store_user_mobile');
|
||||
if ($storeUserMobile && !$user) {
|
||||
$userInfoKey = md5($openid . '_' . time() . '_wechat');
|
||||
Cache::setTokenBucket($userInfoKey, $createData, 7200);
|
||||
return ['key' => $userInfoKey];
|
||||
} else if (!$user) {
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
} else {
|
||||
//更新用户信息
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
|
||||
}
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成关注二维码图片
|
||||
* @return array
|
||||
* @throws ValidateException
|
||||
*/
|
||||
public function follow()
|
||||
{
|
||||
$canvas = Canvas::instance();
|
||||
$path = 'uploads/follow/';
|
||||
$imageType = 'jpg';
|
||||
$name = 'follow';
|
||||
$siteUrl = sys_config('site_url');
|
||||
$imageUrl = $path . $name . '.' . $imageType;
|
||||
$canvas->setImageUrl(public_path() . 'statics/qrcode/follow.png')->setImageHeight(720)->setImageWidth(500)->pushImageValue();
|
||||
$wechatQrcode = sys_config('wechat_qrcode');
|
||||
if (($strlen = stripos($wechatQrcode, 'uploads')) !== false) {
|
||||
$wechatQrcode = substr($wechatQrcode, $strlen);
|
||||
}
|
||||
if (!$wechatQrcode)
|
||||
throw new ValidateException('请上传二维码');
|
||||
$canvas->setImageUrl($wechatQrcode)->setImageHeight(344)->setImageWidth(344)->setImageLeft(76)->setImageTop(76)->pushImageValue();
|
||||
$image = $canvas->setFileName($name)->setImageType($imageType)->setPath($path)->setBackgroundWidth(500)->setBackgroundHeight(720)->starDrawChart();
|
||||
return ['path' => $image ? $siteUrl . '/' . $image : ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众号静默授权
|
||||
* @param $spread_uid
|
||||
* @param bool $notLogin
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function silenceAuth($spread_uid, bool $notLogin = false, string $snsapi = '')
|
||||
{
|
||||
if ($snsapi) {
|
||||
$wechatInfoConfig = $this->getAuthWechatInfo();
|
||||
} else {
|
||||
$wechatInfoConfig = OfficialAccount::userFromCode();
|
||||
}
|
||||
$openid = $wechatInfoConfig['openid'];
|
||||
try {
|
||||
$wechatInfo = OfficialAccount::instance()->user()->get($wechatInfoConfig['openid'])->toArray();
|
||||
} catch (\Throwable $e) {
|
||||
$createData = [$openid, [], $spread_uid, '', 'wechat'];
|
||||
$userInfoKey = md5($openid . '_' . time() . '_wechat');
|
||||
Cache::setTokenBucket($userInfoKey, $createData, 7200);
|
||||
return ['auth_login' => 1, 'key' => $userInfoKey];
|
||||
}
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
|
||||
if (!$user) {
|
||||
$wechatInfo['headimgurl'] = isset($wechatInfo['headimgurl']) && $wechatInfo['headimgurl'] != '' ? $wechatInfo['headimgurl'] : sys_config('h5_avatar');
|
||||
$createData = [$openid, $wechatInfo, $spread_uid, '', 'wechat'];
|
||||
//获取是否强制绑定手机号
|
||||
$storeUserMobile = sys_config('store_user_mobile');
|
||||
if ($notLogin || $storeUserMobile) {
|
||||
$userInfoKey = md5($openid . '_' . time() . '_wechat');
|
||||
Cache::setTokenBucket($userInfoKey, $createData, 7200);
|
||||
return ['auth_login' => 1, 'key' => $userInfoKey];
|
||||
} else {
|
||||
//写入用户信息
|
||||
$user = $wechatUserServices->wechatOauthAfter($createData);
|
||||
}
|
||||
} else {
|
||||
//更新用户信息
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], ['spread_uid' => $spread_uid]]);
|
||||
}
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众号静默授权
|
||||
* @param $spread
|
||||
* @param $phone
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws ModelNotFoundException|\Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public function silenceAuthBindingPhone($key, $phone)
|
||||
{
|
||||
if (!$key) {
|
||||
throw new ValidateException('请刷新页面或者重新授权');
|
||||
}
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = CacheService::getTokenBucket($key);
|
||||
if (!$createData) {
|
||||
throw new ValidateException('请刷新页面或者重新授权');
|
||||
}
|
||||
$wechatInfo['phone'] = $phone;
|
||||
/** @var WechatUserServices $wechatUser */
|
||||
$wechatUser = app()->make(WechatUserServices::class);
|
||||
//更新用户信息
|
||||
$user = $wechatUser->wechatOauthAfter([$openid, $wechatInfo, $spread_uid, $login_type, $userType]);
|
||||
return $this->getReturnInfo($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $userData
|
||||
* @param string $phone
|
||||
* @param string $userType
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function appAuth(array $userData, string $phone, string $userType = 'app')
|
||||
{
|
||||
$openid = $userData['openId'] ?? "";
|
||||
$userInfo = [
|
||||
'phone' => $phone,
|
||||
'unionid' => $userData['unionId'] ?? '',
|
||||
'headimgurl' => $userData['avatarUrl'] ?? '',
|
||||
'nickname' => $userData['nickName'] ?? '',
|
||||
'province' => $userData['province'] ?? '',
|
||||
'country' => $userData['country'] ?? '',
|
||||
'city' => $userData['city'] ?? '',
|
||||
'openid' => $openid,
|
||||
];
|
||||
$login_type = $userType;
|
||||
$spread_uid = $userInfo['spread_uid'] ?? "";
|
||||
if (!$phone) {
|
||||
//获取是否强制绑定手机号
|
||||
$storeUserMobile = sys_config('store_user_mobile');
|
||||
if ($userInfo['unionid'] && $storeUserMobile) {
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$uid = $this->dao->value(['unionid' => $userInfo['unionid'], 'is_del' => 0], 'uid');
|
||||
$res = $userServices->value(['uid' => $uid], 'phone');
|
||||
if (!$uid || !$res) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//openid存在加数据情况
|
||||
if ($openid) {
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$uid = $this->dao->value(['openid' => $openid, 'is_del' => 0], 'uid');
|
||||
$res = $userServices->value(['uid' => $uid], 'phone');
|
||||
if (!$uid || !$res) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @var WechatUserServices $wechatUser */
|
||||
$wechatUser = app()->make(WechatUserServices::class);
|
||||
//更新用户信息
|
||||
$user = $wechatUser->wechatOauthAfter([$openid, $userInfo, $spread_uid, $login_type, $userType]);
|
||||
$token = $this->getReturnInfo($user);
|
||||
$token['isbind'] = false;
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否关注
|
||||
* @param int $uid
|
||||
* @return bool
|
||||
*/
|
||||
public function isSubscribe(int $uid)
|
||||
{
|
||||
if ($uid) {
|
||||
$subscribe = (bool)$this->dao->value(['uid' => $uid], 'subscribe');
|
||||
} else {
|
||||
$subscribe = true;
|
||||
}
|
||||
return $subscribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新公众号用户信息
|
||||
* @param int $uid
|
||||
* @return array
|
||||
*/
|
||||
public function updateUserInfo(int $uid)
|
||||
{
|
||||
$wechatInfoConfig = OfficialAccount::userFromCode();
|
||||
$openid = $wechatInfoConfig['openid'] ?? null;
|
||||
try {
|
||||
$wechatInfo = OfficialAccount::instance()->user()->get($wechatInfoConfig['openid']);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ValidateException('更新公众号用户信息失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (!$openid) {
|
||||
throw new ValidateException('更新公众号用户信息失败:没有获取到openid');
|
||||
}
|
||||
|
||||
$wechatInfo['nickname'] = $wechatInfoConfig['nickname'] ?? $wechatInfo['nickname'];
|
||||
$wechatInfo['headimgurl'] = $wechatInfoConfig['headimgurl'] ?? $wechatInfo['headimgurl'];
|
||||
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$id = $wechatUserServices->value(['openid' => $openid, 'uid' => $uid, 'user_type' => 'wechat'], 'id');
|
||||
if (!$id) {
|
||||
throw new ValidateException('没有查到用户信息');
|
||||
}
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$user = $userService->getUserInfo($uid);
|
||||
if (isset($user['status']) && !$user['status']) {
|
||||
throw new ValidateException('您已被禁止登录,请联系管理员');
|
||||
}
|
||||
if ($user) {
|
||||
//更新用户信息
|
||||
$wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
|
||||
}
|
||||
|
||||
return [
|
||||
'nickname' => $wechatInfo['nickname'],
|
||||
'avatar' => $wechatInfo['headimgurl'],
|
||||
'is_complete' => 1
|
||||
];
|
||||
}
|
||||
}
|
||||
553
app/services/wechat/WechatUserServices.php
Normal file
553
app/services/wechat/WechatUserServices.php
Normal file
@@ -0,0 +1,553 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\services\wechat;
|
||||
|
||||
use app\jobs\user\UserJob;
|
||||
use app\services\BaseServices;
|
||||
use app\dao\wechat\WechatUserDao;
|
||||
use app\services\user\LoginServices;
|
||||
use app\services\user\UserServices;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\exceptions\AuthException;
|
||||
use crmeb\services\wechat\OfficialAccount;
|
||||
use think\annotation\Inject;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class WechatUserServices
|
||||
* @package app\services\wechat
|
||||
* @mixin WechatUserDao
|
||||
*/
|
||||
class WechatUserServices extends BaseServices
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WechatUserDao
|
||||
*/
|
||||
#[Inject]
|
||||
protected WechatUserDao $dao;
|
||||
|
||||
/**
|
||||
* @param $user_ids
|
||||
* @param $column
|
||||
* @param $key
|
||||
* @param string $user_type
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/19
|
||||
*/
|
||||
public function getColumnUser($user_ids, $column, $key, string $user_type = 'wechat')
|
||||
{
|
||||
return $this->dao->getColumn([['uid', 'IN', $user_ids], ['user_type', '=', $user_type]], $column, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个微信用户
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getWechatUserInfo(array $where, $field = '*')
|
||||
{
|
||||
return $this->dao->getOne($where, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用uid获得 微信openid
|
||||
* @param $uid
|
||||
* @return mixed
|
||||
*/
|
||||
public function uidToOpenid(int $uid, string $userType = 'wechat')
|
||||
{
|
||||
return $this->dao->value(['uid' => $uid, 'user_type' => $userType], 'openid');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用openid获得uid
|
||||
* @param $openid
|
||||
* @param string $openidType
|
||||
* @return mixed
|
||||
*/
|
||||
public function openidTouid($openid, $openidType = 'openid')
|
||||
{
|
||||
$uid = $this->dao->value([[$openidType, '=', $openid], ['user_type', '<>', 'h5']], 'uid');
|
||||
if (!$uid)
|
||||
throw new AdminException('对应的uid不存在');
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户取消关注
|
||||
* @param $openid
|
||||
* @return bool
|
||||
*/
|
||||
public function unSubscribe($openid)
|
||||
{
|
||||
if (!$this->dao->update($openid, ['subscribe' => 0, 'subscribe_time' => time()], 'openid'))
|
||||
throw new AdminException('取消关注失败');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新微信用户信息
|
||||
* @param $message
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function saveUserV1($message)
|
||||
{
|
||||
$openid = $message->FromUserName;
|
||||
if ($this->getWechatUserInfo(['openid' => $openid])) {
|
||||
$this->updateWecahtUser($openid);
|
||||
} else {
|
||||
$this->setWecahtUser($openid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户存在就更新 不存在就添加
|
||||
* @param $openid
|
||||
* @param int $spread_uid
|
||||
* @param string $phone
|
||||
* @return \app\services\user\User|array|\think\Model|null
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function saveUser($openid, $spread_uid = 0, $phone = '')
|
||||
{
|
||||
$is_new = false;
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$wechatUser = $this->getWechatUserInfo(['openid' => $openid]);
|
||||
if ($wechatUser && $wechatUser['uid']) {
|
||||
$userInfo = $userServices->getUserInfo((int)$wechatUser['uid']);
|
||||
//无关注只是授权生成用户
|
||||
if (!$wechatUser['subscribe_time'] && $wechatUser['uid']) {
|
||||
$is_new = true;
|
||||
$spread_uid = $userInfo['spread_uid'] ?? 0;
|
||||
}
|
||||
$this->updateWecahtUser($openid);
|
||||
} else {
|
||||
$userInfo = $this->setNewUser($openid, $spread_uid, ['phone' => $phone]);
|
||||
$is_new = true;
|
||||
}
|
||||
if ($is_new) {
|
||||
UserJob::dispatchDo('subscribeSpreadLottery', [(int)$userInfo['uid'], $openid, (int)$spread_uid]);
|
||||
}
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* @param $openid
|
||||
* @param string $phone
|
||||
* @return bool
|
||||
*/
|
||||
public function updateWecahtUser($openid)
|
||||
{
|
||||
try {
|
||||
$userInfo = OfficialAccount::getUserInfo($openid);
|
||||
} catch (\Throwable $e) {
|
||||
$userInfo = [];
|
||||
}
|
||||
if (isset($userInfo['nickname']) && $userInfo['nickname']) {
|
||||
$userInfo['nickname'] = filter_emoji($userInfo['nickname']);
|
||||
} else {
|
||||
if (isset($userInfo['nickname'])) unset($userInfo['nickname']);
|
||||
}
|
||||
if (isset($userInfo['tagid_list'])) {
|
||||
$userInfo['tagid_list'] = implode(',', $userInfo['tagid_list']);
|
||||
}
|
||||
if ($userInfo && !$this->dao->update($openid, $userInfo, 'openid'))
|
||||
throw new AdminException('更新失败');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入微信用户信息
|
||||
* @param $openid
|
||||
* @param int $uid
|
||||
* @return bool
|
||||
*/
|
||||
public function setWecahtUser($openid, int $uid = 0)
|
||||
{
|
||||
try {
|
||||
$wechatInfo = OfficialAccount::getUserInfo($openid);
|
||||
} catch (\Throwable $e) {
|
||||
$wechatInfo = [];
|
||||
}
|
||||
if (!isset($wechatInfo['openid']))
|
||||
throw new ValidateException('请关注公众号!');
|
||||
if (isset($wechatInfo['nickname']) && $wechatInfo['nickname']) {
|
||||
$wechatInfo['nickname'] = filter_emoji($wechatInfo['nickname']);
|
||||
} else {
|
||||
mt_srand();
|
||||
$wechatInfo['nickname'] = 'wx' . rand(100000, 999999);
|
||||
}
|
||||
if (isset($wechatInfo['tagid_list'])) {
|
||||
$wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']);
|
||||
}
|
||||
$wechatInfo['user_type'] = 'wechat';
|
||||
$wechatInfo['uid'] = $uid;
|
||||
$wechatInfo['add_time'] = time();
|
||||
if ($wechatInfo && !$this->dao->save($wechatInfo)) {
|
||||
throw new AdminException('用户储存失败!');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加新用户
|
||||
* @param $openid
|
||||
* @param int $spread_uid
|
||||
* @param array $append 追加字段
|
||||
* @return \app\services\user\User|\think\Model
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function setNewUser($openid, $spread_uid = 0, array $append = [])
|
||||
{
|
||||
|
||||
try {
|
||||
$wechatInfo = OfficialAccount::getUserInfo($openid);
|
||||
if (isset($wechatInfo['errcode']) && $wechatInfo['errcode'] == 40001) {
|
||||
OfficialAccount::instance()->application()->getAccessToken()->refresh();
|
||||
$wechatInfo = OfficialAccount::getUserInfo($openid);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('获取用户信息失败' . $e->getMessage());
|
||||
|
||||
$wechatInfo = [];
|
||||
}
|
||||
if (!isset($wechatInfo['openid']))
|
||||
throw new ValidateException('请关注公众号!');
|
||||
|
||||
if (isset($wechatInfo['nickname']) && $wechatInfo['nickname']) {
|
||||
$wechatInfo['nickname'] = $wechatInfo['nickname'];
|
||||
$wechatInfo['is_complete'] = 1;
|
||||
} else {
|
||||
//昵称不存在的信息不完整
|
||||
$wechatInfo['is_complete'] = 0;
|
||||
mt_srand();
|
||||
$wechatInfo['nickname'] = 'wx' . rand(100000, 999999);
|
||||
}
|
||||
if (isset($wechatInfo['tagid_list'])) {
|
||||
$wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']);
|
||||
}
|
||||
|
||||
$uid = 0;
|
||||
$userType = 'wechat';
|
||||
$userInfo = [];
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
if (isset($append['phone']) && $append['phone']) {
|
||||
$userInfo = $userServices->getOne(['phone' => $append['phone']]);
|
||||
$wechatInfo['phone'] = $append['phone'];
|
||||
}
|
||||
if (!$userInfo) {
|
||||
if (isset($wechatInfo['unionid']) && $wechatInfo['unionid']) {
|
||||
$uid = $this->dao->value(['unionid' => $wechatInfo['unionid']], 'uid');
|
||||
if ($uid) {
|
||||
$userInfo = $userServices->getOne(['uid' => $uid]);
|
||||
}
|
||||
} else {
|
||||
$userInfo = $this->getAuthUserInfo($openid, $userType);
|
||||
}
|
||||
}
|
||||
if ($userInfo) {
|
||||
$uid = (int)$userInfo['uid'];
|
||||
if (isset($userInfo['status']) && !$userInfo['status'])
|
||||
throw new ValidateException('您已被禁止登录,请联系管理员');
|
||||
}
|
||||
$wechatInfo['user_type'] = $userType;
|
||||
if ($userInfo) {
|
||||
//更新用户表和wechat_user表
|
||||
//判断该类性用户在wechatUser中是否存在
|
||||
$wechatUser = $this->dao->getOne(['uid' => $uid, 'user_type' => $userType]);
|
||||
if ($wechatUser) {
|
||||
$wechatUser = $this->dao->getOne(['openid' => $openid]);
|
||||
}
|
||||
/** @var LoginServices $loginService */
|
||||
$loginService = app()->make(LoginServices::class);
|
||||
$this->transaction(function () use ($openid, $loginService, $wechatInfo, $userInfo, $uid, $userType, $spread_uid, $wechatUser) {
|
||||
$wechatInfo['spread_uid'] = $spread_uid;
|
||||
$wechatInfo['uid'] = $uid;
|
||||
$loginService->updateUserInfo($wechatInfo, $userInfo);
|
||||
if ($wechatUser) {
|
||||
if (isset($append['phone']) && $append['phone'] && $wechatUser['openid'] != $openid) {
|
||||
throw new ValidateException('该手机号已被注册');
|
||||
}
|
||||
if (!$this->dao->update($wechatUser['id'], $wechatInfo, 'id')) {
|
||||
throw new ValidateException('更新数据失败');
|
||||
}
|
||||
} else {
|
||||
if (!$this->dao->save($wechatInfo)) {
|
||||
throw new ValidateException('写入信息失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//user表没有用户,wechat_user表没有用户创建新用户
|
||||
//不存在则创建用户
|
||||
$userInfo = $this->transaction(function () use ($openid, $userServices, $wechatInfo, $spread_uid, $userType) {
|
||||
$userInfo = $userServices->setUserInfo($wechatInfo, (int)$spread_uid, $userType);
|
||||
if (!$userInfo) {
|
||||
throw new AuthException('生成User用户失败!');
|
||||
}
|
||||
$wechatInfo['uid'] = $userInfo->uid;
|
||||
$wechatInfo['add_time'] = $userInfo->add_time;
|
||||
$wechatUser = $this->dao->getOne(['openid' => $openid]);
|
||||
if ($wechatUser) {
|
||||
if (!$this->dao->update($wechatUser['id'], $wechatInfo, 'id')) {
|
||||
throw new ValidateException('更新数据失败');
|
||||
}
|
||||
} else {
|
||||
if (!$this->dao->save($wechatInfo)) {
|
||||
throw new AuthException('生成微信用户失败!');
|
||||
}
|
||||
}
|
||||
return $userInfo;
|
||||
});
|
||||
}
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 授权后获取用户信息
|
||||
* @param $openid
|
||||
* @param $user_type
|
||||
*/
|
||||
public function getAuthUserInfo($openid, $user_type)
|
||||
{
|
||||
$user = [];
|
||||
//兼容老用户
|
||||
$uids = $this->dao->getColumn(['unionid|openid' => $openid, 'is_del' => 0], 'uid,user_type', 'user_type');
|
||||
if ($uids) {
|
||||
$uid = $uids[$user_type]['uid'] ?? 0;
|
||||
if (!$uid) {
|
||||
$ids = array_column($uids, 'uid');
|
||||
$uid = $ids[0];
|
||||
}
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$user = $userServices->getUserInfo($uid);
|
||||
if (isset($user['status']) && !$user['status'])
|
||||
throw new ValidateException('您已被禁止登录,请联系管理员');
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新微信用户信息
|
||||
* @param $event
|
||||
* @return bool
|
||||
*/
|
||||
public function wechatUpdata($data)
|
||||
{
|
||||
[$uid, $userData] = $data;
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
if (!$userInfo = $userServices->getUserInfo($uid)) {
|
||||
return false;
|
||||
}
|
||||
/** @var LoginServices $loginService */
|
||||
$loginService = app()->make(LoginServices::class);
|
||||
$loginService->updateUserInfo($userData, $userInfo);
|
||||
//更新用户信息
|
||||
/** @var WechatUserServices $wechatUser */
|
||||
$wechatUser = app()->make(WechatUserServices::class);
|
||||
|
||||
$wechatUserInfo = [];
|
||||
if (isset($userData['nickname']) && $userData['nickname']) $wechatUserInfo['nickname'] = filter_emoji($userData['nickname'] ?? '');//姓名
|
||||
if (isset($userData['headimgurl']) && $userData['headimgurl']) $wechatUserInfo['headimgurl'] = $userData['headimgurl'] ?? '';//头像
|
||||
if (isset($userData['sex']) && $userData['sex']) $wechatUserInfo['sex'] = $userData['gender'] ?? '';//性别
|
||||
if (isset($userData['language']) && $userData['language']) $wechatUserInfo['language'] = $userData['language'] ?? '';//语言
|
||||
if (isset($userData['city']) && $userData['city']) $wechatUserInfo['city'] = $userData['city'] ?? '';//城市
|
||||
if (isset($userData['province']) && $userData['province']) $wechatUserInfo['province'] = $userData['province'] ?? '';//省份
|
||||
if (isset($userData['country']) && $userData['country']) $wechatUserInfo['country'] = $userData['country'] ?? '';//国家
|
||||
if (!empty($wechatUserInfo['nickname']) || !empty($wechatUserInfo['headimgurl'])) {
|
||||
$wechatUserInfo['is_complete'] = 1;
|
||||
} else {
|
||||
$wechatUserInfo['is_complete'] = 0;
|
||||
}
|
||||
if ($wechatUserInfo) {
|
||||
if (isset($userData['openid']) && $userData['openid'] && false === $wechatUser->update(['uid' => $userInfo['uid'], 'openid' => $userData['openid']], $wechatUserInfo)) {
|
||||
throw new ValidateException('更新失败');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信授权成功后
|
||||
* @param $event
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function wechatOauthAfter(array $data)
|
||||
{
|
||||
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $data;
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$spread_uid = (int)$spread_uid;
|
||||
if ($spread_uid && !$userServices->userExist($spread_uid)) {
|
||||
$spread_uid = 0;
|
||||
}
|
||||
//删除多余字段
|
||||
unset($wechatInfo['subscribe_scene'], $wechatInfo['qr_scene'], $wechatInfo['qr_scene_str']);
|
||||
if ($login_type) {
|
||||
$wechatInfo['login_type'] = $login_type;
|
||||
}
|
||||
if (!isset($wechatInfo['nickname']) || !$wechatInfo['nickname']) {
|
||||
if (isset($wechatInfo['phone']) && $wechatInfo['phone']) {
|
||||
$wechatInfo['nickname'] = substr_replace($wechatInfo['phone'], '****', 3, 4);
|
||||
} else {
|
||||
mt_srand();
|
||||
$wechatInfo['nickname'] = 'wx' . rand(100000, 999999);
|
||||
}
|
||||
} else {
|
||||
$wechatInfo['is_complete'] = 1;
|
||||
$wechatInfo['nickname'] = filter_emoji($wechatInfo['nickname']);
|
||||
}
|
||||
//统一用户处理:1:同一手机号用户 2:开放平台 3:openid
|
||||
$userInfo = [];
|
||||
if (isset($wechatInfo['phone']) && $wechatInfo['phone']) {
|
||||
$userInfo = $userServices->getOne(['phone' => $wechatInfo['phone']]);
|
||||
}
|
||||
if (!$userInfo) {
|
||||
if (isset($wechatInfo['unionid']) && $wechatInfo['unionid']) {
|
||||
$uid = $this->dao->value(['unionid' => $wechatInfo['unionid'], 'is_del' => 0], 'uid');
|
||||
if ($uid) {
|
||||
$userInfo = $userServices->getOne(['uid' => $uid]);
|
||||
} else {
|
||||
$userInfo = $this->getAuthUserInfo($openid, $userType);
|
||||
}
|
||||
} else {
|
||||
$userInfo = $this->getAuthUserInfo($openid, $userType);
|
||||
}
|
||||
}
|
||||
$uid = (int)($userInfo['uid'] ?? 0);
|
||||
$wechatInfo['user_type'] = $userType;
|
||||
//user表存在和wechat_user表同时存在
|
||||
return $this->transaction(function () use ($openid, $uid, $userInfo, $wechatInfo, $userServices, $spread_uid, $userType) {
|
||||
$wechatInfo['spread_uid'] = $spread_uid;
|
||||
$wechatInfo['uid'] = $uid;
|
||||
if ($userInfo) {
|
||||
if (isset($userInfo['status']) && !$userInfo['status'])
|
||||
throw new ValidateException('您已被禁止登录,请联系管理员');
|
||||
//更新用户表
|
||||
/** @var LoginServices $loginService */
|
||||
$loginService = app()->make(LoginServices::class);
|
||||
$loginService->updateUserInfo($wechatInfo, $userInfo);
|
||||
} else {
|
||||
//新增用户表
|
||||
$userInfo = $userServices->setUserInfo($wechatInfo, $spread_uid, $userType);
|
||||
if (!$userInfo) {
|
||||
throw new AuthException('生成User用户失败!');
|
||||
}
|
||||
//用户绑定客户事件
|
||||
if (!empty($wechatInfo['unionid'])) {
|
||||
event('user.client', [$userInfo['uid'], $wechatInfo['unionid']]);
|
||||
}
|
||||
//用户绑定成员事件
|
||||
if (!empty($userInfo['phone'])) {
|
||||
event('user.work', [$userInfo['uid'], $userInfo['phone']]);
|
||||
}
|
||||
}
|
||||
$uid = $userInfo['uid'];
|
||||
$wechatInfo['uid'] = $userInfo->uid;
|
||||
$wechatInfo['add_time'] = $userInfo->add_time;
|
||||
|
||||
//判断该类性用户在wechatUser中是否存在
|
||||
$wechatUser = $this->dao->getOne(['uid' => $uid, 'user_type' => $userType, 'is_del' => 0]);
|
||||
if (!$wechatUser) {
|
||||
$wechatUser = $this->dao->getOne(['openid' => $openid, 'is_del' => 0]);
|
||||
}
|
||||
if ($wechatUser) {
|
||||
if (isset($wechatInfo['phone']) && $wechatInfo['phone'] && $wechatUser['openid'] != $openid) {
|
||||
throw new ValidateException('该手机号已被注册');
|
||||
}
|
||||
if (!$this->dao->update($wechatUser['id'], $wechatInfo, 'id')) {
|
||||
throw new ValidateException('更新数据失败');
|
||||
}
|
||||
} else {
|
||||
if (!$this->dao->save($wechatInfo)) {
|
||||
throw new AuthException('生成微信用户失败!');
|
||||
}
|
||||
}
|
||||
return $userInfo;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息(同步)
|
||||
* @param array $openids
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function syncWechatUser(array $openids)
|
||||
{
|
||||
if (!$openids) {
|
||||
return [];
|
||||
}
|
||||
$wechatUser = $this->dao->getList([['openid', 'in', $openids]]);
|
||||
$noBeOpenids = $openids;
|
||||
if ($wechatUser) {
|
||||
$beOpenids = array_column($wechatUser, 'openid');
|
||||
$noBeOpenids = array_diff($openids, $beOpenids);
|
||||
if ($beOpenids) {
|
||||
$data = [];
|
||||
foreach ($beOpenids as $openid) {
|
||||
try {
|
||||
$info = OfficialAccount::getUserInfo($openid);
|
||||
} catch (\Throwable $e) {
|
||||
$info = [];
|
||||
}
|
||||
if (!$info) continue;
|
||||
$data['subscribe'] = $info['subscribe'] ?? 1;
|
||||
if (($info['subscribe'] ?? 1) == 1) {
|
||||
$data['unionid'] = $info['unionid'] ?? '';
|
||||
$data['nickname'] = $info['nickname'] ?? '';
|
||||
$data['sex'] = $info['sex'] ?? 0;
|
||||
$data['language'] = $info['language'] ?? '';
|
||||
$data['city'] = $info['city'] ?? '';
|
||||
$data['province'] = $info['province'] ?? '';
|
||||
$data['country'] = $info['country'] ?? '';
|
||||
$data['headimgurl'] = $info['headimgurl'] ?? '';
|
||||
$data['subscribe_time'] = $info['subscribe_time'] ?? '';
|
||||
$data['groupid'] = $info['groupid'] ?? 0;
|
||||
$data['remark'] = $info['remark'] ?? '';
|
||||
$data['tagid_list'] = isset($info['tagid_list']) && $info['tagid_list'] ? implode(',', $info['tagid_list']) : '';
|
||||
}
|
||||
$this->dao->update(['openid' => $info['openid']], $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $noBeOpenids;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user