481 lines
13 KiB
PHP
481 lines
13 KiB
PHP
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use App\Helpers\Str as StrHelper;
|
||
use Dcat\Admin\Traits\HasDateTimeFormatter;
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Illuminate\Support\Str;
|
||
|
||
class UserInfo extends Model
|
||
{
|
||
use HasDateTimeFormatter;
|
||
|
||
public const GENDER_UNKNOWN = 'unknown'; // 未知
|
||
public const GENDER_MALE = 'male'; // 男性
|
||
public const GENDER_FEMALE = 'female'; // 女性
|
||
|
||
public const AGENT_LEVEL_CIVILIAN = 0;
|
||
public const AGENT_LEVEL_VIP = 1;
|
||
public const AGENT_LEVEL_COMMUNITY = 2;
|
||
public const AGENT_LEVEL_DISTRICT = 3;
|
||
public const AGENT_LEVEL_CITY = 4;
|
||
public const AGENT_LEVEL_PROVINCE = 5;
|
||
public const AGENT_LEVEL_BRANCH = 6;
|
||
public const AGENT_LEVEL_DIRECTOR = 7;
|
||
|
||
/**
|
||
* @var string
|
||
*/
|
||
protected $primaryKey = 'user_id';
|
||
|
||
/**
|
||
* @var bool
|
||
*/
|
||
public $incrementing = false;
|
||
|
||
/**
|
||
* @var array
|
||
*/
|
||
protected $attributes = [
|
||
'gender' => self::GENDER_UNKNOWN,
|
||
'bonusable' => true,
|
||
];
|
||
|
||
/**
|
||
* @var array
|
||
*/
|
||
protected $fillable = [
|
||
'inviter_id',
|
||
'nickname',
|
||
'avatar',
|
||
'gender',
|
||
'birthday',
|
||
'bonusable',
|
||
'depth',
|
||
'path',
|
||
'agent_level',
|
||
'quota_v1',
|
||
'quota_v2',
|
||
'growth_value',
|
||
'pre_growth_value',
|
||
'group_sales_value',
|
||
'real_inviter_id',
|
||
];
|
||
|
||
/**
|
||
* @var array
|
||
*/
|
||
protected $casts = [
|
||
'birthday' => 'date',
|
||
'bonusable' => 'bool',
|
||
];
|
||
|
||
/**
|
||
* 代理等级排名,从低到高
|
||
*
|
||
* @var array
|
||
*/
|
||
public static $agentLevelRanks = [
|
||
self::AGENT_LEVEL_CIVILIAN => 1,
|
||
self::AGENT_LEVEL_VIP => 2,
|
||
self::AGENT_LEVEL_COMMUNITY => 3,
|
||
self::AGENT_LEVEL_DISTRICT => 4,
|
||
self::AGENT_LEVEL_CITY => 5,
|
||
self::AGENT_LEVEL_PROVINCE => 6,
|
||
self::AGENT_LEVEL_BRANCH => 7,
|
||
self::AGENT_LEVEL_DIRECTOR => 8,
|
||
];
|
||
|
||
/**
|
||
* 代理等级配置 key
|
||
*
|
||
* @var array
|
||
*/
|
||
public static $agentLevelKeys = [
|
||
self::AGENT_LEVEL_CIVILIAN => 'civilian',
|
||
self::AGENT_LEVEL_VIP => 'vip',
|
||
self::AGENT_LEVEL_COMMUNITY => 'community',
|
||
self::AGENT_LEVEL_DISTRICT => 'district',
|
||
self::AGENT_LEVEL_CITY => 'city',
|
||
self::AGENT_LEVEL_PROVINCE => 'province',
|
||
self::AGENT_LEVEL_BRANCH => 'branch',
|
||
self::AGENT_LEVEL_DIRECTOR => 'director',
|
||
];
|
||
|
||
public static $genderTexts = [
|
||
self::GENDER_UNKNOWN => '未知',
|
||
self::GENDER_MALE => '男性',
|
||
self::GENDER_FEMALE => '女性',
|
||
];
|
||
|
||
/**
|
||
* 代理等级文本
|
||
*
|
||
* @var array
|
||
*/
|
||
public static $agentLevelTexts = [
|
||
self::AGENT_LEVEL_CIVILIAN => '粉丝',
|
||
self::AGENT_LEVEL_VIP => '会员',
|
||
self::AGENT_LEVEL_COMMUNITY => '铁牌会员',
|
||
self::AGENT_LEVEL_DISTRICT => '铜牌会员',
|
||
self::AGENT_LEVEL_CITY => '银牌会员',
|
||
self::AGENT_LEVEL_PROVINCE => '金牌会员',
|
||
self::AGENT_LEVEL_BRANCH => '钻石会员',
|
||
self::AGENT_LEVEL_DIRECTOR => '默认用户',
|
||
];
|
||
|
||
/**
|
||
* {@inheritdoc}
|
||
*/
|
||
protected static function booted()
|
||
{
|
||
parent::saving(function ($userInfo) {
|
||
// 如果没有邀请码,则自动分配邀请码
|
||
if ($userInfo->code === null) {
|
||
do {
|
||
$userInfo->code = strtolower(StrHelper::randomAlpha(6));
|
||
} while (static::where('code', $userInfo->code)->exists());
|
||
} elseif ($userInfo->isDirty('code')) {
|
||
$userInfo->code = strtolower($userInfo->code);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 用户
|
||
*
|
||
* @return void
|
||
*/
|
||
public function user()
|
||
{
|
||
return $this->belongsTo(User::class);
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的邀请人信息
|
||
*/
|
||
public function inviterInfo()
|
||
{
|
||
return $this->belongsTo(UserInfo::class, 'inviter_id');
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的真实邀请人信息
|
||
*/
|
||
public function realInviterInfo()
|
||
{
|
||
return $this->belongsTo(UserInfo::class, 'real_inviter_id');
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的预收益
|
||
*/
|
||
public function distributionPreIncomes()
|
||
{
|
||
return $this->hasMany(DistributionPreIncome::class, 'user_id', 'user_id');
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的代理升级日志
|
||
*/
|
||
public function agentUpgradeLogs()
|
||
{
|
||
return $this->hasMany(AgentUpgradeLog::class, 'user_id', 'user_id');
|
||
}
|
||
|
||
/**
|
||
* 属于此用户的经销商信息
|
||
*/
|
||
public function dealer()
|
||
{
|
||
return $this->hasOne(Dealer::class, 'user_id', 'user_id');
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的直推店铺总数
|
||
*/
|
||
public function getVipAgentsCount()
|
||
{
|
||
return static::where('inviter_id', $this->getKey())
|
||
->where('agent_level', '>=', static::AGENT_LEVEL_VIP)
|
||
->count();
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的不同线上的下级区级代理总数
|
||
*/
|
||
public function getDistrictAgentsCountOnDifferentLines()
|
||
{
|
||
$path = $this->full_path;
|
||
|
||
// 获取所有区级以上的代理
|
||
$agents = static::where('agent_level', '>=', static::AGENT_LEVEL_DISTRICT)
|
||
->where('path', 'like', "{$path}%")
|
||
->get();
|
||
|
||
$lines = [];
|
||
|
||
foreach ($agents as $agent) {
|
||
preg_match("#\A{$path}\d+-#", $agent->full_path, $matches);
|
||
|
||
$line = $matches[0];
|
||
|
||
if (! isset($lines[$line])) {
|
||
$lines[$line] = 1;
|
||
}
|
||
}
|
||
|
||
return count($lines);
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的不同线上的下级市级代理总数
|
||
*/
|
||
public function getCityAgentsCountOnDifferentLines()
|
||
{
|
||
$path = $this->full_path;
|
||
|
||
// 获取所有市级以上的代理
|
||
$agents = static::where('agent_level', '>=', static::AGENT_LEVEL_CITY)
|
||
->where('path', 'like', "$path%")
|
||
->get();
|
||
|
||
$lines = [];
|
||
|
||
foreach ($agents as $agent) {
|
||
preg_match("#\A{$path}\d+-#", $agent->full_path, $matches);
|
||
|
||
$line = $matches[0];
|
||
|
||
if (! isset($lines[$line])) {
|
||
$lines[$line] = 1;
|
||
}
|
||
}
|
||
|
||
return count($lines);
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的不同线上的下级省级代理总数
|
||
*/
|
||
public function getProvinceAgentsCountOnDifferentLines()
|
||
{
|
||
$path = $this->full_path;
|
||
|
||
// 获取所有省级以上的代理
|
||
$agents = static::where('agent_level', '>=', static::AGENT_LEVEL_PROVINCE)
|
||
->where('path', 'like', "$path%")
|
||
->get();
|
||
|
||
$lines = [];
|
||
|
||
foreach ($agents as $agent) {
|
||
preg_match("#\A{$path}\d+-#", $agent->full_path, $matches);
|
||
|
||
$line = $matches[0];
|
||
|
||
if (! isset($lines[$line])) {
|
||
$lines[$line] = 1;
|
||
}
|
||
}
|
||
|
||
return count($lines);
|
||
}
|
||
|
||
/**
|
||
* 尝试提升代理等级
|
||
*
|
||
* @return void
|
||
*/
|
||
public function attemptUpgradeAgentLevel(): void
|
||
{
|
||
$lvl = $this->agent_level;
|
||
|
||
// 如果代理等级是分公司或董事时,不能继续提升代理等级
|
||
if (in_array($lvl, [static::AGENT_LEVEL_BRANCH, static::AGENT_LEVEL_DIRECTOR])) {
|
||
return;
|
||
}
|
||
|
||
$rules = config('agent.upgrade_rules');
|
||
|
||
// 如果成长值不足650时,不能升级
|
||
if (bccomp($this->growth_value, $rules['vip']['sales_value']) < 0) {
|
||
return;
|
||
}
|
||
|
||
// 如果代理等级是粉丝,则可升级为店铺
|
||
if ($lvl === static::AGENT_LEVEL_CIVILIAN) {
|
||
$lvl = static::AGENT_LEVEL_VIP;
|
||
}
|
||
|
||
// 团队销售值
|
||
$salesValue = $this->team_sales_value;
|
||
|
||
if ($lvl === static::AGENT_LEVEL_VIP) {
|
||
$vipsCount = $this->getVipAgentsCount();
|
||
|
||
// 如果直推店铺人数>=6,并且团队销售值>=65000,则可升级为区级代理
|
||
// 或者直推店铺人数>=4,则可升级为社区
|
||
if (
|
||
$vipsCount >= $rules['district']['vips_agents_count']
|
||
&& bccomp($salesValue, $rules['district']['team_sales_value']) >= 0
|
||
) {
|
||
$lvl = static::AGENT_LEVEL_DISTRICT;
|
||
} elseif ($vipsCount >= $rules['community']['vips_agents_count']) {
|
||
$lvl = static::AGENT_LEVEL_COMMUNITY;
|
||
}
|
||
} elseif ($lvl === static::AGENT_LEVEL_COMMUNITY && bccomp($salesValue, $rules['district']['team_sales_value']) >= 0) {
|
||
if ($this->getVipAgentsCount() >= $rules['district']['vips_agents_count']) {
|
||
$lvl = static::AGENT_LEVEL_DISTRICT;
|
||
}
|
||
}
|
||
|
||
if ($lvl === static::AGENT_LEVEL_DISTRICT && bccomp($salesValue, $rules['city']['team_sales_value']) >= 0) {
|
||
if ($this->getDistrictAgentsCountOnDifferentLines() >= $rules['city']['district_agents_count']) {
|
||
$lvl = static::AGENT_LEVEL_CITY;
|
||
}
|
||
}
|
||
|
||
if ($lvl === static::AGENT_LEVEL_CITY && bccomp($salesValue, $rules['province']['team_sales_value']) >= 0) {
|
||
if ($this->getCityAgentsCountOnDifferentLines() >= $rules['province']['city_agents_count']) {
|
||
$lvl = static::AGENT_LEVEL_PROVINCE;
|
||
}
|
||
}
|
||
|
||
if ($lvl === static::AGENT_LEVEL_PROVINCE && bccomp($salesValue, $rules['branch']['team_sales_value']) >= 0) {
|
||
if ($this->getProvinceAgentsCountOnDifferentLines() >= $rules['branch']['province_agents_count']) {
|
||
$lvl = static::AGENT_LEVEL_BRANCH;
|
||
}
|
||
}
|
||
|
||
$this->changeAgentLvl($lvl, '达到升级条件');
|
||
}
|
||
|
||
/**
|
||
* 变更代理等级
|
||
*
|
||
* @param int $lvl
|
||
* @param string|null $remark
|
||
* @return void
|
||
*/
|
||
public function changeAgentLvl(int $lvl, ?string $remark = null)
|
||
{
|
||
if ($this->agent_level === $lvl) {
|
||
return $lvl;
|
||
}
|
||
|
||
$beforeAgentLevel = $this->agent_level;
|
||
|
||
$this->update([
|
||
'agent_level' => $lvl,
|
||
]);
|
||
|
||
$this->agentUpgradeLogs()->create([
|
||
'before_agent_level' => $beforeAgentLevel,
|
||
'change_agent_level' => $lvl,
|
||
'remark' => $remark,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 变更预收益成长值
|
||
*
|
||
* @param float $growthValue
|
||
* @param bool $downgrade
|
||
* @return void
|
||
*/
|
||
public function incrPreGrowthValue($growthValue, $downgrade = true)
|
||
{
|
||
if (bccomp($growthValue, '0', 2) === 0) {
|
||
return;
|
||
}
|
||
|
||
// 增加预收益的成长值
|
||
$this->increment('pre_growth_value', $growthValue);
|
||
|
||
$totalGrowthValue = bcadd($this->growth_value, $this->pre_growth_value, 2);
|
||
$compared = bccomp($totalGrowthValue, config('agent.upgrade_rules.vip.sales_value'), 2);
|
||
|
||
if ($this->agent_level === static::AGENT_LEVEL_VIP && $compared < 0) {
|
||
if ($downgrade) {
|
||
$this->update([
|
||
'agent_level' => static::AGENT_LEVEL_CIVILIAN,
|
||
]);
|
||
}
|
||
} elseif ($this->agent_level === static::AGENT_LEVEL_CIVILIAN && $compared >= 0) {
|
||
$this->update([
|
||
'agent_level' => static::AGENT_LEVEL_VIP,
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取此用户的所有父级ID
|
||
*
|
||
* @return array
|
||
*/
|
||
public function getParentIdsAttribute(): array
|
||
{
|
||
if ($path = trim($this->path, '-')) {
|
||
return explode('-', $path);
|
||
}
|
||
|
||
return [];
|
||
}
|
||
|
||
public function getRealParentIdsAttribute(): array
|
||
{
|
||
$rpids = (array) $this->realInviterInfo?->parent_ids;
|
||
if ($this->real_inviter_id) {
|
||
array_push($rpids, $this->real_inviter_id);
|
||
}
|
||
return $rpids;
|
||
}
|
||
|
||
/**
|
||
* 代理等级排名
|
||
*
|
||
* @return int
|
||
*/
|
||
public function getAgentLevelRankAttribute(): int
|
||
{
|
||
return static::$agentLevelRanks[$this->agent_level] ?? 0;
|
||
}
|
||
|
||
/**
|
||
* 代理等级配置 key
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getAgentLevelKeyAttribute(): string
|
||
{
|
||
return static::$agentLevelKeys[$this->agent_level] ?? 'unknown';
|
||
}
|
||
|
||
public function getAgentLevelNameAttribute(): string
|
||
{
|
||
return static::$agentLevelTexts[$this->agent_level] ?? '未知';
|
||
}
|
||
|
||
/**
|
||
* 获取完整的邀请路径
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getFullPathAttribute(): string
|
||
{
|
||
return Str::finish($this->path.$this->getKey(), '-');
|
||
}
|
||
|
||
/**
|
||
* 获团队销售值
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getTeamSalesValueAttribute(): string
|
||
{
|
||
return bcadd($this->growth_value, $this->group_sales_value, 2);
|
||
}
|
||
}
|