chore: 添加静态资源文件和版本配置文件

添加大量图片、字体、音频等静态资源文件,包括用户界面图片、模板文件和安装界面素材
添加系统版本配置文件(.version)和常量定义文件(.constant)
更新供应商依赖文件,包括Symfony VarDumper组件和相关许可证文件
添加验证码配置文件(captcha.php)和.gitignore规则
This commit is contained in:
2026-03-06 20:14:23 +08:00
commit c9a98f342c
4485 changed files with 490986 additions and 0 deletions

View 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;
}
}

View 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);
}
}

View 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;
}

View 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;
}

View 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()));
}
}
}

View 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;
}
}

View 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');
}
}

View 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;
}
}

View 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');
}
}

View 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']);
}
}
}

View 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');
}
}

View 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']);
}
}
}

View 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
];
}
}

View 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;
}
}