You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
586 lines
22 KiB
586 lines
22 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
|
// +----------------------------------------------------------------------
|
|
// | Author: CRMEB Team <admin@crmeb.com>
|
|
// +----------------------------------------------------------------------
|
|
|
|
namespace app\services\work;
|
|
|
|
|
|
use app\dao\work\WorkClientDao;
|
|
use app\jobs\work\WorkClientJob;
|
|
use app\services\BaseServices;
|
|
use app\services\user\label\UserLabelServices;
|
|
use app\services\user\UserServices;
|
|
use crmeb\services\wechat\config\WorkConfig;
|
|
use crmeb\services\wechat\WechatResponse;
|
|
use crmeb\services\wechat\Work;
|
|
use crmeb\traits\ServicesTrait;
|
|
use think\exception\ValidateException;
|
|
use think\facade\Log;
|
|
|
|
/**
|
|
* 企业微信客户
|
|
* Class WorkClientServices
|
|
* @package app\services\work
|
|
* @mixin WorkClientDao
|
|
*/
|
|
class WorkClientServices extends BaseServices
|
|
{
|
|
|
|
use ServicesTrait;
|
|
|
|
/**
|
|
* WorkClientServices constructor.
|
|
* @param WorkClientDao $dao
|
|
*/
|
|
public function __construct(WorkClientDao $dao)
|
|
{
|
|
$this->dao = $dao;
|
|
}
|
|
|
|
/**
|
|
* @param array $where
|
|
* @param array $field
|
|
* @param bool $isPage
|
|
* @return array
|
|
*/
|
|
public function getList(array $where, array $field = ['*'], bool $isPage = true)
|
|
{
|
|
$page = $limit = 0;
|
|
if ($isPage) {
|
|
[$page, $limit] = $this->getPageValue();
|
|
}
|
|
$list = $this->dao->getDataList($where, $field, $page, $limit, 'create_time', [
|
|
'followOne' => function ($query) {
|
|
$query->with([
|
|
'member' => function ($query) {
|
|
$query->field(['userid', 'id', 'name', 'main_department'])
|
|
->with(['mastareDepartment']);
|
|
}
|
|
])->field(['userid', 'client_id', 'state', 'id']);
|
|
},
|
|
'follow' => function ($query) {
|
|
$query->field(['id', 'client_id'])->with(['tags']);
|
|
},
|
|
]);
|
|
foreach ($list as &$item) {
|
|
if (!empty($item['follow'])) {
|
|
$tags = [];
|
|
foreach ($item['follow'] as $value) {
|
|
if (!empty($value['tags'])) {
|
|
$tags = array_merge($tags, $value['tags']);
|
|
}
|
|
}
|
|
$newTags = [];
|
|
foreach ($tags as $tag) {
|
|
if (!in_array($tag['tag_name'], array_column($newTags, 'tag_name'))) {
|
|
$newTags[] = $tag;
|
|
}
|
|
}
|
|
$item['followOne']['tags'] = $newTags;
|
|
}
|
|
}
|
|
$count = $this->dao->count($where);
|
|
return compact('list', 'count');
|
|
}
|
|
|
|
/**
|
|
* 自动同步客户
|
|
* @param int $page
|
|
* @param string $cursor
|
|
* @return bool
|
|
*/
|
|
public function authGetExternalcontact(int $page = 1, string $cursor = '')
|
|
{
|
|
/** @var WorkConfig $config */
|
|
$config = app()->make(WorkConfig::class);
|
|
$corpId = $config->get('corpId');
|
|
if (!$corpId) {
|
|
return true;
|
|
}
|
|
/** @var WorkMemberServices $memberService */
|
|
$memberService = app()->make(WorkMemberServices::class);
|
|
$menmberList = $memberService->getDataList(['corp_id' => $corpId], ['userid'], $page, 10);
|
|
//没有数据就返回成功
|
|
if (!$menmberList) {
|
|
return true;
|
|
}
|
|
|
|
$userids = array_column($menmberList, 'userid');
|
|
$response = Work::getBatchClientList($userids, $cursor);
|
|
$externalContactList = $response['external_contact_list'] ?? [];
|
|
$external = [];
|
|
$followUser = [];//内部人员跟踪
|
|
$externalUserids = [];//客户信息
|
|
$this->transaction(function () use ($externalContactList, $corpId, $externalUserids, $followUser, $external) {
|
|
foreach ($externalContactList as $item) {
|
|
$externalContact = $item['external_contact'];
|
|
$unionid = $externalContact['unionid'] ?? '';
|
|
if (isset($externalContact['unionid'])) {
|
|
unset($externalContact['unionid']);
|
|
}
|
|
$corpName = $corpFullName = $position = '';
|
|
if (isset($externalContact['corp_name'])) {
|
|
$corpName = $externalContact['corp_name'];
|
|
unset($externalContact['corp_name']);
|
|
}
|
|
if (isset($externalContact['corp_full_name'])) {
|
|
$corpFullName = $externalContact['corp_full_name'];
|
|
unset($externalContact['corp_full_name']);
|
|
}
|
|
if (isset($externalContact['position'])) {
|
|
$position = $externalContact['position'];
|
|
unset($externalContact['position']);
|
|
}
|
|
|
|
$externalContact['position'] = $position;
|
|
$externalContact['external_profile'] = json_encode($externalContact['external_profile'] ?? []);
|
|
|
|
$followUserData = [
|
|
'userid' => $item['follow_info']['userid'],
|
|
'remark' => $item['follow_info']['remark'] ?? '',
|
|
'description' => $item['follow_info']['description'] ?? '',
|
|
'createtime' => $item['follow_info']['createtime'] ?? '',
|
|
'remark_corp_name' => $item['follow_info']['remark_corp_name'] ?? '',
|
|
'remark_mobiles' => json_encode($item['follow_info']['remark_mobiles'] ?? ''),
|
|
'add_way' => $item['follow_info']['add_way'] ?? '',
|
|
'oper_userid' => $item['follow_info']['oper_userid'] ?? '',
|
|
'create_time' => time(),
|
|
'tags' => [],
|
|
];
|
|
|
|
if (!empty($item['follow_info']['tag_id'])) {
|
|
$tagRes = Work::getCorpTags($item['follow_info']['tag_id']);
|
|
foreach ($tagRes['tag_group'] ?? [] as $group) {
|
|
foreach ($group['tag'] as $tag) {
|
|
$followUserData['tags'][] = [
|
|
'group_name' => $group['group_name'] ?? '',
|
|
'tag_name' => $tag['name'] ?? '',
|
|
'type' => $tag['type'] ?? 1,
|
|
'tag_id' => $tag['id'],
|
|
'create_time' => time()
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
$followUser[$externalContact['external_userid']] = $followUserData;
|
|
$externalUserids[] = $externalContact['external_userid'];
|
|
$externalUserid = $externalContact['external_userid'];
|
|
$externalContact['corp_id'] = $corpId;
|
|
$externalContact['unionid'] = $unionid;
|
|
$externalContact['corp_name'] = $corpName;
|
|
$externalContact['corp_full_name'] = $corpFullName;
|
|
if ($this->dao->count(['external_userid' => $externalUserid, 'corp_id' => $corpId])) {
|
|
unset($externalContact['external_userid']);
|
|
$this->dao->update(['external_userid' => $externalUserid], $externalContact);
|
|
} else {
|
|
$externalContact['create_time'] = time();
|
|
$externalContact['update_time'] = time();
|
|
$external[] = $externalContact;
|
|
}
|
|
}
|
|
if ($external) {
|
|
$this->dao->saveAll($external);
|
|
}
|
|
$clientList = $this->dao->getColumn([['external_userid', 'in', $externalUserids], ['corp_id', '=', $corpId]], 'id', 'external_userid');
|
|
/** @var WorkClientFollowServices $followService */
|
|
$followService = app()->make(WorkClientFollowServices::class);
|
|
if ($followUser) {
|
|
/** @var WorkClientFollowTagsServices $tagService */
|
|
$tagService = app()->make(WorkClientFollowTagsServices::class);
|
|
foreach ($followUser as $userid => $items) {
|
|
$items['client_id'] = $clientList[$userid];
|
|
if (($id = $followService->value(['client_id' => $clientList[$userid], 'userid' => $items['userid']], 'id'))) {
|
|
$followService->update($id, [
|
|
'remark' => $items['remark'],
|
|
'description' => $items['description'],
|
|
'createtime' => $items['createtime'],
|
|
'remark_corp_name' => $items['remark_corp_name'],
|
|
'remark_mobiles' => $items['remark_mobiles'],
|
|
'add_way' => $items['add_way'],
|
|
'oper_userid' => $items['oper_userid'],
|
|
]);
|
|
} else {
|
|
$res = $followService->save($items);
|
|
$id = $res->id;
|
|
}
|
|
if (!empty($items['tags'])) {
|
|
$tagService->delete(['follow_id' => $id]);
|
|
foreach ($items['tags'] as &$tag) {
|
|
$tag['follow_id'] = $id;
|
|
}
|
|
$tagService->saveAll($items['tags']);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
if (isset($response['next_cursor']) && $response['next_cursor']) {
|
|
WorkClientJob::dispatchDo('authClient', [$page, $response['next_cursor'] ?? '']);
|
|
} else if (count($userids) >= 10 && empty($response['next_cursor'])) {
|
|
WorkClientJob::dispatchDo('authClient', [$page + 1, '']);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public function saveClientTags(array $tagGroup)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* 创建客户
|
|
* @param array $payload
|
|
* @return mixed
|
|
*/
|
|
public function createClient(array $payload)
|
|
{
|
|
$corpId = $payload['ToUserName'];//企业id
|
|
$externalUserID = $payload['ExternalUserID'];//外部企业userid
|
|
$state = $payload['State'] ?? '';//扫码值
|
|
$userId = $payload['UserID'];//成员userid
|
|
|
|
//保存客户
|
|
$clientId = $this->saveOrUpdateClient($corpId, $externalUserID, $userId);
|
|
|
|
//发送欢迎语
|
|
try {
|
|
event('work.welcome', [$payload['WelcomeCode'] ?? '', $state, $clientId, $userId]);
|
|
} catch (\Throwable $e) {
|
|
Log::error([
|
|
'message' => '发送欢迎语失败:' . $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|
|
|
|
//设置欢客户标签
|
|
try {
|
|
event('work.label', [$state, $userId, $externalUserID]);
|
|
} catch (\Throwable $e) {
|
|
Log::error([
|
|
'message' => '设置欢客户标签失败:' . $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|
|
|
|
//关联客户与商城用户
|
|
try {
|
|
event('work.user', [$clientId]);
|
|
} catch (\Throwable $e) {
|
|
Log::error([
|
|
'message' => '关联客户与商城用户失败:' . $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|
|
|
|
return $clientId;
|
|
}
|
|
|
|
/**
|
|
* 更新客户信息
|
|
* @param array $payload
|
|
* @return mixed
|
|
*/
|
|
public function updateClient(array $payload)
|
|
{
|
|
$corpId = $payload['ToUserName'];
|
|
$externalUserID = $payload['ExternalUserID'];
|
|
$userId = $payload['UserID'];//成员serid
|
|
|
|
$clientId = $this->saveOrUpdateClient($corpId, $externalUserID, $userId);
|
|
|
|
//关联客户与商城用户
|
|
try {
|
|
event('work.user', [$clientId]);
|
|
} catch (\Throwable $e) {
|
|
Log::error([
|
|
'message' => '关联客户与商城用户失败:' . $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|
|
|
|
return $clientId;
|
|
}
|
|
|
|
/**
|
|
* 企业成员删除客户
|
|
* @param array $payload
|
|
* @return bool
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\DbException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public function deleteClient(array $payload)
|
|
{
|
|
$corpId = $payload['ToUserName'];
|
|
$externalUserID = $payload['ExternalUserID'];
|
|
$userId = $payload['UserID'];//成员serid
|
|
$clientInfo = $this->dao->get(['external_userid' => $externalUserID, 'corp_id' => $corpId], ['id']);
|
|
if ($clientInfo) {
|
|
$this->transaction(function () use ($clientInfo, $userId) {
|
|
$this->dao->destroy($clientInfo->id);
|
|
/** @var WorkClientFollowServices $followService */
|
|
$followService = app()->make(WorkClientFollowServices::class);
|
|
$followService->update(['client_id' => $clientInfo->id, 'userid' => $userId], ['is_del_user' => 1]);
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 客户删除企业微信成员
|
|
* @param array $payload
|
|
* @return bool
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\DbException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public function deleteFollowClient(array $payload)
|
|
{
|
|
$corpId = $payload['ToUserName'];
|
|
$externalUserID = $payload['ExternalUserID'];
|
|
$userId = $payload['UserID'];//成员serid
|
|
$clientInfo = $this->dao->get(['external_userid' => $externalUserID, 'corp_id' => $corpId], ['id']);
|
|
/** @var WorkClientFollowServices $followService */
|
|
$followService = app()->make(WorkClientFollowServices::class);
|
|
if ($clientInfo) {
|
|
$followService->update(['client_id' => $clientInfo->id, 'userid' => $userId], ['is_del_user' => 1]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 更新或者添加客户信息
|
|
* @param string $corpId
|
|
* @param string $externalUserID
|
|
* @param string $userId
|
|
* @return mixed
|
|
*/
|
|
public function saveOrUpdateClient(string $corpId, string $externalUserID, string $userId)
|
|
{
|
|
$response = Work::getClientInfo($externalUserID);
|
|
$externalContact = $response['external_contact'] ?? [];
|
|
$followUser = $response['follow_user'] ?? [];
|
|
$res = true;
|
|
$externalContact['corp_id'] = $corpId;
|
|
$externalContact['external_profile'] = json_encode($externalContact['external_profile'] ?? []);
|
|
$clientId = $this->dao->value(['external_userid' => $externalContact['external_userid'], 'corp_id' => $corpId], 'id');
|
|
|
|
try {
|
|
$clientId = $this->transaction(function () use ($userId, $res, $clientId, $externalContact, $followUser) {
|
|
if ($clientId) {
|
|
$this->dao->update($clientId, $externalContact);
|
|
} else {
|
|
$res = $this->dao->save($externalContact);
|
|
$clientId = $res->id;
|
|
}
|
|
$userids = [];
|
|
$res1 = false;
|
|
foreach ($followUser as &$item) {
|
|
$item['create_time'] = time();
|
|
if ($userId === $item['userid']) {
|
|
$res1 = true;
|
|
}
|
|
$userids[] = $item['userid'];
|
|
$item['client_id'] = $clientId;
|
|
if (isset($item['wechat_channels'])) {
|
|
unset($item['wechat_channels']);
|
|
}
|
|
}
|
|
if (!$res1 && $userId) {
|
|
$followUser[] = [
|
|
'client_id' => $clientId,
|
|
'userid' => $userId,
|
|
'createtime' => time(),
|
|
'tags' => []
|
|
];
|
|
}
|
|
//添加了此外部联系人的企业成员
|
|
if ($followUser) {
|
|
/** @var WorkClientFollowServices $followService */
|
|
$followService = app()->make(WorkClientFollowServices::class);
|
|
/** @var WorkClientFollowTagsServices $tagService */
|
|
$tagService = app()->make(WorkClientFollowTagsServices::class);
|
|
foreach ($followUser as $item) {
|
|
if (($id = $followService->value(['client_id' => $clientId, 'userid' => $item['userid']], 'id'))) {
|
|
$followService->update($id, [
|
|
'remark' => $item['remark'],
|
|
'description' => $item['description'],
|
|
'remark_corp_name' => $item['remark_corp_name'] ?? '',
|
|
'add_way' => $item['add_way'] ?? '',
|
|
'oper_userid' => $item['oper_userid'] ?? '',
|
|
]);
|
|
} else {
|
|
$res = $followService->save($item);
|
|
$id = $res->id;
|
|
}
|
|
$tagService->delete(['follow_id' => $id]);
|
|
if (!empty($item['tags'])) {
|
|
$tagsNews = [];
|
|
foreach ($item['tags'] as $tag) {
|
|
$tag['follow_id'] = $id;
|
|
$tagsNews[] = $tag;
|
|
}
|
|
$tagService->saveAll($tagsNews);
|
|
}
|
|
}
|
|
}
|
|
if (!$res) {
|
|
throw new ValidateException('保存失败');
|
|
}
|
|
return $clientId;
|
|
});
|
|
|
|
} catch (\Throwable $e) {
|
|
Log::error([
|
|
'message' => $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|
|
return $clientId;
|
|
}
|
|
|
|
/**
|
|
* @param string $userid
|
|
* @param array $clientInfo
|
|
* @return array
|
|
*/
|
|
public function getClientInfo(string $userid, array $clientInfo)
|
|
{
|
|
$clientInfo['userInfo'] = [];
|
|
if ($clientInfo['uid']) {
|
|
/** @var UserServices $make */
|
|
$make = app()->make(UserServices::class);
|
|
$userInfo = $make->get($clientInfo['uid'], ['*'], ['label', 'userGroup', 'spreadUser']);
|
|
if ($userInfo) {
|
|
$clientInfo['userInfo'] = $userInfo->toArray();
|
|
$clientInfo['userInfo']['birthday'] = $clientInfo['userInfo']['birthday'] ? date('Y-m-d', $clientInfo['userInfo']['birthday']) : '';
|
|
}
|
|
}
|
|
return $clientInfo;
|
|
}
|
|
|
|
/**
|
|
* 异步批量设置标签
|
|
* @param array $addTag
|
|
* @param array $removeTag
|
|
* @param array $userId
|
|
* @param array $where
|
|
* @param int $isAll
|
|
* @return bool
|
|
*/
|
|
public function synchBatchLabel(array $addTag, array $removeTag, array $userId, array $where, int $isAll = 0)
|
|
{
|
|
if ($isAll) {
|
|
$clientList = $this->dao->getDataList($where, ['external_userid', 'id', 'unionid', 'uid'], 0, 0, null, ['followOne']);
|
|
} else {
|
|
$clientList = $this->dao->getDataList(['external_userid' => $userId], ['external_userid', 'id', 'unionid', 'uid'], 0, 0, null, ['followOne']);
|
|
}
|
|
$batchClient = [];
|
|
foreach ($clientList as $item) {
|
|
if (!empty($item['followOne'])) {
|
|
$batchClient[] = [
|
|
'external_userid' => $item['external_userid'],
|
|
'userid' => $item['followOne']['userid'],
|
|
'add_tag' => $addTag,
|
|
'remove_tag' => $removeTag,
|
|
];
|
|
}
|
|
}
|
|
if ($batchClient) {
|
|
foreach ($batchClient as $item) {
|
|
WorkClientJob::dispatchDo('setLabel', [$item]);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 设置客户标签
|
|
* @param array $markTag
|
|
* @return WechatResponse|false
|
|
*/
|
|
public function setClientMarkTag(array $markTag)
|
|
{
|
|
try {
|
|
$res = Work::markTags($markTag['userid'], $markTag['external_userid'], $markTag['add_tag'], $markTag['remove_tag']);
|
|
$res = new WechatResponse($res);
|
|
//同步标签后同步用户信息
|
|
/** @var WorkConfig $config */
|
|
$config = app()->make(WorkConfig::class);
|
|
$corpId = $config->get('corpId');
|
|
WorkClientJob::dispatchSece(2, 'saveClientInfo', [$corpId, $markTag['external_userid'], $markTag['userid']]);
|
|
return $res;
|
|
} catch (\Throwable $e) {
|
|
Log::error([
|
|
'message' => '修改客户标签发生错误:' . $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 查找成员下附带的客户人数
|
|
* @param array $where
|
|
* @return int
|
|
*/
|
|
public function getUserIdsByCount(array $where)
|
|
{
|
|
if ($where['is_all']) {
|
|
unset($where['time'], $where['label'], $where['notLabel']);
|
|
}
|
|
$where['timeKey'] = 'create_time';
|
|
|
|
if (!empty($where['label'])) {
|
|
/** @var UserLabelServices $service */
|
|
$service = app()->make(UserLabelServices::class);
|
|
$tagId = $service->getColumn([
|
|
['id', 'in', $where['label']],
|
|
], 'tag_id');
|
|
$where['label'] = array_unique($tagId);
|
|
}
|
|
if (!empty($where['notLabel'])) {
|
|
/** @var UserLabelServices $service */
|
|
$service = app()->make(UserLabelServices::class);
|
|
$tagId = $service->getColumn([
|
|
['id', 'in', $where['notLabel']],
|
|
], 'tag_id');
|
|
$where['notLabel'] = array_unique($tagId);
|
|
}
|
|
|
|
return $this->dao->getClientCount($where);
|
|
}
|
|
|
|
/**
|
|
* 解绑用户
|
|
* @param int $uid
|
|
*/
|
|
public function unboundUser(int $uid)
|
|
{
|
|
try {
|
|
$this->dao->update(['uid' => $uid], ['uid' => 0]);
|
|
} catch (\Throwable $e) {
|
|
Log::error([
|
|
'message' => '解绑用户失败:' . $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|