This commit is contained in:
2023-03-08 09:16:04 +08:00
commit e78454540f
1318 changed files with 210569 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\DB;
use Modules\User\Traits\BelongsToUser;
class Account extends Model
{
use BelongsToUser;
protected $table = 'user_accounts';
protected $primaryKey = 'user_id';
const TYPES = [
'balance' => '现金余额',
'score' => '水滴',
'coins' => '未使用',
'other' => '其他',
];
/**
* Notes : 账户日志
*
* @Date : 2021/4/27 11:22 上午
* @Author : < Jason.C >
* @return HasMany
*/
public function logs(): HasMany
{
return $this->hasMany(AccountLog::class, 'account_id', 'user_id');
}
/**
* Notes: 执行账户规则
*
* @Author: <C.Jason>
* @Date : 2019/11/28 1:24 下午
* @param $rule string|int
* @param float $variable
* @param bool $frozen
* @param array $source
* @return bool
* @throws Exception
*/
public function rule($rule, float $variable = 0, bool $frozen = true, array $source = []): bool
{
if (is_numeric($rule)) {
$rule = AccountRule::findOrFail($rule);
} else {
$rule = AccountRule::where('name', $rule)->firstOrFail();
}
if ($rule->trigger == 0) {
// 不限制执行的
return $this->accountExecute($rule, $variable, $frozen, $source);
} elseif ($rule->trigger > $this->logs()
->where('rule_id', $rule->id)
->whereDate('created_at', Carbon::today())
->count()) {
// 每日执行 trigger 次
return $this->accountExecute($rule, $variable, $frozen, $source);
} elseif ($rule->trigger < 0 && ! $this->logs()->where('rule_id', $rule->id)->first()) {
// 终身只能执行一次
return $this->accountExecute($rule, $variable, $frozen, $source);
}
throw new Exception('达到最大可执行次数');
}
/**
* Notes: 增加账户余额
*
* @Author: <C.Jason>
* @Date : 2019/11/28 1:25 下午
* @param $type
* @param $variable
* @return bool
*/
public function increase($type, $variable): bool
{
DB::transaction(function () use ($type, $variable) {
$this->increment($type, $variable);
$log = [
'rule_id' => 0,
'type' => $type,
'variable' => $variable,
'frozen' => 0,
'balance' => $this->{$type},
'source' => ['type' => 'increase'],
];
$this->logs()->create($log);
});
return true;
}
/**
* Notes: 扣除账户金额
*
* @Author: <C.Jason>
* @Date : 2019/11/28 1:25 下午
* @param $type
* @param $variable
* @return bool
* @throws Exception
*/
public function decrease($type, $variable): bool
{
DB::transaction(function () use ($type, $variable) {
$this->decrement($type, $variable);
$log = [
'rule_id' => 0,
'type' => $type,
'variable' => -$variable,
'frozen' => 0,
'balance' => $this->{$type},
'source' => ['type' => 'deduct'],
];
$this->logs()->create($log);
});
return true;
}
/**
* Notes: 执行账户规则
*
* @Author: <C.Jason>
* @Date : 2019/11/28 1:41 下午
* @param AccountRule $rule
* @param $variable
* @param $frozen
* @param $source
* @return bool
* @throws Exception
*/
protected function accountExecute(AccountRule $rule, $variable, $frozen, $source): bool
{
if ($variable != 0) {
$rule->variable = $variable;
}
DB::transaction(function () use ($rule, $frozen, $source) {
// 如果是扣款,立即执行,如果非冻结,也立即执行
if ($rule->variable < 0 || $rule->deductions == 1 || $frozen === false) {
$this->increment($rule->type, $rule->variable);
$frozen = false;
}
$log = [
'rule_id' => $rule->id,
'type' => $rule->type,
'amount' => $rule->variable,
'frozen' => $frozen,
'balance' => $this->{$rule->type},
'source' => $source ?: [],
'remark' => $source['remark'] ?? $rule->remark,
'settle_at' => $source['settle_at'] ?? null,
'frozen_at' => $source['frozen_at'] ?? null,
];
// 写入记录
$this->logs()->create($log);
});
return true;
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\OrderByIdDesc;
use Exception;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AccountLog extends Model
{
use OrderByIdDesc;
protected $table = 'user_account_logs';
protected $dates = [
'settle_at',
];
protected $casts = [
'source' => 'json',
];
/**
* Notes : 账户
*
* @Date : 2021/4/27 11:22 上午
* @Author : < Jason.C >
* @return BelongsTo
*/
public function account(): BelongsTo
{
return $this->belongsTo(Account::class, 'account_id');
}
public function rule(): BelongsTo
{
return $this->belongsTo(AccountRule::class, 'rule_id');
}
/**
* Notes: 冻结一条账户记录
*
* @Author: <C.Jason>
* @Date : 2019/12/1 10:48 上午
* @return bool
* @throws Exception
*/
public function freeze(): bool
{
if ($this->frozen == 0) {
$this->account->decrement($this->type, $this->amount);
$this->frozen = 1;
$this->balance = $this->account->{$this->type};
$this->save();
return true;
} else {
throw new Exception('账目已冻结');
}
}
/**
* Notes: 解冻一条记录
*
* @Author: <C.Jason>
* @Date : 2019/12/1 10:48 上午
* @return bool
* @throws Exception
*/
public function thaw(): bool
{
if ($this->frozen == 1) {
$this->account->increment($this->type, $this->amount);
$this->frozen = 0;
$this->balance = $this->account->{$this->type};
$this->save();
return true;
} else {
throw new Exception('已经领取');
}
}
public function getAmountFormatAttribute(): string
{
return ($this->amount > 0 ? '+' : '').$this->amount;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class AccountRule extends Model
{
use Cachable,
SoftDeletes;
protected $table = 'user_account_rules';
public function logs(): HasMany
{
return $this->hasMany(AccountLog::class, 'rule_id');
}
/**
* Notes : 获取可执行次数的文本显示
* @Date : 2021/5/21 2:37 下午
* @Author : < Jason.C >
* @return string
*/
protected function getTriggerTextAttribute(): string
{
switch ($this->trigger <=> 0) {
case -1:
return '仅一次';
case 0:
return '不限制';
case 1:
return $this->trigger . ' 次/日';
default:
return '';
}
}
}

View File

@@ -0,0 +1,271 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\HasCovers;
use App\Traits\OrderByOrderAsc;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Identity extends Model
{
// use Cachable;
use HasCovers,
HasIdentityScopes,
OrderByOrderAsc,
SoftDeletes;
public $dates = [
'end_at'
];
const Conditions = [
'cost' => '原价',
'price' => '开通金额',
];
const Rules = [
'give_crystal' => '开通赠水滴',
'recommend_rate' => '推荐比例',
];
const CHANNEL_ONLINE = 1;
const CHANNEL_OFFLINE = 2;
const CHANNELS = [
self::CHANNEL_ONLINE => '线上',
self::CHANNEL_OFFLINE => '线下'
];
const EXPERIENCE_YES = 1;
const EXPERIENCE_NO = 0;
const EXPERIENCES = [
self::EXPERIENCE_YES => '是',
self::EXPERIENCE_NO => '否'
];
const JOB_YK = 0;
const JOB_TY = 1;
const JOB_JK = 2;
const JOB_NK = 3;
const JOB_CS = 4;
const JOB_HH = 5;
const JOBS = [
self::JOB_YK => '游客',
self::JOB_TY => '体验官',
self::JOB_JK => '季卡',
self::JOB_NK => '年卡',
self::JOB_CS => '创始',
self::JOB_HH => '合伙人',
];
protected $table = 'user_identities';
protected $casts = [
'conditions' => 'json',
'rules' => 'json',
'rights' => 'json',
'ruleshows' => 'json',
];
public function setConditionsAttribute($value)
{
$this->attributes['conditions'] = json_encode(array_values($value));
}
public function setRulesAttribute($value)
{
$rules = collect($this->rules);
foreach ($value as &$item) {
$info = $rules->where('name', $item['name'])->first();
if (! isset($item['icon']) && $info && isset($info['icon'])) {
$item['icon'] = $info['icon'];
}
}
$this->attributes['rules'] = json_encode(array_values($value));
}
public function setRuleshowsAttribute($value)
{
$rules = collect($this->ruleshows);
foreach ($value as &$item) {
$info = $rules->where('name', $item['name'])->first();
if (! isset($item['icon']) && $info && isset($info['icon'])) {
$item['icon'] = $info['icon'];
}
}
$this->attributes['ruleshows'] = json_encode(array_values($value));
}
public function setRightsAttribute($value)
{
$rights = collect($this->rights);
foreach ($value as &$item) {
$info = $rights->where('name', $item['name'])->first();
if (! isset($item['cover']) && $info && isset($info['cover'])) {
$item['cover'] = $info['cover'];
}
}
$this->attributes['rights'] = json_encode(array_values($value));
}
/**
* Notes: 获取所有规则
*
* @Author: 玄尘
* @Date: 2022/8/21 13:03
*/
public function getRules()
{
$rules = $this->ruleshows;
foreach ($rules as $key => $rule) {
if (isset($rule['icon'])) {
$rules[$key]['cover'] = $this->parseImageUrl($rule['icon']);
}
$rules[$key]['text'] = Arr::get(config('identity.show_rules'), $rule['name']);
}
return $rules;
}
/**
* Notes: 获取未开通身份时反馈的数据
*
* @Author: 玄尘
* @Date: 2022/8/26 14:16
* @return array|mixed
*/
public function getNotRules()
{
$rules = $this->rules;
if ($this->job == Identity::JOB_JK) {
return [
'give_crystal' => [
'cover' => $this->getRuleIcon('give_crystal'),
'value' => $this->getRule('give_crystal'),
'text' => '赠送水滴'
],
'recommend_coupon' => [
'cover' => $this->getRuleIcon('recommend_coupon'),
'value' => $this->getRule('recommend_coupon'),
'text' => '赠送抵值券'
],
'stock' => [
'value' => $this->stock,
'text' => "赠送{$this->stock}箱水"
],
'year' => [
'value' => $this->years,
'text' => $this->years."个月有效期"
],
];
}
if ($this->job == Identity::JOB_NK) {
return [
'stock' => [
'value' => $this->stock,
'text' => "赠送{$this->stock}箱水"
],
'year' => [
'value' => $this->years,
'text' => $this->years."个月有效期"
],
];
}
return $rules;
}
/**
* Notes : 组内用户
*
* @Date : 2021/5/6 12:06 下午
* @Author : < Jason.C >
* @return BelongsToMany
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class, 'user_identity')
->withTimestamps();
}
/**
* Notes : 不同的客服匹配不同的身份
*
* @Date : 2021/6/07 10:50
* @Author : Mr.wang
* @return BelongsToMany
*/
public function identities(): BelongsToMany
{
return $this->belongsToMany(Service::class, 'user_service_identity')
->withTimestamps();
}
/**
* 返回身份中的某一个规则
*
* @param string $key
* @param mixed $default
* @return mixed|string
*/
public function getRule(string $key = '', $default = '')
{
$values = collect($this->rules);
$value = $values->where('name', $key)->first();
if ($value) {
return $value['value'];
} else {
return $default;
}
}
/**
* Notes: 获取规则图标
*
* @Author: 玄尘
* @Date: 2022/8/26 14:24
* @param string $key
* @param $default
* @return mixed|string
*/
public function getRuleIcon(string $key = '', $default = '')
{
$values = collect($this->rules);
$value = $values->where('name', $key)->first();
if ($value && isset($value['icon'])) {
return $this->parseImageUrl($value['icon']);
} else {
return $default;
}
}
/**
* 返回条件中的某一个字段
*
* @param string $key
* @param mixed $default
* @return mixed|string
*/
public function getCondition(string $key = '', $default = '')
{
$values = collect($this->conditions);
$value = $values->where('name', $key)->first();
if ($value) {
return $value['value'];
} else {
return $default;
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\OrderByIdDesc;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Modules\User\Traits\BelongsToUser;
class IdentityLog extends Model
{
use BelongsToUser, OrderByIdDesc;
protected $table = 'user_identity_logs';
protected $casts = [
'source' => 'json',
];
const CHANNEL_AUTO = 'Auto';
const CHANNEL_REG = 'Reg';
const CHANNEL_SYSTEM = 'System';
const CHANNEL_MAP = [
self::CHANNEL_AUTO => '自动变更',
self::CHANNEL_REG => '注册默认',
self::CHANNEL_SYSTEM => '后台变更',
];
public function before_identity(): BelongsTo
{
return $this->belongsTo(Identity::class, 'before')->withDefault([
'name' => '已删除',
]);
}
public function after_identity(): BelongsTo
{
return $this->belongsTo(Identity::class, 'after')->withDefault([
'name' => '已删除',
]);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Modules\User\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Modules\User\Traits\BelongsToUser;
class IdentityMiddle extends Pivot
{
use BelongsToUser, MorphManyTimeline;
protected $table = 'user_identity';
protected $primaryKey = 'user_id';
public $dates = [
'started_at', 'ended_at'
];
static function boot()
{
parent::boot();
self::created(function ($identity) {
if ($identity->identity->order > 1) {
$identity->addTimeline();
}
});
}
public function identity(): BelongsTo
{
return $this->belongsTo(Identity::class, 'identity_id');
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Modules\Payment\Traits\WithPayments;
use Modules\User\Models\Traits\OrderActions;
use Modules\User\Traits\BelongsToUser;
class Order extends Model
{
use WithPayments,
OrderActions,
BelongsToUser;
protected $table = 'user_orders';
const STATE_INIT = 'INIT';
const STATE_SUCCESS = 'SUCCESS';
const STATE_REFUND = 'refund';
const STATES = [
self::STATE_INIT => '待审核',
self::STATE_SUCCESS => '已支付',
self::STATE_REFUND => '已退款',
];
const TYPE_OPEN = 1;
const TYPE_RENEW = 2;
const TYPES = [
self::TYPE_OPEN => '开通',
self::TYPE_RENEW => '续费',
];
const CHANNEL_IDENTITY = 1;
const CHANNEL_EXPERIENCE = 2;
const CHANNEL_PARTNER = 3;
const CHANNELS = [
self::CHANNEL_IDENTITY => '开通身份',
self::CHANNEL_EXPERIENCE => '开通体验官',
self::CHANNEL_PARTNER => '开通合伙人',
];
public $casts = [
'source' => 'json'
];
public function identity(): BelongsTo
{
return $this->belongsTo(Identity::class, 'identity_id');
}
/**
* Notes: 设置订单支付
* @Author: 玄尘
* @Date : 2020/11/12 11:19
*/
public function pay()
{
$this->state = self::STATE_SUCCESS;
$this->save();
event(new UserOrderPaid($this));
}
/**
* Notes: 是否可以支付
* @Author: 玄尘
* @Date : 2021/6/4 10:19
* @return bool
*/
public function canPay(): bool
{
return $this->state == self::STATE_INIT;
}
/**
* Notes: 是否可以退款
*
* @Author: 玄尘
* @Date: 2022/8/22 13:18
* @return bool
*/
public function canRefund(): bool
{
return $this->isPay();
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Encore\Admin\Traits\ModelTree;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Modules\User\Traits\BelongsToUser;
class Relation extends Model
{
use BelongsToUser,
ModelTree;
protected $table = 'user_relations';
protected $primaryKey = 'user_id';
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->setParentColumn('parent_id');
$this->setOrderColumn('created_at');
$this->setTitleColumn('user_id');
}
/**
* 上级用户
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(User::class, 'parent_id');
}
/**
* Notes: 关联中间表
*
* @Author: 玄尘
* @Date: 2022/8/19 13:16
*/
public function identities(): BelongsToMany
{
return $this->belongsToMany(
Identity::class,
'user_identity',
'user_id',
'identity_id',
);
}
/**
* 所有下级用户
*
* @return HasManyThrough
*/
public function children(): HasManyThrough
{
return $this->hasManyThrough(
User::class,
Relation::class,
'parent_id',
'id',
'user_id',
'user_id'
);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\HasCovers;
use App\Traits\HasStatus;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Service extends Model
{
use Cachable,
HasStatus,
SoftDeletes,
HasCovers;
protected $table = 'user_services';
/**
* Notes : 不同的客服匹配不同的身份
* @Date : 2021/6/07 10:40
* @Author : Mr.wang
* @return BelongsToMany
*/
public function identities(): BelongsToMany
{
return $this->belongsToMany(Identity::class, 'user_service_identity')
->withTimestamps();
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Modules\User\Traits\BelongsToUser;
class Sign extends Model
{
use BelongsToUser;
protected $table = 'user_signs';
protected $dates = [
'last_sign_at',
'reset_at',
];
const FINISH_INIT = 0;
const FINISH_SIGN = 1;
const FINISH_LOG = 2;
const FINISHS = [
self::FINISH_INIT => '进行中',
self::FINISH_SIGN => '打卡完成',
self::FINISH_LOG => '报告完成',
];
protected static function boot()
{
parent::boot();
self::saved(function ($sign) {
$params = SignConfig::getParams();
if ($params['open'] == 1 && $sign->need_case == 0 && $sign->continue_days >= $params['cycle_day']) {
$sign->update(['need_case' => 1, 'is_finish' => 1]);
}
});
}
/**
* Notes : 获取最新连续签到数
*
* @Date : 2021/5/28 12:00
* @Author : Mr.wang
* @return int|mixed
*/
public function getContinueDays()
{
if ($this->last_sign_at->diffInDays() > 1) {
$continue = 1;
} else {
$continue = $this->continue_days + 1;
}
return $continue;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\HasCovers;
use App\Traits\HasStatus;
class SignBanner extends Model
{
use HasStatus,
HasCovers;
protected $table = 'user_sign_banners';
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
class SignConfig extends Model
{
use Cachable;
const TYPE_SINGLE = 'single';
const TYPE_CONTINUOUS = 'continuous';
const TYPE_CYCLE = 'cycle';
const TYPES = [
self::TYPE_SINGLE => '单日',
self::TYPE_CONTINUOUS => '连续',
self::TYPE_CYCLE => '周期连续',
];
const SHOWTYPES_DAY = 'day';
const SHOWTYPES_WEEK = 'week';
const SHOWTYPES_MONTH = 'month';
const SHOWTYPES = [
self::SHOWTYPES_DAY => '7天展示',
self::SHOWTYPES_WEEK => '一周展示',
self::SHOWTYPES_MONTH => '一月展示',
];
protected $table = 'user_sign_configs';
protected $casts = [
'params' => 'json',
'tasks' => 'json',
];
public static function getParams($key = '')
{
$model = SignConfig::find(1);
if (! $key) {
return collect($model->params);
} else {
return $model->params[$key] ?? '';
}
}
public static function getTasks($key = '')
{
$model = SignConfig::find(1);
if (! $key) {
return collect($model->tasks);
} else {
return $model->tasks[$key] ?? '';
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Modules\User\Traits\BelongsToUser;
class SignLog extends Model
{
use BelongsToUser;
const UPDATED_AT = null;
protected $table = 'user_sign_logs';
protected $casts = [
'date' => 'date',
];
protected static function boot()
{
parent::boot();
self::created(function ($model) {
});
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\HasStatus;
class SignText extends Model
{
use HasStatus;
protected $table = 'user_sign_texts';
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\OrderByIdDesc;
class Sms extends Model
{
use OrderByIdDesc;
protected $table = 'user_sms';
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
class SmsConfig extends Model
{
use Cachable;
protected $table = 'user_sms_configs';
protected $casts = [
'template' => 'json',
];
public static function boot()
{
parent::boot();
self::saved(function ($model) {
if ($model->in_use && $model->id) {
self::where('id', '<>', $model->id)
->where('in_use', 1)
->update(['in_use' => 0]);
}
});
}
/**
* Notes : 默认网关配置
* @Date : 2021/5/27 5:18 下午
* @Author : < Jason.C >
* @return array
*/
public function getGateway(): array
{
return SmsGateway::where('slug', $this->default_gateway)->value('configs');
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
class SmsGateway extends Model
{
use Cachable;
protected $table = 'user_sms_gateways';
protected $casts = [
'configs' => 'json',
];
}

View File

@@ -0,0 +1,283 @@
<?php
namespace Modules\User\Models\Traits;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Storage;
trait CertificationTrait
{
protected $params = [];
protected $header = [];
protected $errorMessage = '';
/**
* Notes : 自动认证操作
*
* @Author: Mr.wang
* @Date : 2021/12/7 13:53
* @param $keys
* @return array|int[]
*/
public function autoVerified($keys): array
{
if (config('userCertification.is_open')) {
if (config('userCertification.is_ocr_open')) {
$verified = $this->ocrVerified(Storage::url($keys['front_card']), 'front');
if (! $verified) {
return [
'code' => 0,
'message' => $this->getErrorMessage(),
];
} else {
if ($verified['words_result']['姓名']['words'] != $keys['name']) {
return [
'code' => 0,
'message' => '正面图片与填写信息不符',
];
}
if ($verified['words_result']['公民身份号码']['words'] != $keys['idcard']) {
return [
'code' => 0,
'message' => '正面图片与填写信息不符',
];
}
}
$verified = $this->ocrVerified(Storage::url($keys['back_card']), 'back');
if (! $verified) {
return [
'code' => 0,
'message' => $this->getErrorMessage(),
];
} else {
if ($verified['image_status'] != 'normal') {
return [
'code' => 0,
'message' => '背面图片与填写信息不符',
];
}
}
}
$base = [
'name' => $keys['name'],
'idcard' => $keys['idcard'],
];
$data = $this->check($base);
if ($data === false) {
return [
'code' => 0,
'message' => $this->getErrorMessage(),
];
} else {
return [
'code' => 1,
];
}
} else {
return [
'code' => 1,
];
}
}
/**
* Notes : ocr认证
*
* @Author: Mr.wang
* @Date : 2021/12/7 13:54
* @param $image
* @param $type
* @return bool
*/
public function ocrVerified($image, $type): bool
{
$AppID = config('userCertification.ocr_appid');
$SecretKey = config('userCertification.ocr_secretkey');
if (empty($AppID)) {
$this->setErrorMessage('请配置接口AppID');
return false;
}
if (empty($SecretKey)) {
$this->setErrorMessage('请配置接口SecretKey');
return false;
}
$access = $this->getAccess($AppID, $SecretKey);
if ($access === false) {
$this->setErrorMessage('access_token不正确');
return false;
}
$token = $access->access_token;
$apiUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/idcard';
$result = $this->getOcr($apiUrl, $token, $image, $type);
if (($result['error_code'] ?? 0) == 100) {
$this->setErrorMessage($result['error_msg'] ?? '未知错误');
return false;
} else {
if (empty($result['words_result']) || ! isset($result['words_result'])) {
$this->setErrorMessage('图片未识别');
return false;
}
return $result;
}
}
protected function getAccess($AppID, $SecretKey)
{
$authUrl = 'https://aip.baidubce.com/oauth/2.0/token';
try {
$Client = new Client();
$response = $Client->post($authUrl, [
'query' => [
'grant_type' => 'client_credentials',
'client_id' => $AppID,
'client_secret' => $SecretKey,
],
]);
$result = json_decode($response->getBody()->getContents());
return $result;
} catch (\Exception $e) {
return false;
}
}
protected function getOcr($url, $token, $image, $type)
{
$url = $url.'?access_token='.$token;
$params = [
'url' => $image,
'id_card_side' => $type,
];
try {
$Client = new Client();
$response = $Client->post($url, ['form_params' => $params]);
$result = json_decode($response->getBody()->getContents(), true);
return $result;
} catch (\Exception $e) {
return false;
}
}
public function getErrorMessage()
{
return $this->errorMessage;
}
protected function setErrorMessage($message)
{
$this->errorMessage = $message;
}
/**
* Notes : 网络验证
*
* @Date : 2021/9/25 15:42
* @Author : Mr.wang
* @param $keys
* @return bool|mixed
*/
protected function check($keys): bool
{
$apiUrl = config('userCertification.url');
if (empty($apiUrl)) {
$this->setErrorMessage('请配置接口地址');
return false;
}
$apiCode = config('userCertification.code');
if (empty($apiCode)) {
$this->setErrorMessage('请配置接口Code');
return false;
}
$this->setParams($keys);
$this->setHeaders();
$result = $this->dopost($apiUrl);
try {
if (config('userCertification.type') == 2) {
if ($result->code == 0 && $result->message == '成功') {
if ($result->result->res == 1) {
return true;
} else {
$this->setErrorMessage('信息'.$result->result->description);
return false;
}
} else {
$this->setErrorMessage('信息'.$result->result->description);
return false;
}
} else {
if ($result->code == 200 && $result->success === true) {
if ($result->data->result == 0) {
return true;
} else {
$this->setErrorMessage($result->data->desc);
return false;
}
} else {
$this->setErrorMessage($result->msg);
return false;
}
}
} catch (\Exception $e) {
return $result;
}
}
protected function setParams($keys)
{
$this->params = $keys;
}
protected function setHeaders()
{
$this->header = [
"Authorization" => 'APPCODE '.config('userCertification.code'),
"Accept" => "application/json",
];
}
protected function dopost($url)
{
try {
$Client = new Client();
$response = $Client->get($url, ['query' => $this->params, 'headers' => $this->header]);
// switch (config('usercertification.request_method')) {
// case 'get':
// $response = $Client->get($url, ['query' => $this->params, 'headers' => $this->header]);
// break;
// case 'post':
// $response = $Client->post($url, ['query' => $this->params, 'headers' => $this->header]);
// break;
// default:
// $this->setErrorMessage('不允许的请求方式');
//
// return false;
// break;
// }
$result = json_decode($response->getBody()->getContents());
return $result;
} catch (\Exception $e) {
preg_match_all('/[\x{4e00}-\x{9fff}]+/u', $e->getmessage(), $cn_name);
$this->setErrorMessage($cn_name[0][0]);
return false;
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Modules\User\Models\Traits;
use Illuminate\Database\Eloquent\Builder;
use Modules\User\Models\Identity;
trait HasIdentityScopes
{
/**
* Notes: 体验官
*
* @Author: 玄尘
* @Date: 2022/8/18 15:25
* @param Builder $query
* @return Builder
*/
public function scopeTy(Builder $query): Builder
{
return $query->where('job', Identity::JOB_TY);
}
/**
* Notes: 季卡
*
* @Author: 玄尘
* @Date: 2022/8/18 15:26
* @param Builder $query
* @return Builder
*/
public function scopeJk(Builder $query): Builder
{
return $query->where('job', Identity::JOB_JK);
}
/**
* Notes: 年卡
*
* @Author: 玄尘
* @Date: 2022/8/18 15:27
* @param Builder $query
* @return Builder
*/
public function scopeNk(Builder $query): Builder
{
return $query->where('job', Identity::JOB_NK);
}
/**
* Notes: 创始
*
* @Author: 玄尘
* @Date: 2022/8/18 15:27
* @param Builder $query
* @return Builder
*/
public function scopeCs(Builder $query): Builder
{
return $query->where('job', Identity::JOB_CS);
}
/**
* Notes: 合伙人
*
* @Author: 玄尘
* @Date: 2022/8/18 15:27
* @param Builder $query
* @return Builder
*/
public function scopeHh(Builder $query): Builder
{
return $query->where('job', Identity::JOB_HH);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Modules\User\Models\Traits;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Modules\User\Models\UserLog;
trait HasLog
{
/**
* Notes: 库存
*
* @Author: 玄尘
* @Date: 2022/7/26 16:13
* @return HasMany
*/
public function logs(): HasMany
{
return $this->hasMany(UserLog::class);
}
/**
* Notes:
*
* @Author: 玄尘
* @Date: 2022/9/7 14:22
* @param $admin
* @param $remark
*/
public function addLog($admin, $remark)
{
$this->logs()->create([
'user_id' => $this->getKey(),
'admin_id' => $admin->getKey(),
'remark' => $remark
]);
}
}

View File

@@ -0,0 +1,192 @@
<?php
namespace Modules\User\Models\Traits;
use Exception;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Support\Facades\DB;
use Modules\User\Models\Relation;
use Modules\User\Models\User;
trait HasRelations
{
/**
* 这个参数,是为了给用户创建事件监听模型使用的
*
* @var int
*/
public int $parent_id = 0;
/**
* Notes: 创建的监听,转移到这里了
*
* @Author: <C.Jason>
* @Date : 2020/1/13 5:52 下午
*/
public static function bootHasRelations()
{
self::created(function ($model) {
if (isset($model->parent_id) && is_numeric($model->parent_id) && $model->parent_id != 0) {
$parent = User::find($model->parent_id);
if ($parent && $model->id != $model->parent_id) {
$model->relation()->create([
'parent_id' => $parent->id,
'bloodline' => $parent->relation->bloodline.$parent->id.',',
'layer' => $parent->relation->layer + 1,
]);
} else {
$model->relation()->create([
'parent_id' => config('user.default_parent_id'),
'bloodline' => config('user.default_parent_id').',',
'layer' => 1,
]);
}
} else {
$model->relation()->create([
'parent_id' => config('user.default_parent_id'),
'bloodline' => config('user.default_parent_id').',',
'layer' => 1,
]);
}
});
}
/**
* Notes: 这个方法,是为了给用户创建事件监听模型使用的
* 目的是去除attribute里面的parent_id参数防止数据库写入错误
*
* @Author: <C.Jason>
* @Date : 2020/1/13 5:58 下午
* @param int $parentID
*/
protected function setParentIdAttribute(int $parentID)
{
$this->parent_id = $parentID;
}
/**
* Notes: 用户关联关系
*
* @Author: <C.Jason>
* @Date : 2020/1/13 5:51 下午
* @return HasOne
*/
public function relation(): HasOne
{
return $this->hasOne(Relation::class)->withDefault();
}
/**
* Notes: 上级用户
*
* @Author: <C.Jason>
* @Date : 2020/1/13 5:51 下午
* @return HasOneThrough
*/
public function parent(): HasOneThrough
{
return $this->hasOneThrough(
User::class,
Relation::class,
'user_id',
'id',
'id',
'parent_id'
);
}
/**
* Notes: 所有下级用户
*
* @Author: <C.Jason>
* @Date : 2020/1/13 5:51 下午
* @return mixed
*/
public function children(): HasManyThrough
{
return $this->hasManyThrough(
User::class,
Relation::class,
'parent_id',
'id',
'id',
'user_id'
);
}
/**
* 调整隶属
*
* @param int $parent_id
* @return bool
* @throws Exception
*/
public function updateParent(int $parent_id = 0): bool
{
if ($parent_id == $this->id) {
throw new Exception('不能绑定自己');
}
if (Relation::where('user_id', $parent_id)
->where('bloodline', 'like', "%,".$this->id.",%")
->exists()) {
throw new Exception('不能绑定自己的下级用户');
}
try {
$relation = $this->relation;
$new_blood = '0,';
$new_layer = 1;
$new_parent_id = 0;
$blood = $relation->bloodline;
$layer = $relation->layer;
$parent = User::find($parent_id);
if ($parent) {
$new_parent_id = $parent->id;
$new_blood = $parent->relation->bloodline.$new_parent_id.',';
$new_layer = $parent->relation->layer + 1;
}
$relation->parent_id = $new_parent_id;
$relation->bloodline = $new_blood;
$relation->layer = $new_layer;
if ($relation->save()) {
$diffLayer = $layer - $new_layer;
DB::update("UPDATE `user_relations` SET `bloodline`=CONCAT(?,SUBSTRING(bloodline,LENGTH(?)+1)),`layer`=`layer`-? WHERE `bloodline` LIKE ?",
[$new_blood, $blood, $diffLayer, "%,".$this->id.",%"]);
}
return true;
} catch (Exception $e) {
return false;
}
}
/**
* Notes: 获取下级数量
*
* @Author: 玄尘
* @Date : 2021/9/24 11:42
*/
public function getRelationCount(): array
{
return [
'all' => Relation::query()
->whereIn('layer', [$this->relation->layer + 1, $this->relation->layer + 2])
->where('bloodline', 'like', "%,".$this->id.",%")
->count(),
'one' => Relation::query()
->where('layer', $this->relation->layer + 1)
->where('bloodline', 'like', "%,".$this->id.",%")
->count(),
'two' => Relation::query()
->where('layer', $this->relation->layer + 2)
->where('bloodline', 'like', "%,".$this->id.",%")
->count(),
];
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Modules\User\Models\Traits;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Arr;
use Modules\User\Models\Sign;
use Modules\User\Models\SignConfig;
use Modules\User\Models\SignLog;
trait HasSign
{
/**
* 用户签到
*
* @return HasOne
*/
public function sign(): HasOne
{
return $this->hasOne(Sign::class);
}
/**
* Notes : 当前日期是否签到
*
* @Date : 2022/1/6 13:25
* @Author : Mr.wang
* @param string $date
* @return bool
*/
public function isSign($date = null): bool
{
$date = $date ?? Carbon::now()->format('Y-m-d');
return $this->signLogs()->whereDate('date', $date)->exists();
}
/**
* Notes : 用户签到日志
*
* @Date : 2022/1/6 13:23
* @Author : Mr.wang
* @return HasMany
*/
public function signLogs(): HasMany
{
return $this->hasMany(SignLog::class);
}
/**
* Notes: 获取签到数据
*
* @Author: 玄尘
* @Date: 2022/8/3 15:35
*/
public function getSignData(): array
{
$all = Arr::get(SignConfig::getParams(), 'cycle_day', 0);
$data = [
'continue' => 0,
'total' => 0,
'all' => $all
];
if ($this->sign) {
$data = [
'continue' => $this->sign->continue_days,
'total' => $this->sign->counts,
'all' => $all,
];
}
$data = array_merge($data, [
'text' => '第'.$data['continue'].'/'.$data['all'].'天'
]);
return $data;
}
}

View File

@@ -0,0 +1,153 @@
<?php
namespace Modules\User\Models\Traits;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Arr;
use Modules\Mall\Facades\Item;
use Modules\Mall\Facades\Order as OrderFacade;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\GoodsSku;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\OrderItem;
use Modules\User\Models\Identity;
use Modules\User\Models\UserStock;
use Modules\User\Models\UserStockLog;
trait HasStock
{
/**
* Notes: 库存
*
* @Author: 玄尘
* @Date: 2022/7/26 16:13
* @return HasOne
*/
public function userStock(): HasOne
{
return $this->hasOne(UserStock::class)->withDefault([
'stock' => 0,
'hold' => 0,
'residue' => 0,
]);
}
/**
* Notes: 获取库存
*
* @Author: 玄尘
* @Date: 2022/7/29 13:58
*/
public function getStockData(): array
{
$identity = $this->identityFirst();
$min = 1;
if ($identity->job == Identity::JOB_TY) {
$min = $identity->stock;
}
$deliver = OrderItem::query()
->whereHas('order', function ($q) {
$q->TypeSample()->ByUser($this)->paid();
})->sum('qty');
$deliverd = OrderItem::query()
->whereHas('order', function ($q) {
$q->TypeSample()->ByUser($this)->whereIn('state', [
Order::STATUS_SIGNED,
Order::STATUS_DELIVERED,
]);
})->sum('qty');
return [
'case_id' => $this->case ? $this->case->id : 0,
'stock' => $this->userStock->stock,//总库存
'hold' => $this->userStock->hold,//已发货
'residue' => (int) bcsub($this->userStock->residue, $deliver),//未提货
'deliver' => $deliver,//待发货
'deliverd' => $deliverd,//已发货
'min_pick' => $min,//最少提货数量
];
}
/**
* Notes: 增加库存
*
* @Author: 玄尘
* @Date: 2022/7/26 16:18
*/
public function addStock($identity_id, $type = null, $addStock = 0)
{
try {
$identity = Identity::find($identity_id);
$stock = $identity->stock;
if ($addStock) {
$stock = $addStock;
}
$userStock = $this->userStock;
if (! $type) {
$type = UserStockLog::TYPE_IN;
}
if (isset($userStock->id)) {
$userStock->increment('stock', $stock);
} else {
$userStock = UserStock::query()->updateOrCreate([
'user_id' => $this->id,
], [
'stock' => $stock,
]);
}
$this->addStockLog($type, $stock, $identity_id);
return true;
} catch (\Exception $exception) {
return new \Exception($exception->getMessage());
}
}
/**
* Notes: 增加销量
*
* @Author: 玄尘
* @Date: 2022/7/29 14:44
*/
public function addHold($number)
{
$this->userStock->increment('hold', $number);
$this->addStockLog(UserStockLog::TYPE_OUT, -$number);
}
/**
* Notes: 增加日志
*
* @Author: 玄尘
* @Date: 2022/8/2 14:55
* @param $type
* @param $variable
* @param int $identity_id
*/
public function addStockLog($type, $variable, int $identity_id = 0)
{
$userStock = UserStock::where('user_id', $this->id)->first();
UserStockLog::query()
->create([
'user_stock_id' => $userStock->id,
'type' => $type,
'variable' => $variable,
'identity_id' => $identity_id,
]);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Modules\User\Models\Traits;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Modules\User\Models\Order;
trait HasVipOrders
{
/**
* Notes : 用户的订单列表
*
* @Date : 2021/4/19 10:28 上午
* @Author : < Jason.C >
* @return HasMany
*/
public function vipOrders(): HasMany
{
return $this->hasMany(Order::class);
}
/**
* Notes: 开通会员缴费
*
* @Author: 玄尘
* @Date: 2022/9/7 13:59
*/
public function getOpenVipPrices($type = 'all')
{
return $this->vipOrders
->where('state', Order::STATE_SUCCESS)
->where('price', '>', 0)
->pluck('price');
}
/**
* Notes: 创建订单
*
* @Author: 玄尘
* @Date: 2022/9/7 16:55
*/
public function createOrder($identity_id, $year, $price, $stock, $source = null)
{
$data = [
'user_id' => $this->id,
'identity_id' => $identity_id,
'year' => $year,
'type' => 1,
'name' => '',
'card_no' => '',
'cover' => '',
'stock' => $stock,
'state' => Order::STATE_INIT,
'price' => $price,
'source' => $source,
];
$order = Order::create($data);
$order->pay();
return $order;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Modules\User\Models\Traits;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Modules\User\Models\UserWechat;
trait HasWechat
{
/**
* Notes : 用户微信信息
*
* @Date : 2021/5/26 11:14
* @Author : Mr.wang
* @return HasOne
*/
public function wechat(): HasOne
{
return $this->hasOne(UserWechat::class);
}
/**
* Notes: 是否关注公众号
*
* @Author: 玄尘
* @Date: 2022/8/3 16:23
*/
public function isOfficialSubscribe(): bool
{
return $this->wechat ? $this->wechat->isOfficialSubscribe() : false;
}
/**
* Notes: 获取openids
*
* @Author: 玄尘
* @Date: 2022/9/27 14:51
* @return string[]
*/
public function getOpenids()
{
if ($this->wechat) {
$data = $this->wechat->getOpenids();
} else {
$data = [
'mini' => '',
'official' => '',
];
}
return $data;
}
}

View File

@@ -0,0 +1,247 @@
<?php
namespace Modules\User\Models\Traits;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Modules\User\Events\UserJoinIdentity;
use Modules\User\Events\UserRemoveIdentity;
use Modules\User\Events\UserUpdateIdentity;
use Modules\User\Models\Identity;
use Modules\User\Models\IdentityLog;
use Modules\User\Models\IdentityMiddle;
trait JoinIdentity
{
/**
* 用户加入身份
*
* @param int $identity_id 身份ID
* @param string $channel 加入渠道
* @param array $source 其他溯源信息
* @throws Exception
*/
public function joinIdentity(
int $identity_id = 0,
string $channel = IdentityLog::CHANNEL_AUTO,
array $source = []
) {
// 单身份,并且已经开通身份
if (config('identity.can_has_many_identity') == false && $this->identityMiddle->count() >= 1) {
//续费
if ($this->hasIdentity($identity_id)) {
$this->renewIdentity($identity_id);
} else {
//升级
self::updateIdentity($identity_id, $channel, $source);
}
} else {
$identity = Identity::find($identity_id);
if ($identity) {
//未开通此身份
if ($this->hasIdentity($identity_id) == false) {
$remark = Arr::get($source, 'remark', '加入身份');
$res = self::identityLog(0, $identity_id, $channel, $remark, $source);
if ($res) {
$serial = $source['serial'] ?? '';
if ($identity->serial_open && empty($serial)) {
$serial = self::getNewSerial($identity->serial_places);
}
$value = [];
if ($identity->years) {
$value = [
'started_at' => now(),
'ended_at' => now()->addMonths($identity->years),
];
}
$this->identityMiddle()
->updateOrCreate([
'identity_id' => $identity_id,
'serial' => $serial,
], $value);
event(new UserJoinIdentity($this, $identity));
} else {
throw new Exception('添加日志失败');
}
} else {
//已开通此身份
$this->renewIdentity($identity_id);
}
} else {
throw new Exception('身份信息不存在');
}
}
}
/**
* Notes: 续费
*
* @Author: 玄尘
* @Date : 2021/6/8 9:49
* @param int $identity_id
*/
public function renewIdentity(int $identity_id)
{
$identity = Identity::find($identity_id);
$before = $this->identityMiddle()->where('identity_id', $identity_id)->first();
//vip
if ($identity->years) {
IdentityMiddle::where('user_id', $this->id)
->where('identity_id', $identity_id)
->update([
'ended_at' => Carbon::parse($before->ended_at)->addMonths($identity->years),
]);
} else {
IdentityMiddle::where('user_id', $this->id)
->where('identity_id', $identity_id)
->update([
'started_at' => null,
'ended_at' => null,
]);
}
event(new UserJoinIdentity($this, $before->identity));
}
/**
* 用户身份调整当多身份关闭时由join触发
*
* @param int $identity_id
* @param string $channel
* @param array $source
* @throws Exception
*/
public function updateIdentity(
int $identity_id = 0,
string $channel = IdentityLog::CHANNEL_AUTO,
array $source = []
) {
$before = $this->identityMiddle()->first();
if ($this->identityMiddle()->count() > 1) {
$this->identityMiddle()->where('identity_id', '!=', $before->identity_id)->delete();
}
$identity = Identity::find($identity_id);
if ($identity) {
$remark = Arr::get($source, 'remark', '身份变更');
$res = self::identityLog($before->identity_id, $identity_id, $channel, $remark, $source);
if ($res) {
$serial = $source['serial'] ?? '';
if ($identity->serial_open && empty($serial)) {
$serial = self::getNewSerial($identity->serial_places);
}
$data = [
'identity_id' => $identity_id,
'serial' => $serial,
'started_at' => null,
'ended_at' => null,
];
if ($identity->years) {
$data['started_at'] = now();
$data['ended_at'] = now()->addMonths($identity->years);
}
IdentityMiddle::where('user_id', $this->id)
->where('identity_id', $before->identity_id)
->update($data);
event(new UserUpdateIdentity($this, $before->identity, $identity));
} else {
throw new Exception('调整身份');
}
} else {
throw new Exception('身份信息不存在');
}
}
/**
* 判断用户是否参与身份
*
* @param int $identity_id
* @return bool
*/
public function hasIdentity(int $identity_id = 0): bool
{
if ($identity_id) {
$res = $this->identityMiddle()->where('identity_id', $identity_id)->first();
return (bool) $res;
} else {
return true;
}
}
/**
* 用户移除身份
*
* @param int $identity_id 身份ID
* @param string $channel 移除渠道
* @param array $source 其他溯源信息
* @throws Exception
*/
public function removeIdentity(
int $identity_id = 0,
string $channel = IdentityLog::CHANNEL_AUTO,
array $source = []
) {
if ($this->identityMiddle()->where('identity_id', $identity_id)->first()) {
$remark = Arr::get($source, 'remark', '身份移除');
$res = self::identityLog($identity_id, 0, $channel, $remark, $source);
if ($res) {
$this->identityMiddle()->where('identity_id', $identity_id)->delete();
event(new UserRemoveIdentity($this, Identity::find($identity_id)));
} else {
throw new Exception('移除记录失败');
}
} else {
throw new Exception('用户不在身份组中');
}
}
/**
* 生成数据库中部存在的号码
*
* @return int
*/
public function getNewSerial($places = 8)
{
try {
$min = pow(10, $places - 1);
$max = 9 * $min;
$query = 'SELECT code FROM (SELECT CEILING(RAND()*'.$max.'+'.$min.') AS code FROM user_identity UNION SELECT CEILING(RAND()*'.$max.'+'.$min.') AS code) AS ss WHERE "code" NOT IN (SELECT serial FROM user_identity where serial !=null) LIMIT 1';
$res = DB::select($query);
return (int) $res[0]->code;
} catch (Exception $e) {
return '';
}
}
public function identityLog(
$before = 0,
$after = 0,
$channel = '',
$remark = '',
$source = []
): Model {
return $this->identity_logs()->create([
'before' => $before,
'after' => $after,
'channel' => $channel,
'remark' => $remark,
'source' => $source,
]);
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace Modules\User\Models\Traits;
use Modules\Coupon\Models\Coupon;
use Modules\Payment\Models\Payment;
use Modules\User\Events\UserOrderPaid;
use Exception;
use Modules\User\Models\Order;
trait OrderActions
{
/**
* Notes: 设置订单支付
*
* @Author: 玄尘
* @Date : 2020/11/12 11:19
*/
public function pay()
{
try {
$this->state = self::STATE_SUCCESS;
$this->save();
event(new UserOrderPaid($this));
return true;
} catch (\Exception $exception) {
return $exception->getMessage();
}
}
/**
* Notes: 退款
*
* @Author: 玄尘
* @Date: 2022/8/22 13:09
*/
public function refund(): bool
{
try {
$payment = $this->payment;
if (! $payment) {
throw new Exception("退款失败,未找到支付信息");
}
$refund = $payment->refunds()->create([
'total' => $this->price,
]);
//微信支付
if ($payment->driver == Payment::DRIVER_WECHAT) {
$order = [
'out_trade_no' => $payment->trade_id,
'out_refund_no' => $refund->refund_no,
'amount' => [
'refund' => $payment->total * 100,
'total' => $payment->total * 100,
'currency' => 'CNY',
],
];
$result = app('pay.wechat')->refund($order);
if (isset($result->code) && $result->code == 'PARAM_ERROR') {
throw new Exception("退款失败,".$result->message);
}
$this->update([
'state' => Order::STATE_REFUND
]);
$refund->update([
'refund_at' => now()
]);
return true;
} elseif ($payment->driver == Payment::DRIVER_ALIPAY) {//支付宝支付
$order = [
'out_trade_no' => $this->order->order_no,
'refund_amount' => $this->actual_total,
];
$result = app('pay.alipay')->refund($order);
if ($result->code != '10000') {
throw new Exception("退款失败,".$result->msg);
}
$this->update([
'state' => Order::STATE_REFUND
]);
$refund->update([
'refund_at' => now()
]);
return true;
} else {
throw new Exception("退款失败,未找到支付路径");
}
} catch (Exception $exception) {
throw new Exception($exception->getMessage());
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Modules\User\Models\Traits;
trait WechatAttribute
{
/**
* Notes : 获取微信公众号openid
*
* @Date : 2021/5/26 17:03
* @Author : Mr.wang
* @return string
*/
public function getOfficialOpenidAttribute(): string
{
return $this->official->openid ?? '';
}
/**
* Notes: description
*
* @Author: 玄尘
* @Date: 2022/8/3 16:22
*/
public function isOfficialSubscribe(): bool
{
return $this->official()->where('subscribe', 1)->exists();
}
/**
* Notes : 获取微信小程序openid
*
* @Date : 2021/5/26 17:03
* @Author : Mr.wang
* @return string
*/
public function getMiniOpenidAttribute(): string
{
return $this->mini->openid ?? '';
}
/**
* Notes: 获取用户openid
*
* @Author: 玄尘
* @Date: 2022/9/26 13:52
* @param $channel
* @return mixed
*/
public function getUserOpenid($channel)
{
$channel = strtolower($channel);
if ($channel == 'mp') {
return Arr::get($this->getOpenids(), 'official');
} else {
return Arr::get($this->getOpenids(), 'mini');
}
}
/**
* Notes: 获取openids
*
* @Author: 玄尘
* @Date: 2022/9/26 15:06
* @return array
*/
public function getOpenids(): array
{
return [
'mini' => $this->mini()->value('openid') ?? '',
'official' => $this->official()->value('openid') ?? '',
];
}
}

View File

@@ -0,0 +1,306 @@
<?php
namespace Modules\User\Models;
use App\Traits\Macroable;
use App\Traits\OrderByIdDesc;
use Encore\Admin\Traits\DefaultDatetimeFormat;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Foundation\Auth\User as Authenticate;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Config;
use Laravel\Sanctum\HasApiTokens;
use Modules\Coupon\Traits\UserHasCoupon;
use Modules\Mall\Traits\HasAddresses;
use Modules\Mall\Traits\HasOrders;
use Modules\Task\Traits\HasTasks;
use Modules\User\Models\Traits\HasChannel;
use Modules\User\Models\Traits\HasInvite;
use Modules\User\Models\Traits\HasLog;
use Modules\User\Models\Traits\HasStock;
use Modules\User\Models\Traits\HasVipOrders;
use Modules\User\Models\Traits\HasWechat;
use Modules\Withdraw\Traits\HasWithdraws;
use Modules\User\Models\Order as UserOrder;
use Overtrue\LaravelFavorite\Traits\Favoriter;
use Overtrue\LaravelSubscribe\Traits\Subscriber;
use SimpleSoftwareIO\QrCode\Facades\QrCode;
use Vinkla\Hashids\Facades\Hashids;
class User extends Authenticate
{
use HasApiTokens,
DefaultDatetimeFormat,
Macroable,
HasStock,
HasLog,
HasGout,
HasWithdraws,
HasOrders,
HasVipOrders,
HasWechat,
HasInvite,
HasAddresses,
Notifiable,
HasTasks,
Subscriber,
Favoriter,
OrderByIdDesc,
Traits\JoinIdentity,
Traits\HasRelations,
Traits\HasSign;
const STATUS_INIT = 1;
const STATUS_REFUND = 2;
const STATUS = [
self::STATUS_INIT => '正常',
self::STATUS_REFUND => '退费',
];
/**
* 禁止写入的字段
*
* @var array
*/
protected $guarded = [];
/**
* 模型隐藏字段
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
protected static function boot()
{
parent::boot();
self::created(function ($user) {
$user->info()->create([
'nickname' => '用户'.substr($user->username, -4),
]);
$user->sign()->create([
'continue_days' => 0,
'counts' => 0,
'last_sign_at' => null,
]);
$user->account()->create();
$defaultIdentity = Identity::where('default', 1)->first();
if ($defaultIdentity) {
$user->joinIdentity($defaultIdentity->id, IdentityLog::CHANNEL_REG);
}
});
}
/**
* Notes : 用户资料
*
* @Date : 2021/3/11 5:41 下午
* @Author : < Jason.C >
* @return HasOne
*/
public function info(): HasOne
{
return $this->hasOne(UserInfo::class);
}
/**
* Notes : 用户账户
*
* @Date : 2021/4/27 11:20 上午
* @Author : < Jason.C >
* @return HasOne
*/
public function account(): HasOne
{
return $this->hasOne(Account::class);
}
/**
* Notes : 当用户有密码的时候才加密
*
* @Date : 2021/3/11 1:43 下午
* @Author : < Jason.C >
* @param $password
*/
public function setPasswordAttribute($password)
{
if ($password) {
$this->attributes['password'] = bcrypt($password);
}
}
/**
* 第一身份
*
* @return mixed|null
*/
public function identityFirst()
{
return $this->identities()->first();
}
/**
* Notes : 用户身份
*
* @Date : 2021/5/6 12:00 下午
* @Author : < Jason.C >
*/
public function identities(): BelongsToMany
{
return $this->belongsToMany(Identity::class, 'user_identity')
->withTimestamps()
->withPivot(['started_at', 'ended_at', 'serial']);
}
/**
* 用户中间表关联
*
* @return HasMany
*/
public function identityMiddle(): HasMany
{
return $this->hasMany(IdentityMiddle::class);
}
/**
* 身份变动表关联
*
* @return HasMany
*/
public function identity_logs(): HasMany
{
return $this->hasMany(IdentityLog::class);
}
/**
* Notes: 升级缴费订单
*
* @Author: 玄尘
* @Date: 2022/9/7 13:57
*/
public function vipOrders(): HasMany
{
return $this->hasMany(UserOrder::class);
}
/**
* 用户个人认证
*
* @return HasOne
*/
public function certification(): HasOne
{
return $this->hasOne(UserCertification::class);
}
/**
* Notes: 获取状态信息
*
* @Author: 玄尘
* @Date: 2022/8/3 16:30
*/
public function getStateData(): array
{
$identity = $this->identities->first();
return [
'isSubscribe' => $this->isOfficialSubscribe(),//关注
'isVip' => $identity ? $identity->order : 0,//是否开会
];
}
/**
* Notes: 获取当前状态
*
* @Author: 玄尘
* @Date: 2022/8/3 16:38
*/
public function getNowStatus(): array
{
$status = $this->getStateData();
if (! $status['isSubscribe']) {
return [
'value' => 'isSubscribe',
'text' => '关注'
];
}
if (! $status['isCase']) {
return [
'value' => 'isCase',
'text' => '健康档案'
];
}
if (! $status['isVip']) {
return [
'value' => 'isVip',
'text' => '开通会员'
];
}
return [
'value' => 'upCase',
'text' => '上传档案'
];
}
/**
* Notes: 开通季卡的次数
*
* @Author: 玄尘
* @Date: 2022/8/21 10:38
*/
public function getOpenJkCount(): int
{
$jkIdentity = Identity::query()->Jk()->first();
$num = 0;
if ($jkIdentity) {
$num = UserOrder::query()
->byUser($this)
->where('identity_id', $jkIdentity->id)
->where('state', UserOrder::STATE_SUCCESS)
->count();
}
return $num;
}
/**
* Notes: description
*
* @Author: 玄尘
* @Date: 2022/9/28 13:08
*/
public function getCodeAttribute()
{
$userIdentity = $this->identityFirst();
if ($userIdentity && $userIdentity->id > 1) {
$invite = Hashids::connection('code')->encode($this->id);
} else {
$invite = '';
}
$url = Config::get('user.invite_code.url').'?invite='.$invite;
return 'data:image/png;base64,'.base64_encode(QrCode::format('png')
->size(100)
->margin(3)
->generate($url));
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Modules\User\Events\UserCertificationSuccess;
use Modules\User\Traits\BelongsToUser;
class UserCertification extends Model
{
use BelongsToUser;
protected $casts = [
'verified' => 'boolean',
];
protected static function boot()
{
parent::boot();
self::created(function ($certification) {
event(new UserCertificationSuccess($certification));
});
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use App\Traits\HasStatus;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Modules\User\Models\Traits\CertificationTrait;
class UserCertificationConfig extends Model
{
use HasStatus,
Cachable,
CertificationTrait;
const IDCARD = 1;
const PHONECARD = 2;
const TYPE = [
self::IDCARD => '姓名,身份证验证',
self::PHONECARD => '姓名,手机号,身份证号验证',
];
public static function loading()
{
$config = Cache::rememberForever('userCertification', function () {
$conf = self::shown()
->first();
Artisan::call('queue:restart');
return $conf ? $conf->toArray() : '';
});
config(['userCertification' => $config]);
}
protected static function boot()
{
parent::boot();
self::saved(function ($model) {
if ($model->status == 1) {
Cache::forget('userCertification');
}
});
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class UserInfo extends Model
{
protected $primaryKey = 'user_id';
/**
* Notes : 用户
*
* @Date : 2021/3/11 5:37 下午
* @Author : < Jason.C >
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Notes : 获取头像的实际地址
*
* @Date : 2021/3/12 10:53 上午
* @Author : < Jason.C >
* @param $avatar
* @return string
*/
public function getAvatarAttribute($avatar): string
{
if (empty($avatar)) {
$avatar = config('user.avatar');
}
if (Str::startsWith($avatar, 'http')) {
return $avatar;
}
return $avatar ? Storage::url($avatar) : '';
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Encore\Admin\Auth\Database\Administrator;
use Modules\User\Traits\BelongsToUser;
class UserLog extends Model
{
use BelongsToUser;
public function admin()
{
return $this->belongsTo(Administrator::class);
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Arr;
use Modules\Mall\Models\Order;
use Modules\User\Traits\BelongsToUser;
class UserStock extends Model
{
use BelongsToUser;
const STATUS_PAID = 1;
const STATUS_DELIVER = 2;
const STATUS_DELIVERED = 3;
const STATUS_SIGNED = 4;
const STOCK_ORDER_STATUS = [
self::STATUS_PAID => '待提货',
self::STATUS_DELIVER => '待发货',
self::STATUS_DELIVERED => '待签收',
self::STATUS_SIGNED => '已签收',
];
const STOCK_ORDER_STATUS_MAP = [
'待提货' => 'primary',
'待发货' => 'success',
'待签收' => 'danger',
'已签收' => 'info',
];
public $appends = ['residue'];
/**
* Notes: 获取剩余
*
* @Author: 玄尘
* @Date: 2022/7/29 14:01
* @return string
*/
public function getResidueAttribute()
{
return bcsub($this->stock, $this->hold);
}
/**
* Notes: 日志
*
* @Author: 玄尘
* @Date: 2022/8/2 14:43
* @return HasMany
*/
public function logs(): HasMany
{
return $this->hasMany(UserStockLog::class);
}
/**
* Notes: 获取体验官提货订单状态
*
* @Author: 玄尘
* @Date: 2022/9/1 16:40
*/
public function getStockOrderStatusAttribute()
{
$order = $this->user->orders()->first();
if (empty($order)) {
return 1;
} elseif ($order->state == Order::STATUS_PAID) {
return 2;
} elseif ($order->state == Order::STATUS_DELIVERED) {
return 3;
} elseif ($order->state == Order::STATUS_SIGNED) {
return 4;
}
}
/**
* Notes: 获取体验官订单名称
*
* @Author: 玄尘
* @Date: 2022/9/1 16:45
*/
public function getStockOrderStatusTextAttribute()
{
return Arr::get(self::STOCK_ORDER_STATUS, $this->stock_order_status, '未知');
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class UserStockLog extends Model
{
const TYPE_INIT = 'init';
const TYPE_IN = 'in';
const TYPE_OUT = 'out';
const TYPES = [
self::TYPE_INIT => '开通会员',
self::TYPE_IN => '续费',
self::TYPE_OUT => '提货',
];
public function identity()
{
return $this->belongsTo(Identity::class);
}
public function userStock(): BelongsTo
{
return $this->belongsTo(UserStock::class);
}
public function getTypeTextAttribute(): string
{
return self::TYPES[$this->type];
}
/**
* Notes: description
*
* @Author: 玄尘
* @Date: 2022/8/3 10:44
* @param Builder $query
* @param User $user
* @return Builder
*/
public function scopeByUser(Builder $query, User $user): Builder
{
return $query->whereHas('userStock', function ($q) use ($user) {
return $q->ByUser($user);
});
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
class UserSubscribe extends Model
{
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Arr;
use Modules\User\Models\Traits\WechatAttribute;
use Modules\User\Traits\BelongsToUser;
class UserWechat extends Model
{
use BelongsToUser,
WechatAttribute;
const SEX = [
'0' => '未知',
'1' => '男',
'2' => '女',
];
/**
* Notes : 小程序关联
*
* @Date : 2021/6/10 3:40 下午
* @Author : Mr.wang
* @return HasOne
*/
public function mini(): HasOne
{
return $this->hasOne(UserWechatMini::class);
}
/**
* Notes : 公众号关联
*
* @Date : 2021/6/10 3:40 下午
* @Author : Mr.wang
* @return HasOne
*/
public function official(): HasOne
{
return $this->hasOne(UserWechatOfficial::class);
}
/**
* Notes : app关联
*
* @Date : 2021/6/10 3:40 下午
* @Author : Mr.wang
* @return HasOne
*/
public function app(): HasOne
{
return $this->hasOne(UserWechatApp::class);
}
/**
* Notes : 获取性别信息
*
* @Date : 2021/12/1 14:35
* @Author : Mr.wang
* @return string
*/
public function getSexAttribute(): string
{
return self::SEX[$this->sex] ?? '未知';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class UserWechatApp extends Model
{
public function userWechat(): BelongsTo
{
return $this->belongsTo(UserWechat::class);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class UserWechatMini extends Model
{
public function userWechat(): BelongsTo
{
return $this->belongsTo(UserWechat::class);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class UserWechatOfficial extends Model
{
public $dates = [
'subscribe_at'
];
public function userWechat(): BelongsTo
{
return $this->belongsTo(UserWechat::class);
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Modules\User\Models;
use App\Models\Model;
class UserWechatSubscribe extends Model
{
}