私域操盘手 - 调整微信号的权重策略计算规则,重构为适配器兼容扩展规则
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace app\cunkebao\controller\wechat;
|
||||
|
||||
use app\common\model\WechatAccount as WechatAccountModel;
|
||||
use AccountWeight\WechatAccountWeightAssessment;
|
||||
use app\common\model\WechatFriendShip as WechatFriendShipModel;
|
||||
use app\common\model\WechatRestricts as WechatRestrictsModel;
|
||||
use app\cunkebao\controller\BaseController;
|
||||
@@ -78,77 +78,6 @@ class GetWechatOnDeviceSummarizeV1Controller extends BaseController
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间相差几个月
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return float|int
|
||||
* @throws \DateMalformedStringException
|
||||
*/
|
||||
protected function getDateDiff(string $wechatId): int
|
||||
{
|
||||
$currentData = new \DateTime(date('Y-m-d H:i:s', time()));
|
||||
$registerDate = new \DateTime($this->getRegisterDate($wechatId));
|
||||
|
||||
$interval = date_diff($currentData, $registerDate);
|
||||
|
||||
return $interval->y * 12 + $interval->m;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算账号年龄权重
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return int
|
||||
* @throws \DateMalformedStringException
|
||||
*/
|
||||
protected function _calculAgeWeight(string $wechatId): int
|
||||
{
|
||||
// 规定账号年龄五年起拥有最高权重
|
||||
$cha = ceil($this->getDateDiff($wechatId) / 60) * 100;
|
||||
|
||||
return $cha > 100 ? 100 : $cha;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算活跃度权重
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return int
|
||||
*/
|
||||
protected function _calActivityWeigth(string $wechatId): int
|
||||
{
|
||||
// 规定每天发送50条消息起拥有最高权重
|
||||
$cha = ceil($this->getChatTimesPerDay($wechatId) / 50) * 100;
|
||||
|
||||
return $cha > 100 ? 100 : $cha;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算限制影响权重
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return int
|
||||
*/
|
||||
protected function _calRestrictWeigth(string $wechatId): int
|
||||
{
|
||||
$list = $this->getRestrict($wechatId); // 2
|
||||
$gtmd = 10 - count($list); // 规定没有限制记录拥有最高权重,10条以上权重为0
|
||||
|
||||
return ($gtmd < 0 ? 0 : $gtmd) * 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算实名认证权重
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return int
|
||||
*/
|
||||
protected function _calRealNameWeigth(string $wechatId): int
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算好友数量(每5权重=1好友,最多20个)
|
||||
*
|
||||
@@ -179,20 +108,17 @@ class GetWechatOnDeviceSummarizeV1Controller extends BaseController
|
||||
*/
|
||||
protected function getAccountWeight(string $wechatId): array
|
||||
{
|
||||
$ageWeight = $this->_calculAgeWeight($wechatId); // 账号年龄权重
|
||||
$activityWeigth = $this->_calActivityWeigth($wechatId); // 计算活跃度权重
|
||||
$restrictWeight = $this->_calRestrictWeigth($wechatId); // 计算限制影响权重
|
||||
$realNameWeight = $this->_calRealNameWeigth($wechatId); // 计算实名认证权重
|
||||
// 微信账号加友权重评估
|
||||
$assessment = new WechatAccountWeightAssessment();
|
||||
$assessment->settingFactor($wechatId);
|
||||
|
||||
$scope = ceil(($ageWeight + $activityWeigth + $restrictWeight + $realNameWeight) / 4); // 计算总分
|
||||
|
||||
return compact(
|
||||
'scope',
|
||||
'ageWeight',
|
||||
'activityWeigth',
|
||||
'restrictWeight',
|
||||
'realNameWeight'
|
||||
);
|
||||
return [
|
||||
'ageWeight' => $assessment->calculAgeWeight()->getResult(), // 账号年龄权重
|
||||
'activityWeigth' => $assessment->calculActivityWeigth()->getResult(), // 计算活跃度权重
|
||||
'restrictWeight' => $assessment->calculRestrictWeigth()->getResult(), // 计算限制影响权重
|
||||
'realNameWeight' => $assessment->calculRealNameWeigth()->getResult(), // 计算实名认证权重
|
||||
'scope' => $assessment->getWeightScope(), // 计算总分
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace AccountWeight\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class WechatAccountWeightAssessmentException extends \Exception
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __construct($message = '', $code = 22921, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
42
Server/extend/AccountWeight/UnitWeight/ActivityWeigth.php
Normal file
42
Server/extend/AccountWeight/UnitWeight/ActivityWeigth.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace AccountWeight\UnitWeight;
|
||||
|
||||
use library\interfaces\WechatAccountWeightResultSet as WechatAccountWeightResultSetInterface;
|
||||
|
||||
class ActivityWeigth implements WechatAccountWeightResultSetInterface
|
||||
{
|
||||
private $weight;
|
||||
|
||||
/**
|
||||
* 获取每天聊天次数。
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return int
|
||||
*/
|
||||
private function getChatTimesPerDay(string $wechatId): int
|
||||
{
|
||||
return mt_rand(0, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function settingFactor($wechatId): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$times = intval($this->getChatTimesPerDay($wechatId) / 50 * 100);
|
||||
|
||||
// 规定每天发送50条消息起拥有最高权重
|
||||
$this->weight = $times > 100 ? 100 : $times;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getResult(): int
|
||||
{
|
||||
return $this->weight ?: 0;
|
||||
}
|
||||
}
|
||||
59
Server/extend/AccountWeight/UnitWeight/AgeWeight.php
Normal file
59
Server/extend/AccountWeight/UnitWeight/AgeWeight.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace AccountWeight\UnitWeight;
|
||||
|
||||
use library\interfaces\WechatAccountWeightResultSet as WechatAccountWeightResultSetInterface;
|
||||
|
||||
class AgeWeight implements WechatAccountWeightResultSetInterface
|
||||
{
|
||||
private $weight;
|
||||
|
||||
/**
|
||||
* 计算账号年龄(从创建时间到现在)
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return string
|
||||
*/
|
||||
private function getRegisterDate(string $wechatId): string
|
||||
{
|
||||
return date('Y-m-d H:i:s', strtotime('-15 months'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间相差几个月
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return int
|
||||
* @throws \DateMalformedStringException
|
||||
*/
|
||||
private function getDateTimeDiff(string $wechatId): int
|
||||
{
|
||||
$currentData = new \DateTime(date('Y-m-d H:i:s', time()));
|
||||
$registerDate = new \DateTime($this->getRegisterDate($wechatId));
|
||||
|
||||
$interval = date_diff($currentData, $registerDate);
|
||||
|
||||
return $interval->y * 12 + $interval->m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function settingFactor($wechatId): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$cha = ceil($this->getDateTimeDiff($wechatId) / 60) * 100;
|
||||
|
||||
// 规定账号年龄五年起拥有最高权重
|
||||
$this->weight = $cha > 100 ? 100 : $cha;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getResult(): int
|
||||
{
|
||||
return $this->weight ?: 0;
|
||||
}
|
||||
}
|
||||
39
Server/extend/AccountWeight/UnitWeight/RealNameWeight.php
Normal file
39
Server/extend/AccountWeight/UnitWeight/RealNameWeight.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace AccountWeight\UnitWeight;
|
||||
|
||||
use library\interfaces\WechatAccountWeightResultSet as WechatAccountWeightResultSetInterface;
|
||||
|
||||
class RealNameWeight implements WechatAccountWeightResultSetInterface
|
||||
{
|
||||
private $weight;
|
||||
|
||||
/**
|
||||
* 使用微信要求必须得实名,所以统一返回满分
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function hereWeGo(): int
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function settingFactor($wechatId): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$this->weight = $this->hereWeGo();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getResult(): int
|
||||
{
|
||||
return $this->weight ?: 0;
|
||||
}
|
||||
}
|
||||
50
Server/extend/AccountWeight/UnitWeight/RestrictWeight.php
Normal file
50
Server/extend/AccountWeight/UnitWeight/RestrictWeight.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace AccountWeight\UnitWeight;
|
||||
|
||||
use app\common\model\WechatRestricts as WechatRestrictsModel;
|
||||
use library\interfaces\WechatAccountWeightResultSet as WechatAccountWeightResultSetInterface;
|
||||
|
||||
class RestrictWeight implements WechatAccountWeightResultSetInterface
|
||||
{
|
||||
private $weight;
|
||||
|
||||
/**
|
||||
* 获取限制记录
|
||||
*
|
||||
* @param string $wechatId
|
||||
* @return int
|
||||
*/
|
||||
private function getRestrictCount(string $wechatId): int
|
||||
{
|
||||
return WechatRestrictsModel::alias('r')
|
||||
->field(
|
||||
[
|
||||
'r.id', 'r.restrictTime date', 'r.level', 'r.reason'
|
||||
]
|
||||
)
|
||||
->where('r.wechatId', $wechatId)->select()
|
||||
->count('*');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function settingFactor($wechatId): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$restrict = 10 - $this->getRestrictCount($wechatId);
|
||||
|
||||
// 规定没有限制记录拥有最高权重,10条以上权重为0
|
||||
$this->weight = ($restrict < 0 ? 0 : $restrict) * 10;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getResult(): int
|
||||
{
|
||||
return $this->weight ?: 0;
|
||||
}
|
||||
}
|
||||
127
Server/extend/AccountWeight/WechatAccountWeightAssessment.php
Normal file
127
Server/extend/AccountWeight/WechatAccountWeightAssessment.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace AccountWeight;
|
||||
|
||||
use AccountWeight\Exceptions\WechatAccountWeightAssessmentException as WeightAssessmentException;
|
||||
use library\ClassTable;
|
||||
use library\interfaces\WechatAccountWeightResultSet as WechatAccountWeightResultSetInterface;
|
||||
use library\interfaces\WechatAccountWeightAssessment as WechatAccountWeightAssessmentInterface;
|
||||
|
||||
class WechatAccountWeightAssessment implements WechatAccountWeightAssessmentInterface
|
||||
{
|
||||
private $wechatId;
|
||||
private $ageWeight;
|
||||
private $activityWeigth;
|
||||
private $restrictWeight;
|
||||
private $realNameWeight;
|
||||
|
||||
/**
|
||||
* 获取言
|
||||
* @return string
|
||||
* @throws WechatAccountWeightAssessmentException
|
||||
*/
|
||||
private function getWechatId(): string
|
||||
{
|
||||
if (empty($this->wechatId)) {
|
||||
throw new WeightAssessmentException('缺少验证参数');
|
||||
}
|
||||
|
||||
return $this->wechatId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function calculAgeWeight(): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$AgeWeight = ClassTable::getSelfInstance()->getInstance(UnitWeight\AgeWeight::class);
|
||||
|
||||
if (!$this->ageWeight) {
|
||||
$this->ageWeight = $AgeWeight->settingFactor(
|
||||
$this->getWechatId()
|
||||
)
|
||||
->getResult();
|
||||
}
|
||||
|
||||
return $AgeWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function calculActivityWeigth(): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$ActivityWeigth = ClassTable::getSelfInstance()->getInstance(UnitWeight\ActivityWeigth::class);
|
||||
|
||||
if (!$this->activityWeigth) {
|
||||
$this->activityWeigth = $ActivityWeigth->settingFactor(
|
||||
$this->getWechatId()
|
||||
)
|
||||
->getResult();
|
||||
}
|
||||
|
||||
return $ActivityWeigth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function calculRestrictWeigth(): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$RestrictWeight = ClassTable::getSelfInstance()->getInstance(UnitWeight\RestrictWeight::class);
|
||||
|
||||
if (!$this->restrictWeight) {
|
||||
$this->restrictWeight = $RestrictWeight->settingFactor(
|
||||
$this->getWechatId()
|
||||
)
|
||||
->getResult();
|
||||
}
|
||||
|
||||
return $RestrictWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function calculRealNameWeigth(): WechatAccountWeightResultSetInterface
|
||||
{
|
||||
$AccountWeight = ClassTable::getSelfInstance()->getInstance(UnitWeight\RealNameWeight::class);
|
||||
|
||||
if (!$this->realNameWeight) {
|
||||
$this->realNameWeight = $AccountWeight->settingFactor(
|
||||
$this->getWechatId()
|
||||
)
|
||||
->getResult();
|
||||
}
|
||||
|
||||
return $AccountWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getWeightScope(): int
|
||||
{
|
||||
return ceil(
|
||||
(
|
||||
$this->calculAgeWeight()->getResult() +
|
||||
$this->calculActivityWeigth()->getResult() +
|
||||
$this->calculRestrictWeigth()->getResult() +
|
||||
$this->calculRealNameWeigth()->getResult()
|
||||
) / 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function settingFactor($params): WechatAccountWeightAssessmentInterface
|
||||
{
|
||||
if (!is_string($params)) {
|
||||
throw new WeightAssessmentException('参数错误,只能传微信ID');
|
||||
}
|
||||
|
||||
$this->wechatId = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
243
Server/extend/library/ClassTable.php
Normal file
243
Server/extend/library/ClassTable.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace library;
|
||||
|
||||
use library\interfaces\CallMap as CallMapInterface;
|
||||
|
||||
class ClassTable implements CallMapInterface
|
||||
{
|
||||
/**
|
||||
* 类映射表
|
||||
*
|
||||
* @var array|mixed
|
||||
*/
|
||||
protected $hashTable;
|
||||
|
||||
/**
|
||||
* 最近绑定的对象
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $latest = [];
|
||||
|
||||
protected static $selfInstance;
|
||||
|
||||
/**
|
||||
* 分组
|
||||
*
|
||||
* @param string $tag
|
||||
* @param array $collection
|
||||
*/
|
||||
private function _tag(string $tag, array $collection): void
|
||||
{
|
||||
$this->{$tag} = (array)($this->{$tag} ?? []);
|
||||
|
||||
// 将值追加到数组中。
|
||||
$this->{$tag} = array_merge($this->{$tag}, $collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回标签的元素
|
||||
*
|
||||
* @param string $tag
|
||||
* @return array|null
|
||||
*/
|
||||
private function getTagElements(string $tag): ?array
|
||||
{
|
||||
return $this->{'__tag__' . $tag} ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并返回一个对象
|
||||
*
|
||||
* @param object|string $class
|
||||
* @param array $parameters
|
||||
* @return object
|
||||
* @throws \Throwable
|
||||
*/
|
||||
private function _getInstance($class, $parameters = []): object
|
||||
{
|
||||
if ($class instanceof \Closure) {
|
||||
return $class();
|
||||
}
|
||||
|
||||
if (is_object($class)) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
if (class_exists(strval($class))) {
|
||||
// 创建该类的新实例
|
||||
return method_exists($class, 'newInstance') ? $class::newInstance(...$parameters) : new $class(...$parameters);
|
||||
}
|
||||
|
||||
throw new \Exception(static::class . ': 调用了未定义的函数', 532341);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单例
|
||||
*
|
||||
* @param $container
|
||||
* @return void
|
||||
*/
|
||||
public static function getSelfInstance(): CallMapInterface
|
||||
{
|
||||
if (!self::$selfInstance instanceof self) {
|
||||
self::$selfInstance = new self();
|
||||
}
|
||||
|
||||
return self::$selfInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function bind($alias, $instance = null, string $tag = null): void
|
||||
{
|
||||
if (is_array($alias)) {
|
||||
foreach ($alias as $name => $instance) {
|
||||
$this->hashTable[$name] = $instance;
|
||||
}
|
||||
|
||||
$this->latest = array_keys($alias);
|
||||
|
||||
if ($tag) {
|
||||
$this->tag($tag, $this->latest);
|
||||
}
|
||||
} else {
|
||||
$this->hashTable[$alias] = $instance;
|
||||
$this->latest = [$alias];
|
||||
|
||||
if ($tag) {
|
||||
$this->tag($tag, $this->latest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function dealloc($alias): void
|
||||
{
|
||||
dealloc($this->hashTable, $alias);
|
||||
|
||||
$insects = array_intersect((array)$alias, $this->latest);
|
||||
|
||||
if ($insects) {
|
||||
$this->latest = array_diff($this->latest, $insects);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function revokeShared($alias): void
|
||||
{
|
||||
$instance = $this->alias($alias);
|
||||
|
||||
$this->bind($alias, get_class($instance));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function tag(string $tag, array $lastElements = []): CallMapInterface
|
||||
{
|
||||
$this->_tag('__tag__' . $tag, ($lastElements ?: $this->latest));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getInstance($class, ...$parameters): object
|
||||
{
|
||||
$name = is_object($class) ? get_class($class) : $class;
|
||||
|
||||
if (!$this->has($name)) {
|
||||
return $this->hashTable[$name] = $this->_getInstance($class, $parameters);
|
||||
}
|
||||
|
||||
return $this->_getInstance($this->hashTable[$name], $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function copy($class, string $name = null): object
|
||||
{
|
||||
$instance = clone $this->getInstance($class);
|
||||
|
||||
if (!is_null($name)) {
|
||||
$this->bind($name, $instance);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getClassByTag(string $tag): ?array
|
||||
{
|
||||
$elements = $this->getTagElements($tag);
|
||||
|
||||
if ($elements) {
|
||||
foreach ($elements as $alias) {
|
||||
if ($this->has($alias)) {
|
||||
$consequences[$alias] = $this->hashTable[$alias];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $consequences ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function has(string $alias): bool
|
||||
{
|
||||
return isset($this->hashTable[$alias]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function detect(string $precept, $class, string $alias = null): ?object
|
||||
{
|
||||
// Scheme 是临时对象,不会被保存
|
||||
if (!$this->has($precept)) {
|
||||
// 如果想共享该对象,可以调用 bind 方法将其注册到哈希表中
|
||||
$instance = $this->_getInstance($class);
|
||||
|
||||
if ($alias) {
|
||||
$this->hashTable[$alias] = $instance;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
return $this->hashTable[$precept];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function alias($alias, array $parameters = []): ?object
|
||||
{
|
||||
return $this->getInstance($alias, ...$parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getShared($alias, array $parameters = []): ?object
|
||||
{
|
||||
$instance = $this->alias($alias, $parameters);
|
||||
|
||||
$this->bind($alias, $instance);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
105
Server/extend/library/Interfaces/CallMap.php
Normal file
105
Server/extend/library/Interfaces/CallMap.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace library\interfaces;
|
||||
|
||||
interface CallMap
|
||||
{
|
||||
/**
|
||||
* 创建并返回一个对象。
|
||||
* 如果需要获取需要初始化参数的对象实例,请使用此方法。
|
||||
*
|
||||
* @param object|string $class
|
||||
* @param null|mixed $parameters
|
||||
* @return object
|
||||
*/
|
||||
public function getInstance($class, ...$parameters): object;
|
||||
|
||||
/**
|
||||
* 通过别名获取对象。
|
||||
*
|
||||
* @param string|int $alias
|
||||
* @param array $parameters
|
||||
* @return object|null
|
||||
*/
|
||||
public function alias($alias, array $parameters = []): ?object;
|
||||
|
||||
/**
|
||||
* 返回对象的副本。
|
||||
*
|
||||
* @param object|string|alias $class
|
||||
* @param string|null $name
|
||||
* @return object
|
||||
*/
|
||||
public function copy($class, string $name = null): object;
|
||||
|
||||
/**
|
||||
* 解析一个服务,解析后的服务会被存储在DI容器中,
|
||||
* 后续对此服务的请求将返回同一个实例
|
||||
*
|
||||
* @param string|int $alias
|
||||
* @param array $parameters
|
||||
* @return object|null
|
||||
*/
|
||||
public function getShared($alias, array $parameters = []): ?object;
|
||||
|
||||
/**
|
||||
* 查询一个对象,如果没有则返回另一个对象。
|
||||
*
|
||||
* @param string $precept
|
||||
* @param string|object $scheme
|
||||
* @param string $alias
|
||||
* @return object|null
|
||||
*/
|
||||
public function detect(string $precept, $scheme, string $stored = null): ?object;
|
||||
|
||||
/**
|
||||
* 通过标签从哈希表中获取集合。此方法通常返回一个数组。
|
||||
*
|
||||
* @param string $tag
|
||||
* @return array
|
||||
*/
|
||||
public function getClassByTag(string $tag): ?array;
|
||||
|
||||
/**
|
||||
* 创建标签。
|
||||
*
|
||||
* @param string $tag
|
||||
* @param array $lastElements
|
||||
* @return mixed
|
||||
*/
|
||||
public function tag(string $tag, array $lastElements = []);
|
||||
|
||||
/**
|
||||
* 检查对象是否存在于类表中。
|
||||
*
|
||||
* @param string $alias
|
||||
* @return mixed
|
||||
*/
|
||||
public function has(string $alias): bool;
|
||||
|
||||
/**
|
||||
* 通过别名注册延迟加载对象。(此方法是延迟加载的)
|
||||
*
|
||||
* @param string|array $alias
|
||||
* @param string|object|null $object
|
||||
* @param string|null $tag
|
||||
* @return mixed
|
||||
*/
|
||||
public function bind($alias, $object = null, string $tag = null);
|
||||
|
||||
/**
|
||||
* 撤销共享实例。
|
||||
*
|
||||
* @param string|int|object $alias
|
||||
* @return mixed
|
||||
*/
|
||||
public function revokeShared($alias);
|
||||
|
||||
/**
|
||||
* 释放内存。
|
||||
*
|
||||
* @param array|string $alias
|
||||
* @return mixed
|
||||
*/
|
||||
public function dealloc($alias);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace library\interfaces;
|
||||
|
||||
use AccountWeight\Exceptions\WechatAccountWeightAssessmentException as WeightAssessmentException;
|
||||
|
||||
/**
|
||||
* 微信账号加友权重评估
|
||||
*/
|
||||
interface WechatAccountWeightAssessment
|
||||
{
|
||||
/**
|
||||
* 设置测算因子
|
||||
*
|
||||
* @param $params
|
||||
* @return WechatAccountWeightAssessment
|
||||
*/
|
||||
public function settingFactor($params): WechatAccountWeightAssessment;
|
||||
|
||||
/**
|
||||
* 计算账号年龄权重
|
||||
*
|
||||
* @return WechatAccountWeightResultSet
|
||||
*/
|
||||
public function calculAgeWeight(): WechatAccountWeightResultSet;
|
||||
|
||||
/**
|
||||
* 计算活跃度权重
|
||||
*
|
||||
* @return WechatAccountWeightResultSet
|
||||
*/
|
||||
public function calculActivityWeigth(): WechatAccountWeightResultSet;
|
||||
|
||||
/**
|
||||
* 计算限制影响权重
|
||||
*
|
||||
* @return WechatAccountWeightResultSet
|
||||
*/
|
||||
public function calculRestrictWeigth(): WechatAccountWeightResultSet;
|
||||
|
||||
/**
|
||||
* 计算实名认证权重
|
||||
*
|
||||
* @return WechatAccountWeightResultSet
|
||||
*/
|
||||
public function calculRealNameWeigth(): WechatAccountWeightResultSet;
|
||||
|
||||
/**
|
||||
* 获取总得分
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWeightScope(): int;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace library\interfaces;
|
||||
|
||||
/**
|
||||
* 微信账号加友权重评估结果数据
|
||||
*/
|
||||
interface WechatAccountWeightResultSet
|
||||
{
|
||||
/**
|
||||
* 设置测算因子
|
||||
*
|
||||
* @param mixed $params
|
||||
* @return WechatAccountWeightResultSet
|
||||
*/
|
||||
public function settingFactor($params): WechatAccountWeightResultSet;
|
||||
|
||||
/**
|
||||
* 返回微信账号加友权重评估结果数据
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getResult(): int;
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace library\s2;
|
||||
|
||||
use think\Exception;
|
||||
use think\facade\Env;
|
||||
|
||||
class CurlHandle
|
||||
{
|
||||
private $baseUrl = '';
|
||||
private $authorization = '';
|
||||
private $header = [];
|
||||
private $method = 'get';
|
||||
private static $instant = null;
|
||||
|
||||
/**
|
||||
* 获取域名
|
||||
* @return void
|
||||
*/
|
||||
private function getBaseUrl()
|
||||
{
|
||||
$this->baseUrl = Env::get('api.wechat_url');
|
||||
}
|
||||
|
||||
/**
|
||||
* CurlHandle constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->getBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置头部
|
||||
*
|
||||
* @param array $headerData 头部数组
|
||||
* @param string $authorization
|
||||
* @param string $type 类型 默认json (json,plain)
|
||||
* @return array
|
||||
*/
|
||||
public function setHeader($key, $value): CurlHandle
|
||||
{
|
||||
if (is_array($key)) {
|
||||
$this->header = array_merge($this->header, $key);
|
||||
} else {
|
||||
$this->header[$key] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getHearder(): array
|
||||
{
|
||||
$header = [];
|
||||
|
||||
foreach ($this->header as $key => $value) {
|
||||
$header[] = $key . ':' . $value;
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function setMethod(string $method): CurlHandle
|
||||
{
|
||||
$this->method = $method;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @return $this
|
||||
*/
|
||||
public function setBaseUrl(string $baseUrl): CurlHandle
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url 请求的链接
|
||||
* @param array $params 请求附带的参数
|
||||
* @param string $method 请求的方式, 支持GET, POST, PUT, DELETE等
|
||||
* @param array $header 头部
|
||||
* @param string $type 数据类型,支持dataBuild、json等
|
||||
* @return bool|string
|
||||
*/
|
||||
public function send($url, $params = [], $type = 'dataBuild')
|
||||
{
|
||||
$str = '';
|
||||
if (!empty($url)) {
|
||||
try {
|
||||
$ch = curl_init();
|
||||
$method = $this->method;
|
||||
$url = $this->baseUrl . $url;
|
||||
|
||||
// 处理GET请求的参数
|
||||
if (strtoupper($method) == 'GET' && !empty($params)) {
|
||||
$url = $url . '?' . dataBuild($params);
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 0);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30); //30秒超时
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getHearder());
|
||||
|
||||
// 处理不同的请求方法
|
||||
if (strtoupper($method) != 'GET') {
|
||||
// 设置请求方法
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
|
||||
|
||||
// 处理参数格式
|
||||
if ($type == 'dataBuild') {
|
||||
$params = dataBuild($params);
|
||||
} elseif ($type == 'json') {
|
||||
$params = json_encode($params);
|
||||
} else {
|
||||
$params = dataBuild($params);
|
||||
}
|
||||
|
||||
// 设置请求体
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //是否验证对等证书,1则验证,0则不验证
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
$str = curl_exec($ch);
|
||||
if(curl_errno($ch)) {
|
||||
echo 'Curl error: ' .curl_errno($ch) . ':' . curl_error($ch);
|
||||
}
|
||||
curl_close($ch);
|
||||
} catch (Exception $e) {
|
||||
$str = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单例
|
||||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function getInstant()
|
||||
{
|
||||
if (self::$instant instanceof self) {
|
||||
return self::$instant;
|
||||
}
|
||||
|
||||
return new static();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace library\s2\interfaces;
|
||||
|
||||
interface AccountInterface
|
||||
{
|
||||
/**
|
||||
* 创建账号
|
||||
*
|
||||
* @param string $accout
|
||||
* @param string $password
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(string $accout, string $password);
|
||||
|
||||
/**
|
||||
* 删除账号
|
||||
*
|
||||
* @param int $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(int $id);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace library\s2\interfaces;
|
||||
|
||||
interface DeviceInterface
|
||||
{
|
||||
/**
|
||||
* 获取设备列表
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public function getlist(array $params): array;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace library\s2\interfaces;
|
||||
|
||||
interface LoginInterface
|
||||
{
|
||||
/**
|
||||
* 创建账号
|
||||
*
|
||||
* @param string $accout
|
||||
* @param string $password
|
||||
* @return mixed
|
||||
*/
|
||||
public function login(string $accout, string $password): LoginInterface;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
namespace library\s2\logics;
|
||||
|
||||
use library\s2\interfaces\AccountInterface;
|
||||
use library\s2\interfaces\LoginInterface;
|
||||
|
||||
class AccountLogic implements AccountInterface, LoginInterface
|
||||
{
|
||||
public function create(string $accout, string $password)
|
||||
{
|
||||
// TODO: Implement create() method.
|
||||
}
|
||||
|
||||
public function login(string $accout, string $password): LoginInterface
|
||||
{
|
||||
// TODO: Implement login() method.
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete(int $id)
|
||||
{
|
||||
// TODO: Implement delete() method.
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace library\s2\logics;
|
||||
|
||||
use app\api\model\DeviceGroupModel;
|
||||
use app\api\model\DeviceModel;
|
||||
use app\common\service\AuthService;
|
||||
use library\s2\CurlHandle;
|
||||
use library\s2\interfaces\DeviceInterface;
|
||||
use think\facade\Log;
|
||||
|
||||
class DeviceLogic implements DeviceInterface
|
||||
{
|
||||
/**
|
||||
* 获取设备列表
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getlist(array $params = []): array
|
||||
{
|
||||
|
||||
try {
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'accountId' => $params['accountId'] ?? '',
|
||||
'keyword' => $params['keyword'] ?? '',
|
||||
'imei' => $params['imei'] ?? '',
|
||||
'groupId' => $params['groupId'] ?? '',
|
||||
'brand' => $params['brand'] ?? '',
|
||||
'model' => $params['model'] ?? '',
|
||||
'deleteType' => $params['deleteType'] ?? 'unDeleted',
|
||||
'operatingSystem' => $params['operatingSystem'] ?? '',
|
||||
'softwareVersion' => $params['softwareVersion'] ?? '',
|
||||
'phoneAppVersion' => $params['phoneAppVersion'] ?? '',
|
||||
'recorderVersion' => $params['recorderVersion'] ?? '',
|
||||
'contactsVersion' => $params['contactsVersion'] ?? '',
|
||||
'rooted' => $params['rooted'] ?? '',
|
||||
'xPosed' => $params['xPosed'] ?? '',
|
||||
'alive' => $params['alive'] ?? '',
|
||||
'hasWechat' => $params['hasWechat'] ?? '',
|
||||
'departmentId' => $params['departmentId'] ?? '',
|
||||
'pageIndex' => $params['pageIndex'] ?? 0,
|
||||
'pageSize' => $params['pageSize'] ?? 20
|
||||
];
|
||||
|
||||
$JWT = AuthService::getSystemAuthorization();
|
||||
$result = CurlHandle::getInstant()
|
||||
->setHeader('Content-Type', 'text/plain')
|
||||
->setHeader('authorization', 'bearer ' . $JWT)
|
||||
->setMethod('get')
|
||||
->send('api/Account/myTenantPageAccounts', $params);
|
||||
|
||||
$response = handleApiResponse($result);
|
||||
// 保存数据到数据库
|
||||
if (!empty($response['results'])) {
|
||||
foreach ($response['results'] as $item) {
|
||||
$this->saveData($item);
|
||||
}
|
||||
}
|
||||
return json_encode(['code' => 200, 'msg' => '获取公司账号列表成功', 'data' => $response]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json_encode(['code' => 500, 'msg' => '获取公司账号列表失败:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function saveData($item)
|
||||
{
|
||||
$data = [
|
||||
'id' => isset($item['id']) ? $item['id'] : '',
|
||||
'userName' => isset($item['userName']) ? $item['userName'] : '',
|
||||
'nickname' => isset($item['nickname']) ? $item['nickname'] : '',
|
||||
'realName' => isset($item['realName']) ? $item['realName'] : '',
|
||||
'groupName' => isset($item['groupName']) ? $item['groupName'] : '',
|
||||
'wechatAccounts' => isset($item['wechatAccounts']) ? json_encode($item['wechatAccounts']) : json_encode([]),
|
||||
'alive' => isset($item['alive']) ? $item['alive'] : false,
|
||||
'lastAliveTime' => isset($item['lastAliveTime']) ? $item['lastAliveTime'] : null,
|
||||
'tenantId' => isset($item['tenantId']) ? $item['tenantId'] : 0,
|
||||
'groupId' => isset($item['groupId']) ? $item['groupId'] : 0,
|
||||
'currentAccountId' => isset($item['currentAccountId']) ? $item['currentAccountId'] : 0,
|
||||
'imei' => $item['imei'],
|
||||
'memo' => isset($item['memo']) ? $item['memo'] : '',
|
||||
'createTime' => isset($item['createTime']) ? strtotime($item['createTime']) : 0,
|
||||
'isDeleted' => isset($item['isDeleted']) ? $item['isDeleted'] : false,
|
||||
'deletedAndStop' => isset($item['deletedAndStop']) ? $item['deletedAndStop'] : false,
|
||||
'deleteTime' => empty($item['isDeleted']) ? 0 : strtotime($item['deleteTime']),
|
||||
'rooted' => isset($item['rooted']) ? $item['rooted'] : false,
|
||||
'xPosed' => isset($item['xPosed']) ? $item['xPosed'] : false,
|
||||
'brand' => isset($item['brand']) ? $item['brand'] : '',
|
||||
'model' => isset($item['model']) ? $item['model'] : '',
|
||||
'operatingSystem' => isset($item['operatingSystem']) ? $item['operatingSystem'] : '',
|
||||
'softwareVersion' => isset($item['softwareVersion']) ? $item['softwareVersion'] : '',
|
||||
'extra' => isset($item['extra']) ? json_encode($item['extra']) : json_encode([]),
|
||||
'phone' => isset($item['phone']) ? $item['phone'] : '',
|
||||
'lastUpdateTime' => isset($item['lastUpdateTime']) ? ($item['lastUpdateTime'] == '0001-01-01T00:00:00' ? 0 : strtotime($item['lastUpdateTime'])) : 0
|
||||
];
|
||||
|
||||
// 使用imei作为唯一性判断
|
||||
$device = DeviceModel::where('id', $item['id'])->find();
|
||||
|
||||
if ($device) {
|
||||
$device->save($data);
|
||||
} else {
|
||||
|
||||
// autoLike:自动点赞
|
||||
// momentsSync:朋友圈同步
|
||||
// autoCustomerDev:自动开发客户
|
||||
// groupMessageDeliver:群消息推送
|
||||
// autoGroup:自动建群
|
||||
|
||||
$data['taskConfig'] = json_encode([
|
||||
'autoLike' => true,
|
||||
'momentsSync' => true,
|
||||
'autoCustomerDev' => true,
|
||||
'groupMessageDeliver' => true,
|
||||
'autoGroup' => true,
|
||||
]);
|
||||
DeviceModel::create($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存设备数据
|
||||
* @param array $item 设备数据
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDevice($item)
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'id' => isset($item['id']) ? $item['id'] : '',
|
||||
'userName' => isset($item['userName']) ? $item['userName'] : '',
|
||||
'nickname' => isset($item['nickname']) ? $item['nickname'] : '',
|
||||
'realName' => isset($item['realName']) ? $item['realName'] : '',
|
||||
'groupName' => isset($item['groupName']) ? $item['groupName'] : '',
|
||||
'wechatAccounts' => isset($item['wechatAccounts']) ? json_encode($item['wechatAccounts']) : json_encode([]),
|
||||
'alive' => isset($item['alive']) ? $item['alive'] : false,
|
||||
'lastAliveTime' => isset($item['lastAliveTime']) ? $item['lastAliveTime'] : null,
|
||||
'tenantId' => isset($item['tenantId']) ? $item['tenantId'] : 0,
|
||||
'groupId' => isset($item['groupId']) ? $item['groupId'] : 0,
|
||||
'currentAccountId' => isset($item['currentAccountId']) ? $item['currentAccountId'] : 0,
|
||||
'imei' => $item['imei'],
|
||||
'memo' => isset($item['memo']) ? $item['memo'] : '',
|
||||
'createTime' => isset($item['createTime']) ? strtotime($item['createTime']) : 0,
|
||||
'isDeleted' => isset($item['isDeleted']) ? $item['isDeleted'] : false,
|
||||
'deletedAndStop' => isset($item['deletedAndStop']) ? $item['deletedAndStop'] : false,
|
||||
'deleteTime' => empty($item['isDeleted']) ? 0 : strtotime($item['deleteTime']),
|
||||
'rooted' => isset($item['rooted']) ? $item['rooted'] : false,
|
||||
'xPosed' => isset($item['xPosed']) ? $item['xPosed'] : false,
|
||||
'brand' => isset($item['brand']) ? $item['brand'] : '',
|
||||
'model' => isset($item['model']) ? $item['model'] : '',
|
||||
'operatingSystem' => isset($item['operatingSystem']) ? $item['operatingSystem'] : '',
|
||||
'softwareVersion' => isset($item['softwareVersion']) ? $item['softwareVersion'] : '',
|
||||
'extra' => isset($item['extra']) ? json_encode($item['extra']) : json_encode([]),
|
||||
'phone' => isset($item['phone']) ? $item['phone'] : '',
|
||||
'lastUpdateTime' => isset($item['lastUpdateTime']) ? ($item['lastUpdateTime'] == '0001-01-01T00:00:00' ? 0 : strtotime($item['lastUpdateTime'])) : 0
|
||||
];
|
||||
|
||||
// 使用ID作为唯一性判断
|
||||
$device = DeviceModel::where('id', $item['id'])->find();
|
||||
|
||||
if ($device) {
|
||||
$device->save($data);
|
||||
} else {
|
||||
$data['taskConfig'] = json_encode([
|
||||
'autoLike' => true,
|
||||
'momentsSync' => true,
|
||||
'autoCustomerDev' => true,
|
||||
'groupMessageDeliver' => true,
|
||||
'autoGroup' => true,
|
||||
]);
|
||||
DeviceModel::create($data);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('保存设备数据失败:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存设备分组数据
|
||||
* @param array $item 设备分组数据
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeviceGroup($item)
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'id' => $item['id'],
|
||||
'tenantId' => $item['tenantId'],
|
||||
'groupName' => $item['groupName'],
|
||||
'groupMemo' => $item['groupMemo'],
|
||||
'count' => isset($item['count']) ? $item['count'] : 0,
|
||||
'createTime' => $item['createTime'] == '0001-01-01T00:00:00' ? 0 : strtotime($item['createTime'])
|
||||
];
|
||||
|
||||
// 使用ID作为唯一性判断
|
||||
$group = DeviceGroupModel::where('id', $item['id'])->find();
|
||||
|
||||
if ($group) {
|
||||
$group->save($data);
|
||||
} else {
|
||||
DeviceGroupModel::create($data);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('保存设备分组数据失败:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user