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

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

View File

@@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user;
use app\services\BaseServices;
use app\services\wechat\WechatUserServices;
use think\facade\Log;
class CancelUserServices extends BaseServices
{
/**
* 注销用户账号
* @param int $uid 用户ID
* @return void
*/
public function cancelUser(int $uid)
{
try {
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
/** @var WechatUserServices $wechatUserServices */
$wechatUserServices = app()->make(WechatUserServices::class);
$userService->update(['spread_uid' => $uid], ['spread_uid' => 0]);// 清除用户与下级的关系
$userService->update($uid, ['spread_uid' => 0, 'integral' => 0, 'now_money' => 0]);//清除
$userService->update(['work_uid' => $uid], ['work_uid' => 0, 'work_userid' => '']);//清除企业微信上下级关系
$userService->destroy($uid);// 软删除用户
$wechatUserServices->update(['uid' => $uid], ['is_del' => 1, 'unionid' => '', 'openid' => time() . rand(1000, 9999)]);// 删除微信用户
// CancelUserJob::dispatch([$uid]);// 相关记录删除队列
} catch (\Throwable $e) {
Log::error('注销用户失败,失败原因:' . $e->getMessage());
}
}
}

View File

@@ -0,0 +1,465 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\jobs\notice\SmsJob;
use app\services\BaseServices;
use app\dao\user\UserDao;
use app\services\message\sms\SmsRecordServices;
use app\services\wechat\WechatUserServices;
use crmeb\exceptions\ApiException;
use crmeb\services\CacheService;
use Firebase\JWT\JWT;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Config;
/**
*
* Class LoginServices
* @package app\services\user
* @mixin UserDao
*/
class LoginServices extends BaseServices
{
/**
* @var UserDao
*/
#[Inject]
protected UserDao $dao;
/**
* H5账号登陆
* @param $account
* @param $password
* @param $spread_uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function login($account, $password, $spread_uid)
{
$user = $this->dao->getOne(['phone' => $account], 'uid,pwd,status');
if ($user) {
if ($user->pwd !== md5((string)$password))
throw new ValidateException('账号或密码错误');
if ($user->pwd === md5('123456'))
throw new ValidateException('请修改您的初始密码,再尝试登录!');
} else {
throw new ValidateException('账号或密码错误');
}
if (!$user['status'])
throw new ValidateException('已被禁止,请联系管理员');
$uid = (int)$user['uid'];
//更新用户信息
$token = $this->createToken($uid, 'api', $user->pwd);
if ($token) {
$this->updateUserInfo(['spread_uid' => $spread_uid], $user);
// 用户登录成功事件
event('user.login', [$uid, app('request')->ip()]);
return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
} else
throw new ValidateException('登录失败');
}
/**
* 更新用户信息
* @param $user
* @param $uid
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateUserInfo($user, $userInfo)
{
$data = [];
if (isset($userInfo['nickname']) && $userInfo['nickname']) {
$data['nickname'] = !isset($user['nickname']) || !$user['nickname'] ? $userInfo->nickname : $user['nickname'];
}
if (isset($userInfo['avatar']) && $userInfo['avatar']) {
$data['avatar'] = !isset($user['headimgurl']) || !$user['headimgurl'] ? $userInfo->avatar : $user['headimgurl'];
}
if (isset($user['phone']) && $user['phone'] && isset($userInfo['phone']) && !$userInfo['phone']) {
$data['phone'] = $user['phone'];
}
$data['last_time'] = time();
$data['last_ip'] = app()->request->ip();
//永久绑定
$store_brokergae_binding_status = sys_config('store_brokerage_binding_status', 1);
$spread_uid = isset($user['code']) && $user['code'] && $user['code'] != $userInfo->uid ? $user['code'] : ($user['spread_uid'] ?? 0);
if ($userInfo->spread_uid && $store_brokergae_binding_status == 1) {
$data['login_type'] = $user['login_type'] ?? $userInfo->login_type;
} else {
//绑定分销关系 = 所有用户
if (sys_config('brokerage_bindind', 1) == 1) {
//分销绑定类型为时间段且过期 ||临时
$store_brokerage_binding_time = sys_config('store_brokerage_binding_time', 30);
if (!$userInfo['spread_uid'] || $store_brokergae_binding_status == 3 || ($store_brokergae_binding_status == 2 && ($userInfo['spread_time'] + $store_brokerage_binding_time * 24 * 3600) < time())) {
$spreadUid = $spread_uid;
if ($spreadUid && $userInfo->uid == $this->dao->value(['uid' => $spreadUid], 'spread_uid')) {
$spreadUid = 0;
}
if($spreadUid == $userInfo->uid){
$spreadUid = 0;
}
if ($spreadUid && $this->dao->count(['uid' => (int)$spreadUid])) {
$data['spread_uid'] = $spreadUid;
$data['spread_time'] = time();
}
}
}
}
if (!$this->dao->update($userInfo['uid'], $data, 'uid')) {
throw new ValidateException('修改信息失败');
}
if (isset($data['spread_uid']) && $data['spread_uid']) {
event('user.register', [$this->dao->get((int)$userInfo['uid']), false, $spread_uid]);
//推送消息
// event('notice.notice', [['spreadUid' => $spreadUid, 'user_type' => $userInfo['user_type'], 'nickname' => $userInfo['nickname']], 'bind_spread_uid']);
}
return true;
}
/**
* 发送验证码
* @param $phone
* @param $type
* @param $time
* @param $ip
* @return int
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function verify($phone, $type, $time, $ip)
{
if ($this->dao->getOne(['account' => $phone]) && $type == 'register') {
throw new ValidateException('手机号已注册');
}
$default = Config::get('sms.default', 'yunxin');
$defaultMaxPhoneCount = Config::get('sms.maxPhoneCount', 10);
$defaultMaxIpCount = Config::get('sms.maxIpCount', 50);
$maxPhoneCount = Config::get('sms.stores.' . $default . '.maxPhoneCount', $defaultMaxPhoneCount);
$maxIpCount = Config::get('sms.stores.' . $default . '.maxIpCount', $defaultMaxIpCount);
/** @var SmsRecordServices $smsRecord */
$smsRecord = app()->make(SmsRecordServices::class);
if ($smsRecord->count(['phone' => $phone, 'add_ip' => $ip, 'time' => 'today']) >= $maxPhoneCount) {
throw new ValidateException('您今日发送得短信次数已经达到上限');
}
if ($smsRecord->count(['add_ip' => $ip, 'time' => 'today']) >= $maxIpCount) {
throw new ValidateException('此IP今日发送次数已经达到上限');
}
if (CacheService::get('code_' . $phone))
throw new ValidateException($time . '分钟内有效');
mt_srand();
$code = rand(100000, 999999);
$data['code'] = $code;
$data['time'] = $time;
$res = SmsJob::dispatch([$phone, $data, 'VERIFICATION_CODE_TIME']);
if (!$res)
throw new ValidateException('短信平台验证码发送失败');
return $code;
}
/**
* H5用户注册
* @param $account
* @param $password
* @param $spread_uid
* @return User|\think\Model
*/
public function register($account, $password, $spread_uid, $user_type = 'h5')
{
if ($this->dao->getOne(['phone' => $account], 'uid')) {
throw new ValidateException('用户已存在,请去修改密码');
}
$phone = $account;
$data['account'] = $account;
$data['pwd'] = md5((string)$password);
$data['phone'] = $phone;
if ($spread_uid && $this->dao->count(['uid' => (int)$spread_uid])) {
$data['spread_uid'] = $spread_uid;
$data['spread_time'] = time();
}
$data['real_name'] = '';
$data['birthday'] = '';
$data['card_id'] = '';
$data['mark'] = '';
$data['addres'] = '';
$data['user_type'] = $user_type;
$data['add_time'] = time();
$data['add_ip'] = app('request')->ip();
$data['last_time'] = time();
$data['last_ip'] = app('request')->ip();
$data['nickname'] = substr(md5($account . time()), 0, 12);
$data['avatar'] = $data['headimgurl'] = sys_config('h5_avatar');
$data['city'] = '';
$data['language'] = '';
$data['province'] = '';
$data['country'] = '';
$data['status'] = 1;
if (!$re = $this->dao->save($data)) {
throw new ValidateException('注册失败');
} else {
//用户注册成功事件
event('user.register', [$re->toArray(), true, $spread_uid]);
//推送消息
// event('notice.notice', [['spreadUid' => $spread, 'user_type' => $user_type, 'nickname' => $data['nickname']], 'bind_spread_uid']);
return $re;
}
}
/**
* 重置密码
* @param $account
* @param $password
*/
public function reset($account, $password)
{
$user = $this->dao->getOne(['account|phone' => $account], 'uid');
if (!$user) {
throw new ValidateException('用户不存在');
}
if (!$this->dao->update($user['uid'], ['pwd' => md5((string)$password)], 'uid')) {
throw new ValidateException('修改密码失败');
}
return true;
}
/**
* 手机号登录
* @param $phone
* @param $spread_uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function mobile($phone, $spread_uid, $user_type = 'h5')
{
//数据库查询
$user = $this->dao->getOne(['phone' => $phone]);
if (!$user) {
$user = $this->register($phone, '123456', $spread_uid, $user_type);
if (!$user) {
throw new ValidateException('用户登录失败,无法生成新用户,请稍后再试!');
}
}
if (!$user->status)
throw new ValidateException('已被禁止,请联系管理员');
// 设置推广关系
$this->updateUserInfo(['spread_uid' => $spread_uid], $user);
$uid = (int)$user['uid'];
$token = $this->createToken($uid, 'api', $user->pwd);
if ($token) {
// 用户登录成功事件
event('user.login', [$uid, app('request')->ip()]);
return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
} else {
throw new ValidateException('登录失败');
}
}
/**
* 切换登录
* @param $user
* @param $from
*/
public function switchAccount($user, $from)
{
if ($from === 'h5') {
$where = [['phone', '=', $user['phone']], ['user_type', '<>', 'h5']];
$login_type = 'wechat';
} else {
//数据库查询
$where = [['account|phone', '=', $user['phone']], ['user_type', '=', 'h5']];
$login_type = 'h5';
}
$switch_user = $this->dao->getOne($where);
if (!$switch_user) {
throw new ValidateException('用户不存在,无法切换');
}
if (!$switch_user->status) {
throw new ValidateException('已被禁止,请联系管理员');
}
$edit_data = ['login_type' => $login_type];
if (!$this->dao->update($switch_user['uid'], $edit_data, 'uid')) {
throw new ValidateException('修改新用户登录类型出错');
}
$uid = (int)$switch_user['uid'];
$token = $this->createToken($uid, 'api', $switch_user['pwd']);
if ($token) {
// 用户登录成功事件
event('user.login', [$uid, app('request')->ip()]);
return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
} else {
throw new ValidateException('切换失败');
}
}
/**
* 绑定手机号(静默还没写入用户信息)
* @param $user
* @param $phone
* @param $step
* @return mixed
*/
public function bindind_phone($phone, $key = '')
{
if (!$key) {
throw new ValidateException('请刷新页面或者重新授权');
}
[$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = CacheService::getTokenBucket($key);
if (!$createData) {
throw new ValidateException('请刷新页面或者重新授权');
}
$wechatInfo['phone'] = $phone;
/** @var WechatUserServices $wechatUser */
$wechatUser = app()->make(WechatUserServices::class);
//更新用户信息
$user = $wechatUser->wechatOauthAfter([$openid, $wechatInfo, $spread_uid, $login_type, $userType]);
$token = $this->createToken((int)$user['uid'], $userType, $user['pwd']);
if ($token) {
return [
'token' => $token['token'],
'userInfo' => $user,
'expires_time' => $token['params']['exp'],
];
} else
throw new ValidateException('获取用户访问token失败!');
}
/**
* 用户绑定手机号
* @param $user
* @param $phone
* @param $step
* @return mixed
*/
public function userBindindPhone(int $uid, $phone, $step)
{
$userInfo = $this->dao->get($uid);
if (!$userInfo) {
throw new ValidateException('用户不存在');
}
if ($this->dao->getOne([['phone', '=', $phone], ['user_type', '<>', 'h5']])) {
throw new ValidateException('此手机已经绑定,无法多次绑定!');
}
if ($userInfo->phone) {
throw new ValidateException('您的账号已经绑定过手机号码!');
}
$data = [];
if ($this->dao->getOne(['account' => $phone, 'phone' => $phone, 'user_type' => 'h5'])) {
if (!$step) return ['msg' => 'H5已有账号是否绑定此账号上', 'data' => ['is_bind' => 1]];
} else {
$data['account'] = $phone;
}
$data['phone'] = $phone;
if ($this->dao->update($userInfo['uid'], $data, 'uid') || $userInfo->phone == $phone)
return ['msg' => '绑定成功', 'data' => []];
else
throw new ValidateException('绑定失败');
}
/**
* 用户绑定手机号
* @param $user
* @param $phone
* @param $step
* @return mixed
*/
public function updateBindindPhone(int $uid, $phone)
{
$userInfo = $this->dao->get($uid);
if (!$userInfo) {
throw new ValidateException('用户不存在');
}
if ($userInfo->phone == $phone) {
throw new ValidateException('新手机号和原手机号相同,无需修改');
}
if ($this->dao->getOne([['phone', '=', $phone]])) {
throw new ValidateException('此手机已经注册');
}
$data = [];
$data['phone'] = $phone;
if ($this->dao->update($userInfo['uid'], $data, 'uid'))
return ['msg' => '修改成功', 'data' => []];
else
throw new ValidateException('修改失败');
}
/**
* 远程登录接口
* @param string $out_token
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2024/10/26 14:49
*/
public function remoteRegister(string $out_token = '')
{
$info = JWT::jsonDecode(JWT::urlsafeB64Decode($out_token));
$userInfo = $this->dao->get(['uid' => $info->uid]);
$data = [];
if (!$userInfo) {
$data['uid'] = $info->uid;
$data['account'] = $info->phone != '' ? $info->phone : 'out_' . $info->uid;
$data['phone'] = $info->phone;
$data['pwd'] = md5('123456');
$data['real_name'] = $info->nickname;
$data['birthday'] = '';
$data['card_id'] = '';
$data['mark'] = '';
$data['addres'] = '';
$data['user_type'] = 'h5';
$data['add_time'] = time();
$data['add_ip'] = app('request')->ip();
$data['last_time'] = time();
$data['last_ip'] = app('request')->ip();
$data['nickname'] = $info->nickname;
$data['avatar'] = $info->avatar;
$data['city'] = '';
$data['language'] = '';
$data['province'] = '';
$data['country'] = '';
$data['status'] = 1;
$data['now_money'] = $info->now_money;
$data['integral'] = $info->integral;
$data['exp'] = $info->exp;
$this->dao->save($data);
} else {
$data['nickname'] = $info->nickname;
$data['avatar'] = $info->avatar;
$data['now_money'] = $info->now_money;
$data['integral'] = $info->integral;
$data['exp'] = $info->exp;
$this->dao->update($info->uid, $data);
}
$token = $this->createToken((int)$info->uid, 'api');
if ($token) {
return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
} else {
throw new ApiException('登录失败');
}
}
}

View File

@@ -0,0 +1,303 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\validate\api\user\AddressValidate;
use app\services\BaseServices;
use app\dao\user\UserAddressDao;
use app\services\other\SystemCityServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Exception;
use think\exception\ValidateException;
use think\Model;
/**
*
* Class UserAddressServices
* @package app\services\user
* @mixin UserAddressDao
*/
class UserAddressServices extends BaseServices
{
/**
* @var UserAddressDao
*/
#[Inject]
protected UserAddressDao $dao;
/**
* 获取单个地址
* @param $id
* @param array $field
* @return array
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function getAddress($id, array $field = [])
{
return $this->dao->get($id, $field);
}
/**
* 获取用户缓存地址
* @param int $uid
* @param int $addressId
* @return bool|mixed|null
*/
public function getAdderssCache(int $id, int $expire = 60)
{
return $this->dao->cacheTag()->remember('user_adderss_cache_' . $id, function () use ($id) {
$res = $this->dao->getOne(['id' => $id, 'is_del' => 0]);
if ($res) {
return $res->toArray();
} else {
return [];
}
}, $expire);
}
/**
* 获取所有地址
* @param array $where
* @param string $field
* @return array
*/
public function getAddressList(array $where, string $field = '*'): array
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $field, $page, $limit);
$count = $this->getAddresCount($where);
return compact('list', 'count');
}
/**
* 获取某个用户的所有地址
* @param int $uid
* @param string $field
* @return array
*/
public function getUserAddressList(int $uid, string $field = '*'): array
{
[$page, $limit] = $this->getPageValue();
$where = ['uid' => $uid];
$where['is_del'] = 0;
return $this->dao->getList($where, $field, $page, $limit);
}
/**
* @param int $uid
* @param int $expire
* @return mixed
* @throws \throwable
*/
public function getUserDefaultAddressCache(int $uid, int $expire = 60)
{
return $this->dao->cacheTag()->remember('user_adderss_cache_user_' . $uid, function () use ($uid) {
$res = $this->dao->getOne(['uid' => $uid, 'is_default' => 1, 'is_del' => 0]);
if ($res) {
return $res->toArray();
} else {
return [];
}
}, $expire);
}
/**
* @param int $uid
* @param string $field
* @return array|Model|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function getUserDefaultAddress(int $uid, string $field = '*')
{
return $this->dao->getOne(['uid' => $uid, 'is_default' => 1, 'is_del' => 0], $field);
}
/**
* 获取条数
* @param array $where
* @return int
*/
public function getAddresCount(array $where): int
{
return $this->dao->count($where);
}
/**
* 添加地址
* @param array $data
* @return bool
*/
public function create(array $data)
{
if (!$this->dao->save($data))
throw new AdminException('写入失败');
return true;
}
/**
* 修改地址
* @param $id
* @param $data
* @return bool
*/
public function updateAddress(int $id, array $data)
{
if (!$this->dao->update($id, $data))
throw new AdminException('修改失败');
return true;
}
/**
* 设置默认定制
* @param int $uid
* @param int $id
* @return bool
*/
public function setDefault(int $uid, int $id)
{
if (!$this->dao->be($id)) {
throw new ValidateException('地址不存在');
}
if (!$this->dao->update($uid, ['is_default' => 0], 'uid'))
throw new Exception('取消原来默认地址');
if (!$this->dao->update($id, ['is_default' => 1]))
throw new Exception('设置默认地址失败');
return true;
}
/**
* 获取单个地址
* @param int $id
* @return mixed
*/
public function address(int $id)
{
$addressInfo = $this->getAdderssCache($id);
if (!$addressInfo || (isset($addressInfo['is_del']) && $addressInfo['is_del'] == 1)) {
throw new ValidateException('数据不存在');
}
return $addressInfo;
}
/**
* 添加|修改地址
* @param int $uid
* @param array $addressInfo
* @return mixed
*/
public function editAddress(int $uid, array $addressInfo)
{
if ($addressInfo['type'] == 1 && !$addressInfo['id']) {
$city = $addressInfo['address']['city'];
/** @var SystemCityServices $systemCity */
$systemCity = app()->make(SystemCityServices::class);
$cityInfo = $systemCity->getOne([['name', '=', $city], ['parent_id', '<>', 0]]);
if ($cityInfo && $cityInfo['city_id']) {
$addressInfo['address']['city_id'] = $cityInfo['city_id'];
} else {
$cityInfo = $systemCity->getOne([['name', 'like', "%$city%"], ['parent_id', '<>', 0]]);
if (!$cityInfo) {
throw new ValidateException('收货地址格式错误!修改后请重新导入!');
}
$addressInfo['address']['city_id'] = $cityInfo['city_id'];
}
}
if (!isset($addressInfo['address']['city_id']) || $addressInfo['address']['city_id'] == 0) throw new ValidateException('添加收货地址失败!');
$addressInfo['province'] = $addressInfo['address']['province'] ?? '';
$addressInfo['city'] = $addressInfo['address']['city'] ?? '';
$addressInfo['city_id'] = $addressInfo['address']['city_id'] ?? 0;
$addressInfo['district'] = $addressInfo['address']['district'] ?? '';
$addressInfo['street'] = $addressInfo['address']['street'] ?? '';
$addressInfo['is_default'] = (int)$addressInfo['is_default'] == true ? 1 : 0;
$addressInfo['uid'] = $uid;
unset($addressInfo['address'], $addressInfo['type']);
//数据验证
validate(AddressValidate::class)->check($addressInfo);
$address_check = [];
if ($addressInfo['id']) {
$address_check = $this->getAddress((int)$addressInfo['id']);
}
if ($address_check && $address_check['is_del'] == 0) {
if ($address_check['uid'] != $uid) {
throw new ValidateException('编辑收货地址失败');
}
$id = (int)$addressInfo['id'];
unset($addressInfo['id']);
if (!$this->dao->update($id, $addressInfo, 'id')) {
throw new ValidateException('编辑收货地址失败');
}
if ($addressInfo['is_default']) {
$this->setDefault($uid, $id);
}
$this->dao->cacheTag()->clear();
return ['type' => 'edit', 'msg' => '编辑地址成功', 'data' => []];
} else {
$addressInfo['add_time'] = time();
unset($addressInfo['id']);
if (!$address = $this->dao->save($addressInfo)) {
throw new ValidateException('添加收货地址失败');
}
if ($addressInfo['is_default']) {
$this->setDefault($uid, (int)$address->id);
}
$this->dao->cacheTag()->clear();
return ['type' => 'add', 'msg' => '添加地址成功', 'data' => ['id' => $address->id]];
}
}
/**
* 删除地址
* @param int $uid
* @param int $id
* @return bool
*/
public function delAddress(int $uid, int $id)
{
$addressInfo = $this->getAddress($id);
if (!$addressInfo || $addressInfo['is_del'] == 1 || $addressInfo['uid'] != $uid) {
throw new ValidateException('数据不存在');
}
if ($this->dao->update($id, ['is_del' => '1'], 'id')) {
$this->dao->cacheTag()->clear();
return true;
} else
throw new ValidateException('删除地址失败!');
}
/**
* 设置默认用户地址
* @param $id
* @param $uid
* @return bool
*/
public function setDefaultAddress(int $id, int $uid)
{
$res1 = $this->dao->update($uid, ['is_default' => 0], 'uid');
$res2 = $this->dao->update(['id' => $id, 'uid' => $uid], ['is_default' => 1]);
$res = $res1 !== false && $res2 !== false;
return $res;
}
}

View File

@@ -0,0 +1,114 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\dao\user\UserAuthDao;
use app\services\work\WorkClientServices;
use crmeb\exceptions\AuthException;
use crmeb\services\CacheService;
use crmeb\services\wechat\config\WorkConfig;
use crmeb\utils\JwtAuth;
use think\annotation\Inject;
/**
*
* Class UserAuthServices
* @package app\services\user
* @mixin UserAuthDao
*/
class UserAuthServices extends BaseServices
{
/**
* @var UserAuthDao
*/
#[Inject]
protected UserAuthDao $dao;
/**
* 获取授权信息
* @param $token
* @return array
* @throws \Psr\SimpleCache\InvalidArgumentException\
*/
public function parseToken($token): array
{
$md5Token = is_null($token) ? '' : md5($token);
if ($token === 'undefined') {
throw new AuthException('请登录', 410000);
}
if (!$token || !$tokenData = CacheService::getTokenBucket($md5Token))
throw new AuthException('请登录', 410000);
if (!is_array($tokenData) || empty($tokenData) || !isset($tokenData['uid'])) {
throw new AuthException('请登录', 410000);
}
/** @var JwtAuth $jwtAuth */
$jwtAuth = app()->make(JwtAuth::class);
//设置解析token
[$id, $type, $auth] = $jwtAuth->parseToken($token);
try {
$jwtAuth->verifyToken();
} catch (\Throwable $e) {
if (!request()->isCli()) CacheService::clearToken($md5Token);
throw new AuthException('登录已过期,请重新登录', 410001);
}
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$user = $userService->getUserCacheInfo($id);
if (!$user) throw new AuthException('用户不存在,请重新登陆', 410001);
if (!$user['status'])
throw new AuthException('您已被禁止登录,请联系管理员', 410002);
if (!$user || $user->uid != $tokenData['uid']) {
if (!request()->isCli()) CacheService::clearToken($md5Token);
throw new AuthException('登录状态有误,请重新登录', 410002);
}
//有密码在检测
if ($user['pwd'] != md5('123456') && $auth !== md5($user['pwd'])) {
throw new AuthException('登录已过期,请重新登录', 410001);
}
$tokenData['type'] = $type;
return compact('user', 'tokenData');
}
/**
* 获取企业客户
* @param string $userid
* @return array
*/
public function parseClient(string $userid)
{
/** @var WorkConfig $workConfig */
$workConfig = app()->make(WorkConfig::class);
$corpId = $workConfig->corpId;
if (!$corpId) {
throw new AuthException('请先配置企业微信');
}
/** @var WorkClientServices $service */
$service = app()->make(WorkClientServices::class);
$clientInfo = $service->get(['corp_id' => $corpId, 'external_userid' => $userid]);
if (!$clientInfo) {
throw new AuthException('客户信息不存在');
}
return $clientInfo->toArray();
}
}

View File

@@ -0,0 +1,168 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
//declare (strict_types=1);
namespace app\services\user;
use app\jobs\user\UserBatchJob;
use app\services\BaseServices;
use app\dao\user\UserDao;
use app\services\user\group\UserGroupServices;
use app\services\user\label\UserLabelServices;
use app\services\user\level\SystemUserLevelServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 用户批量操作
* Class UserBatchProcessServices
* @package app\services\user
* @mixin UserDao
*/
class UserBatchProcessServices extends BaseServices
{
/**
* 批量处理参数
* @var array[]
*/
protected array $data = [
'group_id' => [],//分组ID
'label_id' => [],//标签ids
'level_id' => 0,//等级ID
'money_status' => 0,//余额变动状态
'money' => 0,//余额变动金额
'integration_status' => 0,//积分变动状态
'integration' => 0,//积分变动数量
'days_status' => 0,//付费会员条数变动状态
'day' => 0,//变动天数
'spread_uid' => 0,//上级推广人uid
];
/**
* @var UserDao
*/
#[Inject]
protected UserDao $dao;
/**
* 根据搜索条件查询uids
* @param $where
* @return array
*/
public function getUidByWhere($where)
{
if ($where) {
if (is_string($where)) {
$where = json_decode($where, true);
} elseif (is_array($where)) {
$where = $where;
} else {
$where = [];
}
}
/** @var UserWechatuserServices $userWechatUser */
$userWechatUser = app()->make(UserWechatuserServices::class);
$fields = 'u.uid';
[$list, $count] = $userWechatUser->getWhereUserList($where, $fields);
$uids = array_unique(array_column($list, 'uid'));
return $uids;
}
/**
* 用户批量操作
* @param int $type
* @param array $uids
* @param array $input_data
* @param bool $is_all
* @param array $where
* @return bool
*/
public function batchProcess(int $type, array $uids, array $input_data, bool $is_all = false, array $where = [], $label_type = 0)
{
//全选
if ($is_all == 1) {
$uids = $this->getUidByWhere($where);
}
$data = [];
switch ($type) {
case 1://分组
$group_id = $input_data['group_id'] ?? 0;
if (!$group_id) throw new ValidateException('请选择分组');
/** @var UserGroupServices $userGroup */
$userGroup = app()->make(UserGroupServices::class);
if (!$userGroup->getGroup($group_id)) {
throw new ValidateException('该分组不存在');
}
$data['group_id'] = $group_id;
break;
case 2://标签
$label_id = $input_data['label_id'] ?? [];
if (!$label_id) throw new ValidateException ('请选择标签');
/** @var UserLabelServices $userLabelServices */
$userLabelServices = app()->make(UserLabelServices::class);
$count = $userLabelServices->getCount([['id', 'IN', $label_id]]);
if ($count != count($label_id)) {
throw new ValidateException('用户标签不存在或被删除');
}
$data['label_id'] = $label_id;
break;
case 3://等级
$level_id = $input_data['level_id'] ?? 0;
if (!$level_id) throw new ValidateException('请选择用户等级');
/** @var SystemUserLevelServices $systemLevelServices */
$systemLevelServices = app()->make(SystemUserLevelServices::class);
//查询当前选择的会员等级
$systemLevel = $systemLevelServices->getLevel($level_id);
if (!$systemLevel) throw new AdminException('您选择赠送的用户等级不存在!');
$data['level_id'] = $level_id;
break;
case 4://积分余额
$data['money_status'] = $input_data['money_status'] ?? 0;
$data['money'] = $input_data['money'] ?? 0;
$data['integration_status'] = $input_data['integration_status'] ?? 0;
$data['integration'] = $input_data['integration'] ?? 0;
if (!$data['money_status'] && !$data['integration_status']) {
throw new AdminException('请选择操作积分或余额');
}
if ($data['money_status'] && !$data['money']) {
throw new AdminException('请填写变动余额数量');
}
if ($data['integration_status'] && !$data['integration']) {
throw new AdminException('请填写变动积分数量');
}
break;
case 5://赠送会员
$data['days_status'] = $input_data['days_status'] ?? 0;
$data['day'] = $input_data['day'] ?? 0;
if (!$data['days_status']) {
throw new AdminException('请选择增加或者减少会员天数');
}
if (!$data['day']) {
throw new AdminException('请填写变动会员天数');
}
break;
case 6://上级推广人
$spread_uid = $input_data['spread_uid'] ?? 0;
if (!$spread_uid) throw new ValidateException('请选择上级推广人');
if (!$this->dao->count(['uid' => $spread_uid])) {
throw new ValidateException('上级用户不存在');
}
$data['spread_uid'] = $spread_uid;
break;
default:
throw new AdminException('暂不支持该类型批操作');
}
//加入批量队列
UserBatchJob::dispatchDo('userBatch', [$type, $uids, $data, $label_type]);
return true;
}
}

View File

@@ -0,0 +1,634 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\jobs\user\UserLevelJob;
use app\services\BaseServices;
use app\dao\user\UserBillDao;
use app\services\system\admin\SystemAdminServices;
use think\annotation\Inject;
use think\Exception;
use think\exception\ValidateException;
use crmeb\services\CacheService;
/**
*
* Class UserBillServices
* @package app\services\user
* @mixin UserBillDao
*/
class UserBillServices extends BaseServices
{
/**
* 用户记录模板
* @var array[]
*/
protected array $incomeData = [
'pay_give_integral' => [
'title' => '购买商品赠送积分',
'category' => 'integral',
'type' => 'gain',
'mark' => '购买商品赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'order_give_integral' => [
'title' => '下单赠送积分',
'category' => 'integral',
'type' => 'gain',
'mark' => '下单赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'order_promotions_give_integral' => [
'title' => '下单优惠活动赠送积分',
'category' => 'integral',
'type' => 'gain',
'mark' => '下单优惠活动赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'order_give_exp' => [
'title' => '下单赠送经验',
'category' => 'exp',
'type' => 'gain',
'mark' => '下单赠送{%num%}经验',
'status' => 1,
'pm' => 1
],
'integral_refund' => [
'title' => '赠送积分回退',
'category' => 'integral',
'type' => 'deduction',
'mark' => '下单赠送积分,回退{%num%}积分',
'status' => 1,
'pm' => 0
],
'order_integral_refund' => [
'title' => '下单支付积分回退',
'category' => 'integral',
'type' => 'integral_refund',
'mark' => '下单支付积分,回退{%num%}积分',
'status' => 1,
'pm' => 1
],
'pay_product_integral_back' => [
'title' => '下单抵扣积分回退',
'category' => 'integral',
'type' => 'pay_product_integral_back',
'mark' => '下单抵扣积分,返还{%num%}积分',
'status' => 1,
'pm' => 1
],
'deduction' => [
'title' => '积分抵扣',
'category' => 'integral',
'type' => 'deduction',
'mark' => '购买商品使用{%number%}积分抵扣{%deductionPrice%}元',
'status' => 1,
'pm' => 0
],
'lottery_use_integral' => [
'title' => '参与抽奖使用积分',
'category' => 'integral',
'type' => 'lottery_use',
'mark' => '参与抽奖使用{%num%}积分',
'status' => 1,
'pm' => 0
],
'lottery_give_integral' => [
'title' => '抽奖中奖赠送积分',
'category' => 'integral',
'type' => 'lottery_add',
'mark' => '抽奖中奖赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'storeIntegral_use_integral' => [
'title' => '积分兑换商品',
'category' => 'integral',
'type' => 'storeIntegral_use',
'mark' => '积分商城兑换商品使用{%num%}积分',
'status' => 1,
'pm' => 0
],
'system_clear_integral' => [
'title' => '到期自动清除积分',
'category' => 'integral',
'type' => 'system_clear',
'mark' => '到期自动清除{%num%}积分',
'status' => 1,
'pm' => 0
],
'newcomer_give_integral' => [
'title' => '新人礼赠送积分',
'category' => 'integral',
'type' => 'newcomer_add',
'mark' => '新人礼赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'level_give_integral' => [
'title' => '会员卡激活赠送积分',
'category' => 'integral',
'type' => 'level_add',
'mark' => '会员卡激活赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'system_add_integral' => [
'title' => '系统增加积分',
'category' => 'integral',
'type' => 'system_add',
'mark' => '系统增加了{%num%}积分',
'status' => 1,
'pm' => 1
],
'system_sub_integral' => [
'title' => '系统减少积分',
'category' => 'integral',
'type' => 'system_sub',
'mark' => '系统扣除了{%num%}积分',
'status' => 1,
'pm' => 0
],
'community_add_integral' => [
'title' => '发布种草内容奖励积分',
'category' => 'integral',
'type' => 'community_add_integral',
'mark' => '发布种草内容赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'community_add_exp' => [
'title' => '发布种草内容奖励经验',
'category' => 'exp',
'type' => 'community_add_exp',
'mark' => '发布种草内容赠送{%num%}经验',
'status' => 1,
'pm' => 1
],
'reply_add_integral' => [
'title' => '评价赠送积分',
'category' => 'integral',
'type' => 'reply_add_integral',
'mark' => '评价商品赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
'reply_add_exp' => [
'title' => '评价赠送经验',
'category' => 'exp',
'type' => 'reply_add_exp',
'mark' => '评价商品赠送{%num%}经验',
'status' => 1,
'pm' => 1
],
'holiday_gift_integral' => [
'title' => '节日有礼赠送积分',
'category' => 'integral',
'type' => 'holiday_gift_integral',
'mark' => '节日有礼赠送{%num%}积分',
'status' => 1,
'pm' => 1
],
];
/**
* @var UserBillDao
*/
#[Inject]
protected UserBillDao $dao;
/**
* 获取用户记录总和
* @param int $uid
* @param string $category
* @param array $type
* @param string $time
* @param bool $pm
* @return mixed
*/
public function getRecordCount(int $uid, string $category = 'now_money', array $type = [], string $time = '', bool $pm = false)
{
$where = [];
$where['uid'] = $uid;
$where['category'] = $category;
$where['status'] = 1;
if (is_string($type) && strlen(trim($type))) {
$where['type'] = explode(',', $type);
}
if ($time) {
$where['time'] = $time;
}
$where['pm'] = $pm ? 1 : 0;
return $this->dao->getBillSumColumn($where);
}
/**
* 获取积分列表
* @param int $uid
* @param array $where_time
* @param string $field
* @return array
*/
public function getIntegralList(int $uid = 0, $where_time = [], string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$where = ['category' => 'integral'];
if ($uid) $where['uid'] = $uid;
if ($where_time) $where['add_time'] = $where_time;
$list = $this->dao->getList($where, $field, $page, $limit);
$count = $this->dao->count($where);
$times = [];
if ($list) {
foreach ($list as &$item) {
$addTime = strtotime($item['add_time']);
$item['time_key'] = $item['time'] = $addTime ? date('Y-m', (int)$addTime) : '';
$item['day'] = $addTime ? date('Y-m-d', (int)$addTime) : '';
$item['number'] = intval($item['number']);
$item['balance'] = intval($item['balance']);
if (in_array($item['type'], ['system_add', 'system_sub'])) {
$item['admin_name'] = app()->make(SystemAdminServices::class)->value(['id' => $item['link_id']], 'real_name');
} else {
$item['admin_name'] = '';
}
$item['is_frozen'] = $item['frozen_time'] > time() ? 1 : 0;
$item['frozen_time'] = $item['frozen_time'] ? date('Y-m-d H:i:s', (int)$item['frozen_time']) : '';
}
$times = array_merge(array_unique(array_column($list, 'time_key')));
}
return compact('list', 'count', 'times');
}
/**
* 获取签到列表
* @param int $uid
* @param array $where_time
* @param string $field
* @return array
*/
public function getSignList(int $uid = 0, $where_time = [], string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$where = ['category' => 'integral', 'type' => 'sign'];
if ($uid) $where['uid'] = $uid;
if ($where_time) $where['add_time'] = $where_time;
$list = $this->dao->getList($where, $field, $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 经验总数
* @param int $uid
* @param array $where_time
* @return float
*/
public function getExpSum(int $uid = 0, $where_time = [])
{
$where = ['category' => ['exp'], 'pm' => 1, 'status' => 1];
if ($uid) $where['uid'] = $uid;
if ($where_time) $where['time'] = $where_time;
return $this->dao->getBillSum($where);
}
/**
* 获取所有经验列表
* @param int $uid
* @param array $where_time
* @param string $field
* @return array
*/
public function getExpList(int $uid = 0, $where_time = [], string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$where = ['category' => ['exp']];
$where['status'] = 1;
if ($uid) $where['uid'] = $uid;
if ($where_time) $where['time'] = $where_time;
$list = $this->dao->getList($where, $field, $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 增加积分
* @param int $uid
* @param string $type
* @param array $data
* @return bool
* @throws Exception
*/
public function incomeIntegral(int $uid, string $type, array $data)
{
$data['uid'] = $uid;
$data['category'] = 'integral';
$data['type'] = $type;
$data['pm'] = 1;
$data['status'] = 1;
$data['add_time'] = time();
if (!$this->dao->save($data))
throw new Exception('增加记录失败');
return true;
}
/**
* 写入用户记录
* @param string $type 写入类型
* @param int $uid
* @param int|array|string $number
* @param int|string $balance
* @param int $link_id
* @return bool|mixed
*/
public function income(string $type, int $uid, $number, $balance, $link_id = 0, $obtain_time = 0, $mark = '')
{
$data = $this->incomeData[$type] ?? null;
if (!$data) {
return true;
}
$data['uid'] = $uid;
$data['balance'] = $balance ?? 0;
$data['link_id'] = $link_id;
if (is_array($number)) {
$key = array_keys($number);
$key = array_map(function ($item) {
return '{%' . $item . '%}';
}, $key);
$value = array_values($number);
$data['number'] = $number['number'] ?? 0;
$data['mark'] = str_replace($key, $value, $data['mark']);
} else {
$data['number'] = $number;
$data['mark'] = str_replace(['{%num%}'], (string)$number, $data['mark']);
}
if ($mark) $data['mark'] = $mark;
$data['add_time'] = time();
$data['obtain_time'] = $obtain_time;
if ($type == 'pay_give_integral' || $type == 'order_give_integral') {
$integral_frozen = sys_config('integral_frozen');
if ($integral_frozen) {
$data['frozen_time'] = $data['add_time'] + ($integral_frozen * 86400);
}
}
if ((float)$data['number']) {
return $this->dao->save($data);
}
return true;
}
/**
* 邀请新用户增加经验
* @param int $spreadUid
*/
public function inviteUserIncExp(int $spreadUid)
{
if (!$spreadUid) {
return false;
}
//用户等级是否开启
if (!sys_config('member_func_status', 1)) {
return false;
}
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$spread_user = $userService->getUserInfo($spreadUid);
if (!$spread_user) {
return false;
}
$exp_num = sys_config('invite_user_exp', 0);
if ($exp_num) {
$userService->incField($spreadUid, 'exp', (int)$exp_num);
$balance = bcadd((string)$spread_user['exp'], (string)$exp_num);
$data = [];
$data['uid'] = $spreadUid;
$data['number'] = $exp_num;
$data['category'] = 'exp';
$data['type'] = 'invite_user';
$data['title'] = $data['mark'] = '邀新奖励';
$data['balance'] = $balance;
$data['pm'] = 1;
$data['status'] = 1;
$this->dao->save($data);
}
//检测会员升级
UserLevelJob::dispatch([$spreadUid]);
return true;
}
/**
* 获取type
* @param array $where
* @param string $filed
*/
public function getBillType(array $where)
{
return $this->dao->getType($where);
}
/**
* 资金类型
*/
public function bill_type()
{
$where = [];
$where['not_type'] = ['gain', 'system_sub', 'deduction', 'sign'];
$where['not_category'] = ['exp', 'integral'];
return CacheService::get('user_type_list', function () use ($where) {
return ['list' => $this->dao->getType($where)];
}, 600);
}
/**
* 记录分享次数
* @param int $uid 用户uid
* @param int $cd 冷却时间
* @return Boolean
* */
public function setUserShare(int $uid, $cd = 300)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo($uid);
if (!$user) {
throw new ValidateException('用户不存在!');
}
$cachename = 'Share_' . $uid;
if (CacheService::get($cachename)) {
return false;
}
$data = ['title' => '用户分享记录', 'uid' => $uid, 'category' => 'share', 'type' => 'share', 'number' => 0, 'link_id' => 0, 'balance' => 0, 'mark' => date('Y-m-d H:i:s', time()) . ':用户分享'];
if (!$this->dao->save($data)) {
throw new ValidateException('记录分享记录失败');
}
CacheService::set($cachename, 1, $cd);
return true;
}
/**
* 获取积分列表
* @param array $where
* @param string $field
* @param int $limit
* @return array
*/
public function getPointList(array $where, string $field = '*', int $limit = 0)
{
$where_data = [];
$where_data['category'] = 'integral';
if (isset($where['uid']) && $where['uid'] != '') {
$where_data['uid'] = $where['uid'];
}
if ($where['start_time'] != '' && $where['end_time'] != '') {
$where_data['time'] = $where['start_time'] . ' - ' . $where['end_time'];
}
if (isset($where['type']) && $where['type'] != '') {
$where_data['type'] = $where['type'];
}
if (isset($where['nickname']) && $where['nickname'] != '') {
$where_data['like'] = $where['nickname'];
}
if (isset($where['excel']) && $where['excel'] != '') {
$where_data['excel'] = $where['excel'];
} else {
$where_data['excel'] = 0;
}
if ($limit) {
[$page] = $this->getPageValue();
} else {
[$page, $limit] = $this->getPageValue();
}
$list = $this->dao->getBillList($where_data, $field, $page, $limit);
foreach ($list as &$item) {
$item['nickname'] = $item['user']['nickname'] ?? '';
$item['number'] = intval($item['number']);
$item['balance'] = intval($item['balance']);
unset($item['user']);
}
$count = $this->dao->count($where_data);
return compact('list', 'count');
}
/**
* 积分头部信息
* @param array $where
* @return array[]
*/
public function getUserPointBadgelist(array $where)
{
$data = [];
$where_data = [];
$where_data['category'] = 'integral';
if ($where['start_time'] != '' && $where['end_time'] != '') {
$where_data['time'] = $where['start_time'] . ' - ' . $where['end_time'];
}
if (isset($where['nickname']) && $where['nickname'] != '') {
$where_data['like'] = $where['nickname'];
}
$data['SumIntegral'] = intval($this->dao->getBillSumColumn($where_data + ['pm' => 1, 'not_type' => [
'integral_refund',
'order_integral_refund',
'pay_product_integral_back',
]]));
$where_data['type'] = 'sign';
$data['CountSign'] = $this->dao->getUserSignPoint($where_data);
$data['SumSign'] = intval($this->dao->getBillSumColumn($where_data));
unset($where_data['type']);
$data['SumDeductionIntegral'] = intval($this->dao->getBillSumColumn($where_data + ['pm' => 0]));
return [
[
'col' => 6,
'count' => $data['SumIntegral'],
'name' => '总积分(个)',
],
[
'col' => 6,
'count' => $data['CountSign'],
'name' => '客户签到次数(次)',
],
[
'col' => 6,
'count' => $data['SumSign'],
'name' => '签到送出积分(个)',
],
[
'col' => 6,
'count' => $data['SumDeductionIntegral'],
'name' => '使用积分(个)',
],
];
}
/**
* @param $uid
* @param $type
* @return array
*/
public function getUserBillList(int $uid, int $type)
{
$where = [];
$where['uid'] = $uid;
$where['category'] = 'now_money';
switch ((int)$type) {
case 0:
$where['type'] = ['recharge', 'pay_money', 'system_add', 'pay_product_refund', 'system_sub', 'pay_member', 'offline_scan', 'lottery_use', 'lottery_add'];
break;
case 1:
$where['type'] = ['pay_money', 'pay_member', 'offline_scan', 'user_recharge_refund', 'lottery_use'];
break;
case 2:
$where['type'] = ['recharge', 'system_add', 'lottery_add'];
break;
case 3:
$where['type'] = ['brokerage', 'brokerage_user'];
break;
case 4:
$where['type'] = ['extract'];
break;
}
$field = 'FROM_UNIXTIME(add_time,"%Y-%m") as time,group_concat(id SEPARATOR ",") ids';
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getUserBillListByGroup($where, $field, 'time', $page, $limit);
$data = [];
if ($list) {
$listIds = array_column($list, 'ids');
$ids = [];
foreach ($listIds as $id) {
$ids = array_merge($ids, explode(',', $id));
}
$info = $this->dao->getColumn([['id', 'in', $ids]], 'FROM_UNIXTIME(add_time,"%Y-%m-%d %H:%i") as add_time,title,number,pm', 'id');
foreach ($list as $item) {
$value['time'] = $item['time'];
$id = explode(',', $item['ids']);
array_multisort($id, SORT_DESC);
$value['list'] = [];
foreach ($id as $v) {
if (isset($info[$v])) {
$value['list'][] = $info[$v];
}
}
$data[] = $value;
}
}
return $data;
}
}

View File

@@ -0,0 +1,56 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user;
use app\dao\user\UserBrokerageFrozenDao;
use app\services\BaseServices;
use think\annotation\Inject;
/**
* 佣金冻结
* Class UserBrokerageFrozenServices
* @package app\services\user
* @mixin UserBrokerageFrozenDao
*/
class UserBrokerageFrozenServices extends BaseServices
{
/**
* @var UserBrokerageFrozenDao
*/
#[Inject]
protected UserBrokerageFrozenDao $dao;
/**
* 保存冻结金额
* @param int $uid
* @param string $price
* @param int $uillId
* @param string $orderId
* @return mixed
*/
public function saveBrokage(int $uid, string $price, int $uillId, string $orderId)
{
$broken_time = intval(sys_config('extract_time'));
$data = [
'uid' => $uid,
'price' => $price,
'uill_id' => $uillId,
'order_id' => $orderId,
'add_time' => time(),
'status' => 1,
'frozen_time' => time() + $broken_time * 86400
];
return $this->dao->save($data);
}
}

View File

@@ -0,0 +1,893 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\dao\user\UserBrokerageDao;
use app\services\BaseServices;
use app\services\message\service\StoreServiceServices;
use app\services\order\StoreOrderCartInfoServices;
use app\services\order\StoreOrderServices;
use think\annotation\Inject;
use think\exception\ValidateException;
use crmeb\services\CacheService;
/**
* 用户佣金
* Class UserBrokerageServices
* @package app\services\user
* @mixin UserBrokerageDao
*/
class UserBrokerageServices extends BaseServices
{
public array $isBrokerage = [
'self_brokerage',//自购佣金
'one_brokerage',//一级佣金
'two_brokerage',//二级佣金
'brokerage_user',//推广用户佣金
'staff_brokerage',//员工佣金
'agent_brokerage',//代理佣金
'division_brokerage',//团队佣金
];
/**
* 用户记录模板
* @var array[]
*/
protected array $incomeData = [
'get_self_brokerage' => [
'title' => '获得自购订单佣金',
'type' => 'self_brokerage',
'mark' => '您成功消费{%pay_price%}元,奖励自购佣金{%number%}',
'status' => 1,
'pm' => 1
],
'get_brokerage' => [
'title' => '获得下级推广订单佣金',
'type' => 'one_brokerage',
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
'status' => 1,
'pm' => 1
],
'get_two_brokerage' => [
'title' => '获得推广订单佣金',
'type' => 'two_brokerage',
'mark' => '二级推广人{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
'status' => 1,
'pm' => 1
],
'get_user_brokerage' => [
'title' => '获得推广用户佣金',
'type' => 'brokerage_user',
'mark' => '成功推广用户:{%nickname%},奖励推广佣金{%number%}',
'status' => 1,
'pm' => 1
],
'extract' => [
'title' => '佣金提现',
'type' => 'extract',
'mark' => '{%mark%},佣金提现{%number%}元',
'status' => 1,
'pm' => 0
],
'extract_fail' => [
'title' => '提现失败',
'type' => 'extract_fail',
'mark' => '提现失败,退回佣金{%num%}元',
'status' => 1,
'pm' => 1
],
'brokerage_to_nowMoney' => [
'title' => '佣金提现到余额',
'type' => 'extract_money',
'mark' => '佣金提现到余额{%num%}元',
'status' => 1,
'pm' => 0
],
'brokerage_refund' => [
'title' => '退款退佣金',
'type' => 'refund',
'mark' => '订单退款扣除佣金{%num%}元',
'status' => 1,
'pm' => 0
],
'get_staff_brokerage' => [
'title' => '获得员工推广订单佣金',
'type' => 'staff_brokerage',
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
'status' => 1,
'pm' => 1
],
'get_agent_brokerage' => [
'title' => '获得代理推广订单佣金',
'type' => 'agent_brokerage',
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
'status' => 1,
'pm' => 1
],
'get_division_brokerage' => [
'title' => '获得区域代理推广订单佣金',
'type' => 'division_brokerage',
'mark' => '{%nickname%}成功消费{%pay_price%}元,奖励推广佣金{%number%}',
'status' => 1,
'pm' => 1
],
];
/**
* @var UserBrokerageDao
*/
#[Inject]
protected UserBrokerageDao $dao;
/**
* 获取用户记录总和
* @param int $uid
* @param string $time
* @param bool $pm
* @return mixed
*/
public function getRecordCount(int $uid, $time = '', $pm = false)
{
$where = [];
$where['uid'] = $uid;
$where['status'] = 1;
if ($time) {
$where['time'] = $time;
}
if ($pm) {
$where['pm'] = 0;
}
return $this->dao->getBrokerageSumColumn($where);
}
/**
* 计算佣金
* @param array $where
* @param int $time
* @return mixed
*/
public function getUsersBokerageSum(array $where, $time = 0)
{
$where_data = [
'status' => 1,
'pm' => $where['pm'] ?? '',
'uid' => $where['uid'] ?? '',
'time' => $where['time'] ?? 0,
'type' => $where['type'] ?? '',
'not_type' => $where['not_type'] ?? ''
];
if ($time) $where_data['time'] = $time;
return $this->dao->getBrokerageSumColumn($where_data);
}
/**
* 某个用户佣金总和
* @param int $uid
* @param array|string[] $type
* @param string $time
* @return float
*/
public function getUserBillBrokerageSum(int $uid, array $type = ['self_brokerage', 'one_brokerage', 'two_brokerage', 'brokerage_user', 'staff_brokerage', 'agent_brokerage', 'division_brokerage'], $time = '')
{
$where = ['uid' => $uid];
if ($type) $where['type'] = $type;
if ($time) $where['time'] = $time;
return $this->dao->getBrokerageSum($where);
}
/**
* 获取用户|所有佣金总数
* @param int $uid
* @param array $where_time
* @return float
*/
public function getBrokerageCount(int $uid = 0, $where_time = [])
{
$where = ['category' => 'now_money', 'type' => ['system_add', 'pay_product', 'extract', 'pay_product_refund', 'system_sub'], 'pm' => 1, 'status' => 1];
if ($uid) $where['uid'] = $uid;
if ($where_time) $where['add_time'] = $where_time;
return $this->dao->getBillCount($where);
}
/**
* 写入用户记录
* @param string $type 写入类型
* @param int $uid
* @param int|string|array $number
* @param int|string $balance
* @param int $link_id
* @return bool|mixed
*/
public function income(string $type, int $uid, $number, $balance, $link_id)
{
$data = $this->incomeData[$type] ?? null;
if (!$data) {
return true;
}
$data['uid'] = $uid;
$data['balance'] = $balance ?? 0;
$data['link_id'] = $link_id;
//佣金记录表存放下单用户 uid 和金额
if (in_array($data['type'], $this->isBrokerage)) {
if ($data['type'] !== 'brokerage_user') {
$orderInfo = app()->make(StoreOrderServices::class)->get($link_id, ['uid', 'pay_price']);
$data['order_uid'] = $orderInfo['uid'] ?? 0;
$data['order_price'] = $orderInfo['pay_price'] ?? 0;
} else {
$data['order_uid'] = $link_id;
}
}
if (is_array($number)) {
$key = array_keys($number);
$key = array_map(function ($item) {
return '{%' . $item . '%}';
}, $key);
$value = array_values($number);
$data['number'] = $number['number'] ?? 0;
$data['frozen_time'] = $number['frozen_time'] ?? 0;
$data['mark'] = str_replace($key, $value, $data['mark']);
} else {
$data['number'] = $number;
$data['mark'] = str_replace(['{%num%}'], (string)$number, $data['mark']);
}
$data['add_time'] = time();
if ((float)$data['number']) {
return $this->dao->save($data);
}
return true;
}
/**
* 资金类型
*/
public function bill_type()
{
return CacheService::get('user_brokerage_type_list', function () {
return ['list' => $this->dao->getBrokerageType([])];
}, 600);
}
/**
* 获取资金列表
* @param array $where
* @param string $field
* @param int $limit
* @return array
*/
public function getBrokerageList(array $where, string $field = '*', int $limit = 0)
{
$where_data = [];
if (isset($where['uid']) && $where['uid'] != '') {
$where_data['uid'] = $where['uid'];
}
if ($where['start_time'] != '' && $where['end_time'] != '') {
$where_data['time'] = str_replace('-', '/', $where['start_time']) . ' - ' . str_replace('-', '/', $where['end_time']);
}
if (isset($where['type']) && $where['type'] != '') {
$where_data['type'] = $where['type'];
}
if (isset($where['nickname']) && $where['nickname'] != '') {
$where_data['like'] = $where['nickname'];
}
if ($limit) {
[$page] = $this->getPageValue();
} else {
[$page, $limit] = $this->getPageValue();
}
$data = $this->dao->getBrokerageList($where_data, $field, $page, $limit);
foreach ($data as &$item) {
$item['nickname'] = $item['user']['nickname'] ?? '';
$item['_add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
unset($item['user']);
}
$count = $this->dao->count($where_data);
return compact('data', 'count');
}
/**
* 获取佣金列表
* @param array $where
* @param int $limit
* @return array
*/
public function getCommissionList(array $where, int $limit = 0)
{
$where_data = [];
$where_data['time'] = $where['time'];
if (isset($where['nickname']) && $where['nickname']) {
$where_data[] = ['u.account|u.nickname|u.uid|u.phone', 'LIKE', "%$where[nickname]%"];
}
if (isset($where['price_max']) && isset($where['price_min'])) {
if ($where['price_max'] != '' && $where['price_min'] != '') {
$where_data[] = ['u.brokerage_price', 'between', [$where['price_min'], $where['price_max']]];
} elseif ($where['price_min'] != '' && $where['price_max'] == '') {
$where_data[] = ['u.brokerage_price', '>=', $where['price_min']];
} elseif ($where['price_min'] == '' && $where['price_max'] != '') {
$where_data[] = ['u.brokerage_price', '<=', $where['price_max']];
}
}
$order_string = '';
$order_arr = ['asc', 'desc'];
if (isset($where['sum_number']) && in_array($where['sum_number'], $order_arr)) {
$order_string .= ',income ' . $where['sum_number'];
}
if (isset($where['brokerage_price']) && in_array($where['brokerage_price'], $order_arr)) {
$order_string .= ',u.brokerage_price ' . $where['brokerage_price'];
}
if ($order_string) {
$order_string = trim($order_string, ',');
}
/** @var UserUserBrokerageServices $userUserBrokerage */
$userUserBrokerage = app()->make(UserUserBrokerageServices::class);
[$count, $list] = $userUserBrokerage->getBrokerageList($where_data, 'b.type,b.pm,sum(IF(b.pm = 1, b.number, 0)) as income,sum(IF(b.pm = 0, b.number, 0)) as pay,u.nickname,u.phone,u.uid,u.now_money,u.brokerage_price,u.delete_time,b.add_time as time', $order_string, $limit);
$uids = array_unique(array_column($list, 'uid'));
/** @var UserExtractServices $userExtract */
$userExtract = app()->make(UserExtractServices::class);
$extractSumList = $userExtract->getUsersSumList($uids);
foreach ($list as &$item) {
// $item['sum_number'] = $item['income'] > $item['pay'] ? bcsub($item['income'], $item['pay'], 2) : 0;
$item['nickname'] = $item['nickname'] . "|" . ($item['phone'] ? $item['phone'] . "|" : '') . $item['uid'];
$item['extract_price'] = $extractSumList[$item['uid']] ?? 0;
$item['sum_number'] = bcadd((string)$item['extract_price'], (string)$item['brokerage_price'], 2);
$item['time'] = $item['time'] ? date('Y-m-d H:i:s', $item['time']) : '';
}
return compact('count', 'list');
}
/**
* 用户佣金详情
* @param int $uid
* @return array
*/
public function user_info(int $uid)
{
/** @var UserServices $user */
$user = app()->make(UserServices::class);
$user_info = $user->getUserWithTrashedInfo($uid, 'nickname,spread_uid,now_money,brokerage_price,add_time');
if (!$user_info) {
throw new ValidateException('您查看的用户信息不存在!');
}
$user_info = $user_info->toArray();
$income = $this->getUserBillBrokerageSum($uid);
$expend = $this->getUserBillBrokerageSum($uid, ['refund']);
$number = (float)bcsub((string)$income, (string)$expend, 2);
$user_info['number'] = max($number, 0);
$user_info['add_time'] = date('Y-m-d H:i:s', $user_info['add_time']);
$user_info['spread_name'] = $user_info['spread_uid'] ? $user->getUserInfo((int)$user_info['spread_uid'], 'nickname', true)['nickname'] ?? '' : '';
return compact('user_info');
}
/**
* 退佣金
* @param $order
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function orderRefundBrokerageBack($order)
{
$id = (int)$order['id'];
$where = [
'uid' => [$order['spread_uid'], $order['spread_two_uid'], $order['division_staff_id'], $order['division_agent_id'], $order['division_id']],
'type' => ['self_brokerage', 'one_brokerage', 'two_brokerage', 'staff_brokerage', 'agent_brokerage', 'division_brokerage'],
'link_id' => $id,
'pm' => 1
];
$brokerageList = $this->dao->getUserBrokerageList($where);
//子订单
if (!$brokerageList && $order['pid']) {
$where['link_id'] = $order['pid'];
$p_brokerageList = $this->dao->getUserBrokerageList($where);
//主订单已分佣 子订单按订单拆分后计算结果回退
if ($p_brokerageList) {
$brokerageList = [
['uid' => $order['spread_uid'], 'number' => $order['one_brokerage']],
['uid' => $order['spread_two_uid'], 'number' => $order['two_brokerage']],
];
}
}
$res = true;
if ($brokerageList) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$brokerages = $userServices->getColumn([['uid', 'in', array_column($brokerageList, 'uid')]], 'brokerage_price', 'uid');
$brokerageData = [];
foreach ($brokerageList as $item) {
if (!$item['uid'] || $item['uid'] <= 0) continue;
$usermoney = $brokerages[$item['uid']] ?? 0;
if ($item['number'] > $usermoney) {
$item['number'] = $usermoney;
}
$res = $res && $userServices->bcDec($item['uid'], 'brokerage_price', (string)$item['number'], 'uid', 2, false);
$brokerageData[] = [
'title' => '退款退佣金',
'uid' => $item['uid'],
'pm' => 0,
'add_time' => time(),
'type' => 'refund',
'number' => $item['number'],
'link_id' => $id,
'balance' => bcsub((string)$usermoney, (string)$item['number'], 2),
'mark' => '订单退款扣除佣金' . floatval($item['number']) . '元'
];
}
if ($brokerageData) {
$res = $res && $this->dao->saveAll($brokerageData);
}
//修改佣金冻结时间
// $this->dao->update($where, ['frozen_time' => 0]);
}
return $res;
}
/**
* 佣金排行
* @param string $time
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function brokerageRankList(string $time = 'week')
{
$where = [];
if ($time) {
$where['time'] = $time;
}
[$page, $limit] = $this->getPageValue();
$list = $this->dao->brokerageRankList($where, $page, $limit);
foreach ($list as $key => &$item) {
if (!isset($item['user']) || !$item['user'] || $item['brokerage_price'] <= 0) {
unset($list[$key]);
continue;
}
$item['nickname'] = $item['user']['nickname'] ?? '';
$item['avatar'] = $item['user']['avatar'] ?? '';
if ($item['brokerage_price'] == '0.00' || $item['brokerage_price'] == 0 || !$item['brokerage_price']) {
unset($list[$key]);
}
unset($item['user']);
}
return array_merge($list);
}
/**
* 获取用户排名
* @param int $uid
* @param string $time
*/
public function getUserBrokerageRank(int $uid, string $time = 'week')
{
$where = [];
if ($time) {
$where['time'] = $time;
}
$list = $this->dao->brokerageRankList($where);
foreach ($list as $key => &$item) {
if (!isset($item['user']) || !$item['user'] || $item['brokerage_price'] <= 0) {
unset($list[$key]);
}
}
$position_tmp_one = array_column($list, 'uid');
$position_tmp_two = array_column($list, 'brokerage_price', 'uid');
$brokerage_price = $position_tmp_two[$uid] ?? 0;
if (!in_array($uid, $position_tmp_one)) {
$position = 0;
} else {
if ($position_tmp_two[$uid] == 0.00) {
$position = 0;
} else {
$position = array_search($uid, $position_tmp_one) + 1;
}
}
return compact('position', 'brokerage_price');
}
/**
* 推广数据 昨天的佣金 累计提现金额 当前佣金
* @param int $uid
* @return mixed
*/
public function commission(int $uid)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if (!$userServices->userExist($uid)) {
throw new ValidateException('数据不存在');
}
/** @var UserExtractServices $userExtract */
$userExtract = app()->make(UserExtractServices::class);
$data = [];
$data['uid'] = $uid;
$data['pm'] = 1;
$data['commissionSum'] = $this->getUsersBokerageSum($data);
$data['pm'] = 0;
$data['commissionRefund'] = $this->getUsersBokerageSum($data);
$data['commissionCount'] = $data['commissionSum'] > $data['commissionRefund'] ? bcsub((string)$data['commissionSum'], (string)$data['commissionRefund'], 2) : 0.00;
$data['lastDayCount'] = $this->getUsersBokerageSum($data, 'yesterday');//昨天的佣金
$data['extractCount'] = $userExtract->getUserExtract($uid);//累计提现金额
return $data;
}
/**
* 前端佣金排行页面数据
* @param int $uid
* @param $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function brokerage_rank(int $uid, $type)
{
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
if (!$userService->userExist($uid)) {
throw new ValidateException('数据不存在');
}
$rankList = $this->getUserBrokerageRank($uid, $type);
return [
'rank' => $this->brokerageRankList($type),
'position' => $rankList['position'],
'brokerage_price' => $rankList['brokerage_price'],
];
}
/**
* 推广 佣金/提现 总和
* @param int $uid
* @param $type 3 佣金 4 提现
* @return mixed
*/
public function spread_count(int $uid, $type)
{
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
if (!$userService->userExist($uid)) {
throw new ValidateException('数据不存在');
}
$count = 0;
if ($type == 3) {
$where = [
'uid' => $uid,
'status' => 1,
'pm' => 1
];
$count1 = $this->dao->getBrokerageSumColumn($where);
$where['pm'] = 0;
$count2 = $this->dao->getBrokerageSumColumn($where);
$count = $count1 - $count2;
} else if ($type == 4) {
/** @var UserExtractServices $userExtract */
$userExtract = app()->make(UserExtractServices::class);
$count = $userExtract->getUserExtract($uid);//累计提现
}
return $count ? $count : 0;
}
/**
* 推广订单
* @param Request $request
* @return mixed
*/
public function spreadOrderList(int $uid, array $data, int $page = 0, int $limit = 0)
{
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$where = ['pid' => 0, 'paid' => 1, 'refund_status' => [0, 3], 'is_del' => 0, 'is_system_del' => 0, 'division_spread' => $uid];
if ($data['start'] || $data['stop']) {
$where['time'] = [$data['start'], $data['stop']];
}
$where['real_name'] = $data['keyword'] ?? '';
$count = $storeOrderServices->count($where);
$list = $storeOrderServices->getList($where, ['id,order_id,uid,add_time,spread_uid,status,spread_two_uid,one_brokerage,two_brokerage,pay_price,cart_id,division_id,division_brokerage,division_agent_id,division_agent_brokerage,division_staff_id,division_staff_brokerage'], $page, $limit, ['brokerage' => function ($query) use ($uid) {
$query->where('uid', $uid);
}]);
return compact('list', 'count');
}
/**
* 用户贡献
* @param int $uid
* @param $order
* @return array
* @throws \ReflectionException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2024/11/23 下午2:18
*/
public function contribute(int $uid, $order = 'order_count')
{
$userService = app()->make(UserServices::class);
if (!in_array($order, ['order_price', 'order_count', 'brokerage'])) {
throw new ValidateException('参数错误');
}
[$page, $limit] = $this->getPageValue();
$order = 'total_' . $order . ' desc';
$list = $this->dao->search(['uid' => $uid])->whereIn('type', $this->isBrokerage)
->group('order_uid')->field("sum(order_price) as total_order_price,sum(number) as total_brokerage,count(*) as total_order_count,order_uid")
->order($order)->page($page, $limit)->select()->toArray();
foreach ($list as &$item) {
$userInfo = $userService->getUserCacheInfo($item['order_uid']);
$item['nickname'] = $userInfo['nickname'] ?? '';
$item['avatar'] = $userInfo['avatar'] ?? '';
}
return $list;
}
/**
* 数据总览
* @param int $uid
* @param array $data
* @return array[]
* User: liusl
* DateTime: 2024/11/23 下午12:12
*/
public function overview(int $uid, array $data)
{
$where = [];
if ($data['start'] || $data['stop']) {
$where['time'] = [$data['start'], $data['stop']];
}
//推广用户
$user_count = app()->make(UserServices::class)->count($where + ['spread_uid' => $uid]);
//提现次数
$extract_count = app()->make(UserExtractServices::class)->count($where + ['uid' => $uid, 'status' => 1]);
//提现金额
$extract_price = app()->make(UserExtractServices::class)->sum($where + ['uid' => $uid, 'status' => 1], 'extract_price', true);
$_data = $this->spreadOrderList($uid, $data);
$list = $_data['list'];
$brokerage = $order_price = '0.00';
$order_count = count($list);
foreach ($list as $item) {
$brokerageMap = [
$item['spread_uid'] => $item['one_brokerage'],
$item['spread_two_uid'] => $item['two_brokerage'],
$item['division_id'] => $item['division_brokerage'],
$item['division_agent_id'] => $item['division_agent_brokerage'],
$item['division_staff_id'] => $item['division_staff_brokerage']
];
$_brokerage = $brokerageMap[$uid] ?? '';
$order_price = bcadd((string)$order_price, (string)$item['pay_price'], 2);
$brokerage = bcadd((string)$brokerage, (string)$_brokerage, 2);
}
return [
[
'name' => '推广用户数',
'value' => $user_count
],
[
'name' => '提现次数',
'value' => $extract_count
],
[
'name' => '提现金额',
'value' => $extract_price
],
[
'name' => '推广佣金',
'value' => $brokerage
],
[
'name' => '订单金额',
'value' => $order_price
],
[
'name' => '订单数',
'value' => $order_count
]
];
}
/**
* 推广订单收入
* @param int $uid
* @param array $data
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2024/11/22 上午10:40
*/
public function orderIncome(int $uid, array $data)
{
$_data = $this->spreadOrderList($uid, $data);
$list = $_data['list'];
$order_count = $brokerage = $no_brokerage = $no_order_count = 0;
foreach ($list as $item) {
$brokerageMap = [
$item['spread_uid'] => $item['one_brokerage'],
$item['spread_two_uid'] => $item['two_brokerage'],
$item['division_id'] => $item['division_brokerage'],
$item['division_agent_id'] => $item['division_agent_brokerage'],
$item['division_staff_id'] => $item['division_staff_brokerage']
];
$_brokerage = $brokerageMap[$uid] ?? '';
if ($item['status'] < 2) {
$no_order_count++;
$no_brokerage = bcadd((string)$no_brokerage, (string)$_brokerage, 2);
} else {
$brokerage = bcadd((string)$brokerage, (string)$_brokerage, 2);
$order_count++;
}
}
return ['order_count' => $order_count, 'no_order_count' => $no_order_count, 'brokerage' => $brokerage, 'no_brokerage' => $no_brokerage];
}
/**
* 推广订单
* @param Request $request
* @return mixed
*/
public function spread_order(int $uid, array $data)
{
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
if (!$userService->userExist($uid)) {
throw new ValidateException('数据不存在');
}
$result = ['list' => [], 'time' => [], 'count' => 0];
[$page, $limit] = $this->getPageValue();
$_data = $this->spreadOrderList($uid, $data, $page, $limit);
$list = $_data['list'];
$count = $_data['count'];
if ($list) {
/** @var StoreOrderCartInfoServices $cartInfoServices */
$cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
$uids = array_unique(array_column($list, 'uid'));
$userInfos = $userService->getColumn([['uid', 'in', $uids]], 'uid,avatar,nickname', 'uid');
foreach ($list as &$item) {
$item['store_name'] = $cartInfoServices->getCarIdByProductTitle((int)$item['id']);
$item['avatar'] = $userInfos[$item['uid']]['avatar'] ?? '';
$item['nickname'] = $userInfos[$item['uid']]['nickname'] ?? '';
$item['number'] = $item['spread_uid'] == $uid ? $item['one_brokerage'] : $item['two_brokerage'];
if ($item['division_id'] == $uid) $item['number'] = $item['division_brokerage'];
if ($item['division_agent_id'] == $uid) $item['number'] = $item['division_agent_brokerage'];
if ($item['division_staff_id'] == $uid) $item['number'] = $item['division_staff_brokerage'];
$item['time_key'] = $item['add_time'] ? date('Y-m', $item['add_time']) : '';
$item['type'] = in_array($item['status'], [2, 3]) ? 'brokerage' : 'number';
$brokerage = $item['brokerage'];
$time = $brokerage['add_time'] ?? $item['add_time'];
$item['time'] = $time ? date('Y-m-d H:i', $time) : '';
$item['is_frozen'] = $brokerage && $brokerage['frozen_time'] > time() ? 1 : 0;
unset($item['brokerage']);
}
}
$times = array_unique(array_column($list, 'time_key'));
$time_data = [];
$i = 0;
$where = ['pid' => 0, 'type' => 0, 'paid' => 1, 'refund_status' => [0, 3], 'is_del' => 0, 'is_system_del' => 0, 'division_spread' => $uid];
if ($data['start'] || $data['stop']) {
$where['time'] = [$data['start'], $data['stop']];
}
$where['real_name'] = $data['keyword'] ?? '';
foreach ($times as $time) {
$time_data[$i]['time'] = $time;
$time_data[$i]['count'] = $storeOrderServices->getMonthCount($where, $time);
$time_data[$i]['sumPrice'] = $storeOrderServices->getMonthMoneyCount($where, $time, 'pay_price');
$i++;
}
$result['list'] = $list;
$result['time'] = $time_data;
$result['count'] = $count;
$priceWhere = ['pid' => 0, 'type' => 0, 'paid' => 1, 'refund_status' => [0, 3], 'is_del' => 0, 'is_system_del' => 0];
if ($data['start'] || $data['stop']) {
$priceWhere['time'] = [$data['start'], $data['stop']];
}
//条件获取一级佣金总和
$sum_price_one = $storeOrderServices->sum($priceWhere + ['spread_uid' => $uid], 'one_brokerage', true);
$sum_price_two = $storeOrderServices->sum($priceWhere + ['spread_two_uid' => $uid], 'two_brokerage', true);
$sum_price_division = $storeOrderServices->sum($priceWhere + ['division_id' => $uid], 'division_brokerage', true);
$sum_price_agent = $storeOrderServices->sum($priceWhere + ['division_agent_id' => $uid], 'division_agent_brokerage', true);
$sum_price_staff = $storeOrderServices->sum($priceWhere + ['division_staff_id' => $uid], 'division_staff_brokerage', true);
$result['sum_brokerage'] = bcadd(bcadd(bcadd(bcadd((string)$sum_price_one, (string)$sum_price_two, 2), (string)$sum_price_division, 2), (string)$sum_price_agent, 2), (string)$sum_price_staff, 2);
return $result;
}
/**
* 用户佣金记录v2
* @param int $uid
* @return array
*/
public function userBrokerageList(int $uid, $data = [])
{
$where = [];
$where['uid'] = $uid;
if ($data['start'] || $data['stop']) {
$where['time'] = [$data['start'], $data['stop']];
}
$where['like'] = $data['keyword'];
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, '*', $page, $limit);
$times = [];
if ($list) {
$extracts = [];
$extractList = function ($types) {
return in_array($types['type'], ['extract', 'extract_fail']);
};
$extractIds = array_column(array_filter($list, $extractList), 'link_id', 'id');
/** @var UserExtractServices $userExtractServices */
$userExtractServices = app()->make(UserExtractServices::class);
if ($extractIds) {
$extracts = $userExtractServices->search(['id' => $extractIds])->column('status,fail_msg,order_id,wechat_state,extract_type', 'id');
}
foreach ($list as &$item) {
if ($item['type'] == 'extract_money') {
$item['extract_status'] = 1;
} else {
$item['extract_status'] = $extracts[$item['link_id']]['status'] ?? 0;
}
$item['extract_msg'] = $extracts[$item['link_id']]['fail_msg'] ?? '';
$wechat_state = $extracts[$item['link_id']]['wechat_state'] ?? '';
$extract_type = $extracts[$item['link_id']]['extract_type'] ?? '';
$item['wechat_state'] = 0;
if ($extract_type == 'weixin' && $wechat_state == 'WAIT_USER_CONFIRM' && sys_config('pay_wechat_type') == 1) {
$item['wechat_state'] = 1;
}
$item['extract_order_id'] = $extracts[$item['link_id']]['order_id'] ?? '';
$item['time_key'] = $item['add_time'] ? date('Y-m', (int)$item['add_time']) : '';
$item['add_time'] = $item['add_time'] ? date('Y/m/d H:i', (int)$item['add_time']) : '';
$item['is_frozen'] = $item['frozen_time'] > time() ? 1 : 0;
$item['frozen_time'] = $item['frozen_time'] ? date('Y/m/d H:i', (int)$item['frozen_time']) : '';
}
$times = array_merge(array_unique(array_column($list, 'time_key')));
}
$income = $this->dao->sum($where + ['pm' => 1, 'not_type' => ['extract_fail']], 'number', true);
$expend = $this->dao->sum($where + ['pm' => 0], 'number', true);
return ['list' => $list, 'time' => $times, 'income' => $income, 'expend' => $expend];
}
/**
* 用户提现记录v2
* @param int $uid
* @param array $data
* @return array
*/
public function userExtractList(int $uid, array $data = [])
{
$where = [];
$where['uid'] = $uid;
$where['type'] = ['extract', 'extract_money', 'extract_fail'];
if ((isset($data['start']) && $data['start']) || (isset($data['stop']) && $data['stop'])) {
$where['time'] = [$data['start'], $data['stop']];
}
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, '*', $page, $limit);
$times = [];
if ($list) {
foreach ($list as &$item) {
$item['time_key'] = $item['add_time'] ? date('Y-m', (int)$item['add_time']) : '';
$item['add_time'] = $item['add_time'] ? date('Y/m/d H:i', (int)$item['add_time']) : '';
}
$times = array_merge(array_unique(array_column($list, 'time_key')));
}
return ['list' => $list, 'time' => $times];
}
}

View File

@@ -0,0 +1,223 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\dao\user\UserCardDao;
use app\services\store\SystemStoreStaffServices;
use app\services\wechat\WechatCardServices;
use app\services\wechat\WechatUserServices;
use crmeb\services\wechat\OfficialAccount;
use think\annotation\Inject;
/**
* 用户领取卡券
* Class UserCardServices
* @package app\services\user
* @mixin UserCardDao
*/
class UserCardServices extends BaseServices
{
/**
* 激活会员卡微信字段对应eb_user字段
* @var string[]
*/
protected array $userField = [
'USER_FORM_INFO_FLAG_MOBILE' => 'phone',
'USER_FORM_INFO_FLAG_BIRTHDAY' => 'birthday'
];
/**
* @var UserCardDao
*/
#[Inject]
protected UserCardDao $dao;
/**
* 用户领取微信会员卡事件
* @param $message
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userGetCard($message)
{
if (isset($message['CardId'])) {
$card_id = $message['CardId'];
$openid = $message['FromUserName'];
/** @var WechatCardServices $wechatCardServices */
$wechatCardServices = app()->make(WechatCardServices::class);
$card = $wechatCardServices->getOne(['card_id' => $card_id]);
if ($card) {
$staffInfo = [];
$uid = (int)$message['OuterId'];
if ($uid) {
try {
/** @var SystemStoreStaffServices $staffServices */
$staffServices = app()->make(SystemStoreStaffServices::class);
$staffInfo = $staffServices->getStaffInfoByUid($uid);
} catch (\Throwable $e) {
$staffInfo = [];
}
}
$data = [
'openid' => $openid,
'card_id' => $card_id,
'code' => $message['UserCardCode'],
'wechat_card_id' => $card['id'],
'staff_id' => $staffInfo['id'] ?? 0,
'store_id' => $staffInfo['store_id'] ?? 0,
'spread_uid' => $uid,
'add_time' => time()
];
$userCard = $this->dao->getOne(['openid' => $openid, 'card_id' => $card_id, 'is_del' => 0]);
if ($userCard) {
$this->dao->update($userCard['id'], $data);
} else {
$this->dao->save($data);
}
}
}
return true;
}
/**
* 激活会员事件
* @param $message
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userSubmitCard($message)
{
if (isset($message['CardId'])) {
$card_id = $message['CardId'];
$openid = $message['FromUserName'];
/** @var WechatCardServices $wechatCardServices */
$wechatCardServices = app()->make(WechatCardServices::class);
$card = $wechatCardServices->getOne(['card_id' => $card_id]);
if ($card) {
$userCard = $this->dao->getOne(['openid' => $openid, 'card_id' => $card_id, 'is_del' => 0]);
if ($userCard && !$userCard['is_submit']) {
$this->transaction(function () use ($openid, $userCard, $card_id) {
$data = $this->getWechatCardInfo($card_id, $userCard['code']);
/** @var WechatUserServices $wechatUserSerives */
$wechatUserSerives = app()->make(WechatUserServices::class);
$userInfo = $wechatUserSerives->saveUser($openid, $userCard['spread_uid'], $data['phone']);
$this->dao->update($userCard['id'], ['uid' => $userInfo['uid'] ?? 0, 'is_submit' => 1, 'submit_time' => time()]);
});
}
}
}
return true;
}
/**
* 获取用户激活会员卡填写信息
* @param string $card_id
* @param string $code
* @return array
*/
public function getWechatCardInfo(string $card_id, string $code)
{
//获取用户激活填写字段信息
$cart = OfficialAccount::getMemberCardUser($card_id, $code);
$formList = $cart['user_info']['common_field_list'] ?? [];
$userField = $this->userField;
$fields = array_keys($userField);
$data = [];
foreach ($formList as $item) {
if (in_array($item['name'], $fields)) {
$data[$userField[$item['name']]] = $item['value'];
}
}
return $data;
}
/**
* 用户删除微信会员卡事件
* @param $message
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userDelCard($message)
{
if (isset($message['CardId'])) {
$card_id = $message['CardId'];
$openid = $message['FromUserName'];
/** @var WechatCardServices $wechatCardServices */
$wechatCardServices = app()->make(WechatCardServices::class);
$card = $wechatCardServices->getOne(['card_id' => $card_id]);
if ($card) {
$userCard = $this->dao->getOne(['openid' => $openid, 'card_id' => $card_id, 'is_del' => 0]);
if ($userCard) {
$this->dao->update($userCard['id'], ['is_del' => 1, 'del_time' => time()]);
}
}
}
return true;
}
/**
* 门店推广统计详情列表
* @param int $store_id
* @param int $staff_id
* @param array $time
* @return array|array[]
*/
public function time(int $store_id, int $staff_id, array $time = [])
{
if (!$time) {
return [[], []];
}
[$start, $stop, $front, $front_stop] = $time;
$where = ['store_id' => $store_id, 'is_submit' => 1];
if ($staff_id) {
$where['staff_id'] = $staff_id;
}
$frontPrice = $this->dao->count($where + ['time' => [$front, $front_stop]]);
$nowPrice = $this->dao->count($where + ['time' => [$start, $stop]]);
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where + ['time' => [$start, $stop]], '*', ['user'], $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
}
return [[$nowPrice, $frontPrice], $list];
}
/**
* 获取会员卡列表
* @param int $uid
* @param UserServices $userServices
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getCardList(array $where, string $field = '*', array $with = ['user'])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $field, $with, $page, $limit);
$count = $this->dao->count($where);
foreach ($list as &$item) {
$item['submit_time'] = $item['submit_time'] ? date('Y-m-d H:i:s', $item['submit_time']) : '';
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
}
return compact('list', 'count');
}
}

View File

@@ -0,0 +1,158 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\dao\user\UserCardDao;
use app\dao\user\UserExtendDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\facade\Route as Url;
/**
* 用户扩展数据服务
* Class UserExtendServices
* @package app\services\user
* @mixin UserExtendDao
*/
class UserExtendServices extends BaseServices
{
/**
* @var UserExtendDao
*/
#[Inject]
protected UserExtendDao $dao;
/**
* 获取用户扩展数据列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where = []): array
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, '*', $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 获取用户扩展数据
* @param int $uid
* @param string $fieldName
* @return string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserExtendValue(int $uid, string $fieldName): string
{
$data = $this->dao->getUserFieldData($uid, $fieldName);
return $data ? $data['field_value'] : '';
}
/**
* 获取用户所有扩展数据
* @param int $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserAllExtendData(int $uid): array
{
return $this->dao->getUserExtendDataKeyValue($uid);
}
/**
* 设置用户扩展数据
* @param int $uid
* @param string $fieldName
* @param string $fieldValue
* @param string $remark
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setUserExtendData(int $uid, string $fieldName, string $fieldValue, string $remark = ''): bool
{
if (!$uid || !$fieldName) {
throw new AdminException('参数错误');
}
return $this->dao->setUserExtendData($uid, $fieldName, $fieldValue, $remark) !== false;
}
/**
* 批量设置用户扩展数据
* @param int $uid
* @param array $extendData
* @return bool
*/
public function setBatchUserExtendData(int $uid, array $extendData): bool
{
if (!$uid || empty($extendData)) {
throw new AdminException('参数错误');
}
return $this->dao->setBatchUserExtendData($uid, $extendData);
}
/**
* 删除用户扩展数据
* @param int $uid
* @param string $fieldName
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function deleteUserExtendData(int $uid, string $fieldName = ''): bool
{
if (!$uid) {
throw new AdminException('参数错误');
}
return $this->dao->deleteUserExtendData($uid, $fieldName);
}
/**
* 保存扩展数据
* @param array $data
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function save(array $data): bool
{
if (!isset($data['uid']) || !isset($data['field_name']) || !isset($data['field_value'])) {
throw new AdminException('参数错误');
}
return $this->dao->setUserExtendData(
(int)$data['uid'],
$data['field_name'],
$data['field_value'],
) !== false;
}
}

View File

@@ -0,0 +1,617 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\jobs\system\CapitalFlowJob;
use app\services\BaseServices;
use app\dao\user\UserExtractDao;
use app\services\order\StoreOrderCreateServices;
use app\services\wechat\WechatUserServices;
use crmeb\exceptions\AdminException;
use crmeb\exceptions\ApiException;
use crmeb\services\AliPayService;
use crmeb\services\wechat\Payment;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
/**
*
* Class UserExtractServices
* @package app\services\user
* @mixin UserExtractDao
*/
class UserExtractServices extends BaseServices
{
public array $wechatState = ['ACCEPTED' => '转账已受理', 'PROCESSING' => '余额不足', 'WAIT_USER_CONFIRM' => '待收款用户确认', 'TRANSFERING' => '转账中', 'SUCCESS' => '转账成功', 'FAIL' => '转账失败', 'CANCELING' => '商户撤销请求受理成功', 'CANCELLED' => '转账撤销完成'];
/**
* @var UserExtractDao
*/
#[Inject]
protected UserExtractDao $dao;
/**
* 获取一条提现记录
* @param int $id
* @param array $field
* @return array|\think\Model|null
*/
public function getExtract(int $id, array $field = [])
{
return $this->dao->get($id, $field);
}
/**
* 获取某个用户提现总数
* @param int $uid
* @return float
*/
public function getUserExtract(int $uid)
{
return $this->dao->getWhereSum(['uid' => $uid, 'status' => [0, 1]]);
}
/**
* 获取某些用户的提现总数列表
* @param array $uids
*/
public function getUsersSumList(array $uids)
{
return $this->dao->getWhereSumList(['uid' => $uids, 'status' => [0, 1]]);
}
/**
* 获取提现记录总数
* @param array $where 查询条件
* @return int
*/
public function getCount(array $where = [])
{
return $this->dao->getCount($where);
}
/**
* 获取提现列表
* @param array $where
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserExtractList(array $where, string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getExtractList($where, $field, $page, $limit);
foreach ($list as &$item) {
$item['nickname'] = $item['user']['nickname'] ?? '';
$item['wechat_state_text'] = $this->wechatState[$item['wechat_state']] ?? '';
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 获取提现总数
* @param array $where
*/
public function getExtractSum(array $where)
{
//提现金额 + 手续费
$price = $this->dao->getExtractMoneyByWhere($where, 'extract_price');
$fee = $this->dao->getExtractMoneyByWhere($where, 'extract_fee');
return (float)bcadd((string)$price, (string)$fee, 2);
}
/**
* 拒绝提现申请
* @param $id
* @param $fail_msg
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function changeFail(int $id, $userExtract, $message)
{
$fail_time = time();
$extract_number = bcadd((string)$userExtract['extract_price'], (string)$userExtract['extract_fee'], 2);
$mark = '提现失败,退回佣金' . $extract_number . '元';
$uid = $userExtract['uid'];
$status = -1;
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo($uid);
if (!$user) {
throw new ValidateException('用户不存在');
}
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$this->transaction(function () use ($user, $userBrokerageServices, $uid, $id, $extract_number, $message, $userServices, $status, $fail_time) {
$now_brokerage = bcadd((string)$user['brokerage_price'], (string)$extract_number, 2);
//增加佣金记录
$userBrokerageServices->income('extract_fail', $uid, $extract_number, $now_brokerage, $id);
//修改用户佣金
if (!$userServices->update($uid, ['brokerage_price' => $now_brokerage], 'uid'))
throw new AdminException('增加用户佣金失败');
if (!$this->dao->update($id, ['fail_time' => $fail_time, 'fail_msg' => $message, 'status' => $status])) {
throw new AdminException('修改失败');
}
});
//消息推送
event('notice.notice', [['uid' => $uid, 'userType' => strtolower($user['user_type']), 'extract_number' => $extract_number, 'nickname' => $user['nickname'], 'message' => $message], 'user_balance_change']);
return true;
}
/**
* 通过提现申请
* @param $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function changeSuccess(int $id, $userExtract)
{
$extractNumber = $userExtract['extract_price'];
$order_id = $this->transaction(function () use ($id, $userExtract, $extractNumber) {
if (!$this->dao->update($id, ['status' => 1])) {
throw new AdminException('修改失败');
}
switch ($userExtract['extract_type']) {
case 'bank':
$order_id = $userExtract['bank_code'];
break;
case 'weixin':
$order_id = $userExtract['wechat'];
$this->processWechatExtract($userExtract, $extractNumber);
break;
case 'alipay':
$order_id = $userExtract['alipay_code'];
$this->processAlipayExtract($userExtract, $extractNumber);
break;
case 'balance'://用户余额账号
/** @var UserRechargeServices $userRechargeServices */
$userRechargeServices = app()->make(UserRechargeServices::class);
$userRechargeServices->importNowMoney((int)$userExtract['uid'], $extractNumber, 'extract');
$order_id = $userExtract['id'];
break;
default:
$order_id = '';
break;
}
return $order_id;
});
if ($userExtract['extract_type'] == 'weixin' && sys_config('pay_wechat_type') == 1) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userType = $userServices->value(['uid' => $userExtract['uid']], 'user_type');
$nickname = $userServices->value(['uid' => $userExtract['uid']], 'nickname');
$phone = $userServices->value(['uid' => $userExtract['uid']], 'phone');
//记录资金流水队列
CapitalFlowJob::dispatch([['order_id' => $order_id, 'store_id' => 0, 'uid' => $userExtract['uid'], 'nickname' => $nickname, 'phone' => $phone, 'price' => $extractNumber, 'pay_type' => $userExtract['extract_type']], 'extract']);
//消息推送
event('notice.notice', [['uid' => $userExtract['uid'], 'userType' => strtolower($userType), 'extractNumber' => $extractNumber, 'nickname' => $nickname], 'user_extract']);
return true;
}
/**
* 处理微信提现
* @param array $userExtract 提现记录
* @param float $extractNumber 提现金额
* @return bool
*/
protected function processWechatExtract($userExtract, $extractNumber)
{
if (sys_config('brokerage_type', 0)) {
/** @var StoreOrderCreateServices $services */
$services = app()->make(StoreOrderCreateServices::class);
$wechat_order_id = $userExtract['order_id'] ?: $services->getNewOrderId();
$transferDetailList = [
[
'info_type' => '活动名称',
'info_content' => '分销员'
],
[
'info_type' => '奖励说明',
'info_content' => '佣金提现'
],
];
$res = Payment::merchantPay($userExtract['channel_type'], $userExtract['uid'], $wechat_order_id, (string)$extractNumber, '提现佣金到零钱', $userExtract['user_name'] ?? '用户', $transferDetailList);
if (!$res) {
throw new ValidateException('企业付款到零钱失败,请稍后再试');
}
if (sys_config('pay_wechat_type') == 1) {
$updateData = [
'transfer_bill_no' => $res['transfer_bill_no'] ?? '',
'wechat_state' => $res['state'] ?? '',
'fail_reason' => $res['fail_reason'] ?? '',
'package_info' => $res['package_info'] ?? ''
];
$this->dao->update($userExtract['id'], $updateData);
event('notice.notice', [[
'uid' => $userExtract['uid'],
'extract_price' => $userExtract['extract_price'],
'type' => 1,
'order_id' => $userExtract['order_id']
], 'revenue_received']);
}
}
return true;
}
/**
* 处理支付宝提现
* @param array $userExtract 提现记录
* @param float $extractNumber 提现金额
* @return bool
*/
protected function processAlipayExtract($userExtract, $extractNumber)
{
if (sys_config('alipay_extract_type', 0)) {
// 构造支付宝提现参数
$bizParams = [
'out_biz_no' => $userExtract['alipay_code'], // 商户订单号
'payee_type' => 'ALIPAY_LOGONID', // 收款方账户类型,ALIPAY_LOGONID-支付宝登录账号
'payee_account' => $userExtract['alipay_code'], // 收款方账户,实名认证的支付宝账号
'amount' => $extractNumber, // 提现金额
'payer_show_name' => sys_config('site_name'), // 付款方姓名/个人名称
'payee_real_name' => $userExtract['real_name'], // 收款方真实姓名/个人名称
'remark' => '提现 ¥' . $extractNumber . ' 到支付宝', // 业务备注
];
// 调用支付宝服务发起支付宝提现请求
$res = AliPayService::instance()->merchantPay($bizParams);
// 如果支付宝提现请求失败,则抛出异常
if (!$res) {
throw new ValidateException('企业付款到零钱失败,请稍后再试');
}
}
return true;
}
/**
* 显示资源列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index(array $where)
{
$list = $this->getUserExtractList($where);
//待提现金额
$where['status'] = 0;
$extract_statistics['price'] = $this->getExtractSum($where);
//已提现金额
$where['status'] = 1;
$extract_statistics['priced'] = $this->getExtractSum($where);
//佣金总金额
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$extract_statistics['brokerage_count'] = $userBrokerageServices->getUsersBokerageSum(array_merge($where, ['pm' => 1, 'not_type' => ['extract_fail', 'refund']]));
//未提现金额
$extract_statistics['brokerage_not'] = $extract_statistics['brokerage_count'] > $extract_statistics['priced'] ? bcsub((string)$extract_statistics['brokerage_count'], (string)$extract_statistics['priced'], 2) : 0.00;
return compact('extract_statistics', 'list');
}
/**
* 显示编辑资源表单页.
*
* @param int $id
* @return \think\Response
*/
public function edit(int $id)
{
$UserExtract = $this->getExtract($id);
if (!$UserExtract) {
throw new AdminException('数据不存在!');
}
$f = array();
$f[] = Form::input('real_name', '姓名', $UserExtract['real_name']);
$f[] = Form::number('extract_price', '提现金额', (float)$UserExtract['extract_price'])->disabled(true);
if ($UserExtract['extract_type'] == 'alipay') {
$f[] = Form::input('alipay_code', '支付宝账号', $UserExtract['alipay_code']);
} else if ($UserExtract['extract_type'] == 'weixin') {
$f[] = Form::input('wechat', '微信号', $UserExtract['wechat']);
} else {
$f[] = Form::input('bank_code', '银行卡号', $UserExtract['bank_code']);
$f[] = Form::input('bank_address', '开户行', $UserExtract['bank_address']);
}
$f[] = Form::input('mark', '备注', $UserExtract['mark'])->type('textarea');
return create_form('编辑', $f, Url::buildUrl('/finance/extract/' . $id), 'PUT');
}
/**
* 获取提现备注表单
* @param int $id 提现记录ID
* @return array
* @throws AdminException
*/
public function remarkForm($id)
{
$UserExtract = $this->getExtract($id);
if (!$UserExtract) {
throw new AdminException('数据不存在!');
}
$f = array();
$f[] = Form::input('mark', '备注', $UserExtract['mark'])->type('textarea');
return create_form('编辑', $f, Url::buildUrl('/finance/extract/remark/' . $id), 'PUT');
}
/**
* 更新提现记录
* @param int $id 提现记录ID
* @param array $data 更新数据
* @return bool
* @throws AdminException
*/
public function update(int $id, array $data)
{
if (!$this->dao->update($id, $data))
throw new AdminException('修改失败');
else
return true;
}
/**
* 拒绝
* @param $id
* @return mixed
*/
public function refuse(int $id, string $message)
{
$extract = $this->getExtract($id);
if (!$extract) {
throw new AdminException('操作记录不存在!');
}
if ($extract->status == 1) {
throw new AdminException('已经提现,错误操作');
}
if ($extract->status == -1) {
throw new AdminException('您的提现申请已被拒绝,请勿重复操作!');
}
$res = $this->changeFail($id, $extract, $message);
if ($res) {
return true;
} else {
throw new AdminException('操作失败!');
}
}
/**
* 通过
* @param $id
* @return mixed
*/
public function adopt(int $id)
{
$extract = $this->getExtract($id);
if (!$extract) {
throw new AdminException('操作记录不存!');
}
if ($extract->status == 1) {
throw new AdminException('您已提现,请勿重复提现!');
}
if ($extract->status == -1) {
throw new AdminException('您的提现申请已被拒绝!');
}
if ($this->changeSuccess($id, $extract)) {
return true;
} else {
throw new AdminException('操作失败!');
}
}
/**待提现的数量
* @return int
*/
public function userExtractCount()
{
return $this->dao->count(['status' => 0]);
}
/**
* 银行卡提现
* @param int $uid
* @return mixed
*/
public function bank(int $uid)
{
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$user = $userService->getUserInfo($uid, 'brokerage_price');
if (!$user) {
throw new ValidateException('数据不存在');
}
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$data['broken_commission'] = $userBrokerageServices->getUserFrozenPrice($uid);
if ($data['broken_commission'] < 0)
$data['broken_commission'] = '0';
$data['brokerage_price'] = $user['brokerage_price'];
//可提现佣金
$data['commissionCount'] = bcsub((string)$data['brokerage_price'], (string)$data['broken_commission'], 2);
$extractBank = sys_config('user_extract_bank') ?? []; //提现银行
$extractBank = str_replace("\r\n", "\n", $extractBank);//防止不兼容
$data['extractBank'] = explode("\n", is_array($extractBank) ? (isset($extractBank[0]) ? $extractBank[0] : $extractBank) : $extractBank);
$data['minPrice'] = sys_config('user_extract_min_price');//提现最低金额
$data['maxPrice'] = sys_config('user_extract_max_price');//提现最高金额
$data['withdraw_fee'] = sys_config('withdraw_fee');//提现手续费
$data['extract_wechat_type'] = sys_config('brokerage_type');//微信提现到账方式
$data['user_extract_balance_status'] = sys_config('user_extract_balance_status', 1);
return $data;
}
/**
* 提现申请
* @param int $uid
* @param array $data
*/
public function cash(int $uid, array $data)
{
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$user = $userService->getUserInfo($uid);
if (!$user) {
throw new ValidateException('数据不存在');
}
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$data['broken_commission'] = $userBrokerageServices->getUserFrozenPrice($uid);
if ($data['broken_commission'] < 0)
$data['broken_commission'] = 0;
$data['brokerage_price'] = $user['brokerage_price'];
//可提现佣金
$commissionCount = (float)bcsub((string)$data['brokerage_price'], (string)$data['broken_commission'], 2);
if ($data['money'] > $commissionCount) {
throw new ValidateException('可提现佣金不足');
}
$extractPrice = $user['brokerage_price'];
$userExtractMinPrice = sys_config('user_extract_min_price');
if ($data['money'] < $userExtractMinPrice) {
throw new ValidateException('提现金额不能小于' . $userExtractMinPrice . '元');
}
$userExtractMaxPrice = sys_config('user_extract_max_price');
if ($data['money'] > $userExtractMaxPrice) {
throw new ValidateException('提现金额不能大于' . $userExtractMaxPrice . '元');
}
if ($extractPrice < 0) {
throw new ValidateException('提现佣金不足' . $data['money']);
}
if ($data['money'] > $extractPrice) {
throw new ValidateException('提现佣金不足' . $data['money']);
}
if ($data['money'] <= 0) {
throw new ValidateException('提现佣金大于0');
}
$extract_fee = bcmul((string)$data['money'], bcdiv(sys_config('withdraw_fee'), '100', 4), 2);
if ($data['extract_type'] == 'balance') {//提现至余额不要手续费
$extract_fee = 0;
}
$insertData = [
'uid' => $user['uid'],
'order_id' => app()->make(StoreOrderCreateServices::class)->getNewOrderId('tx'),
'extract_type' => $data['extract_type'],
'extract_price' => bcsub((string)$data['money'], (string)$extract_fee, 2),
'extract_fee' => $extract_fee,
'add_time' => time(),
'balance' => $user['brokerage_price'],
'status' => 0,
'channel_type' => $data['channel_type']
];
if (isset($data['name']) && strlen(trim($data['name']))) $insertData['real_name'] = $data['name'];
else $insertData['real_name'] = $user['nickname'];
if (isset($data['cardnum'])) $insertData['bank_code'] = $data['cardnum'];
else $insertData['bank_code'] = '';
if (isset($data['bankname'])) $insertData['bank_address'] = $data['bankname'];
else $insertData['bank_address'] = '';
if (isset($data['weixin'])) $insertData['wechat'] = $data['weixin'];
else $insertData['wechat'] = $user['nickname'];
if ($data['extract_type'] == 'alipay') {
$insertData['alipay_code'] = $data['alipay_code'];
$insertData['qrcode_url'] = $data['qrcode_url'];
$mark = '使用支付宝';
} else if ($data['extract_type'] == 'bank') {
$mark = '使用银联卡' . $insertData['bank_code'];
} else if ($data['extract_type'] == 'weixin') {
$insertData['qrcode_url'] = $data['qrcode_url'];
$mark = '使用微信提现';
/** @var WechatUserServices $wechatServices */
$wechatServices = app()->make(WechatUserServices::class);
$openid = $wechatServices->getWechatOpenid($uid, 'wechat');
if (sys_config('brokerage_type', 0) && $openid) {
if ($insertData['extract_price'] < 0.01) {
throw new ValidateException('扣除手续费后,提现金额不足1元;而企业微信付款到零钱最低金额为0.01元');
}
}
} elseif ($data['extract_type'] == 'balance') {
$mark = '提现至余额账号';
}
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$res1 = $this->transaction(function () use ($insertData, $data, $uid, $userService, $user, $userBrokerageServices, $mark) {
if (!$res1 = $this->dao->save($insertData)) {
throw new ValidateException('提现失败');
}
//修改用户佣金
$balance = bcsub((string)$user['brokerage_price'], (string)$data['money'], 2) ?? 0;
if (!$userService->update($uid, ['brokerage_price' => $balance], 'uid')) {
throw new ValidateException('修改用户信息失败');
}
//保存佣金记录
$userBrokerageServices->income('extract', $uid, ['mark' => $mark, 'number' => $data['money']], $balance, $res1['id']);
//提现值余额 不用审核,直接通过
if ($data['extract_type'] == 'balance') {
$this->adopt((int)$res1['id']);
}
return $res1;
});
//用户申请体现事件
event('user.extract', [$user, $data, $res1]);
return true;
}
/**
* @param array $where
* @param string $SumField
* @param string $selectType
* @param string $group
* @return float|mixed
*/
public function getOutMoneyByWhere(array $where, string $SumField, string $selectType, string $group = "")
{
switch ($selectType) {
case "sum" :
return $this->dao->getWhereSumField($where, $SumField);
case "group" :
return $this->dao->getGroupField($where, $SumField, $group);
}
}
/**
* 用户取消提现申请
* @param int $id 提现记录ID
* @param int $uid 用户ID
* @return bool
* @throws ValidateException
*/
public function cancel($id, $uid)
{
$extract = $this->getExtract($id);
if (!$extract) {
throw new ValidateException('提现记录不存在');
}
if ($extract['status'] != 0) {
throw new ValidateException('提现状态不是待审核,不能取消');
}
if ($extract['uid'] != $uid) {
throw new ValidateException('提现不属于当前用户');
}
$res = $this->changeFail($id, $extract, '用户取消提现申请');
if ($res) {
return true;
} else {
throw new ValidateException('操作失败!');
}
}
}

View File

@@ -0,0 +1,108 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user;
use app\dao\user\UserFriendsDao;
use app\services\BaseServices;
use app\services\user\level\SystemUserLevelServices;
use think\annotation\Inject;
/**
* 获取好友列表
* Class UserFriendsServices
* @package app\services\user
* @mixin UserFriendsDao
*/
class UserFriendsServices extends BaseServices
{
/**
* @var UserFriendsDao
*/
#[Inject]
protected UserFriendsDao $dao;
/**
* 保存好友关系
* @param int $uid
* @param int $friends_uid
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function saveFriend(int $uid, int $friends_uid)
{
$data = [
'uid' => $uid,
'friends_uid' => $friends_uid
];
$userFriend = $this->dao->get($data);
$res1 = true;
if (!$userFriend) {
$data['add_time'] = time();
$res1 = $this->dao->save($data);
}
return $res1;
}
/**
* 获取好友uids 我推广的 推广我的
* @param int $uid
* @return array
*/
public function getFriendUids(int $uid)
{
$result = [];
if ($uid) {
$spread = $this->dao->getColumn(['uid' => $uid], 'friends_uid');
$sup_spread = $this->dao->getColumn(['friends_uid' => $uid], 'uid');
$result = array_unique(array_merge($spread, $sup_spread));
}
return $result;
}
/**
* 获取好友
* @param int $id
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getFriendList(int $uid, string $field = 'uid,nickname,level,add_time')
{
$uids = $this->getFriendUids($uid);
$list = [];
$count = 0;
if ($uids) {
[$page, $limit] = $this->getPageValue();
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$list = $userServices->getList(['uid' => $uids], $field, $page, $limit);
/** @var SystemUserLevelServices $systemLevelServices */
$systemLevelServices = app()->make(SystemUserLevelServices::class);
$systemLevelList = $systemLevelServices->getWhereLevelList([], 'id,name');
if ($systemLevelList) $systemLevelList = array_combine(array_column($systemLevelList, 'id'), $systemLevelList);
foreach ($list as &$item) {
$item['type'] = $systemLevelList[$item['level']]['name'] ?? '暂无';
$item['add_time'] = $item['add_time'] && is_numeric($item['add_time']) ? date('Y-m-d H:i:s', $item['add_time']) : '';
}
$count = $userServices->count(['uid' => $uids]);
}
return compact('list', 'count');
}
}

View File

@@ -0,0 +1,242 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user;
use app\dao\user\UserBillDao;
use app\jobs\user\UserIntegralJob;
use app\services\BaseServices;
use think\annotation\Inject;
/**
* 用户积分
* Class UserIntegralServices
* @package app\services\user
* @mixin UserBillDao
*/
class UserIntegralServices extends BaseServices
{
/**
* @var UserBillDao
*/
#[Inject]
protected UserBillDao $dao;
/**
* 清空到期积分(分批加入队列)
* @return bool
*/
public function clearExpireIntegral()
{
//是否开启积分有效期
if (!sys_config('integral_effective_status', 0)) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$users = $userServices->getColumn([['integral', '>', 0]], 'uid');
if ($users) {
//拆分大数组
$uidsArr = array_chunk($users, 100);
foreach ($uidsArr as $uids) {
//加入同步|更新用户队列
UserIntegralJob::dispatch([$uids]);
}
}
return true;
}
/**
* 执行清空到期积分
* @param array $uids
* @return bool
*/
public function doClearExpireIntegral(array $uids)
{
if (!$uids) return true;
//是否开启积分有效期
if (!sys_config('integral_effective_status', 0)) {
return true;
}
[$clear_time, $start_time, $end_time] = $this->getTime();
$start = date('Y年m月d日', $start_time);
$end = date('Y年m月d日', $end_time);
$where = ['category' => 'integral', 'pm' => 1, 'status' => 1];
$where['add_time'] = [$start_time, $end_time];
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$users = $userServices->getColumn([['uid', 'in', $uids]], 'uid,integral', 'uid');
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
//查询是否清除过
$clear_uids = $userBillServices->getColumn([['category', '=', 'integral'], ['type', '=', 'system_clear'], ['add_time', '>=', $clear_time], ['add_time', '<=', $clear_time + 86400]], 'uid');
foreach ($uids as $uid) {
$number = 0;
if (!isset($users[$uid]) || in_array($uid, $clear_uids)) continue;
$user = $users[$uid];
$where['uid'] = $uid;
$where['pm'] = 1;
$userSumIntegralInc = $userBillServices->getBillSum($where);
$where['pm'] = 0;
$userSumIntegralDec = $userBillServices->getBillSum($where);
$userSumIntegral = $userSumIntegralInc > $userSumIntegralDec ? (int)bcsub((string)$userSumIntegralInc, (string)$userSumIntegralDec, 0) : 0;
if ($userSumIntegral) {
$user_data = [];
$number = $userSumIntegral;
if ($userSumIntegral >= $user['integral']) {
$number = $user['integral'];
$user_data['integral'] = 0;
} else {
$user_data['integral'] = (int)bcsub((string)$user['integral'], (string)$userSumIntegral, 0);
}
//记录清除积分
$userBillServices->income('system_clear_integral', $uid, (int)$number, (int)$user_data['integral'], $uid);
$userServices->update($uid, $user_data);
}
}
return true;
}
/**
* 获取用户清空到期积分
* @param int $uid
* @param array $user
* @return array|int[]
*/
public function getUserClearIntegral(int $uid, $user = [])
{
if (!$uid) return [0, 0];
//是否开启积分有效期
if (!sys_config('integral_effective_status', 0)) {
return [0, 0];
}
[$clear_time, $start_time, $end_time] = $this->getTime();
$where = ['category' => 'integral', 'pm' => 1, 'status' => 1];
$where['add_time'] = [$start_time, $end_time];
if (!$user) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->get($uid, 'uid,integral');
}
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
$where['uid'] = $uid;
$userSumIntegralInc = $userBillServices->getBillSum($where);
$where['pm'] = 0;
$userSumIntegralDec = $userBillServices->getBillSum($where);
$userSumIntegral = $userSumIntegralInc > $userSumIntegralDec ? (int)bcsub((string)$userSumIntegralInc, (string)$userSumIntegralDec, 0) : 0;
if ($userSumIntegral) {
if ($userSumIntegral >= $user['integral']) {
$userSumIntegral = $user['integral'];
}
}
return [$userSumIntegral, $clear_time];
}
/**
* 获取清空积分时间段
* @param int $type
* @return int[]
*/
public function getTime(int $type = 0)
{
if (!$type) $type = (int)sys_config('integral_effective_time', 3);
switch ($type) {
case 1://月
$start = date('Y-m-01 00:00:00', strtotime('-1 month'));
$end = date("Y-m-d 23:59:59", strtotime(-date('d') . 'day'));
$clear_end = date('Y-m-t', strtotime(date('Y-m-d')));
break;
case 2://季度
$season = ceil((date('n')) / 3) - 1;//上季度是第几季度
$start = date('Y-m-d 00:00:00', mktime(0, 0, 0, $season * 3 - 3 + 1, 1, date('Y')));
$end = date('Y-m-d 23:59:59', mktime(23, 59, 59, $season * 3, date('t', mktime(0, 0, 0, $season * 3, 1, date("Y"))), date('Y')));
$clear_end = date('Y-m-t', mktime(0, 0, 0, ($season + 1) * 3, 1, date('Y')));
break;
case 3://年
default://默认年
$start = date('Y-01-01 00:00:00', strtotime('-1 year'));
$end = date('Y-m-d 23:59:59', strtotime($start . "+12 month -1 day"));
$clear_end = date('Y-12-31');
break;
}
return [strtotime($clear_end), strtotime($start), strtotime($end)];
}
/**
* 新人礼赠送积分
* @param int $uid
* @return bool
*/
public function newcomerGiveIntegral(int $uid)
{
if (!sys_config('newcomer_status')) {
return false;
}
$status = sys_config('register_integral_status');
if (!$status) {//未开启
return true;
}
$integral = (int)sys_config('register_give_integral', []);
if (!$integral) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo($uid);
if (!$userInfo) {
return true;
}
$balance = bcadd((string)$userInfo['integral'], (string)$integral, 2);
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
$userBillServices->income('newcomer_give_integral', $uid, (int)$integral, (int)$balance);
$userServices->update($uid, ['integral' => $balance]);
return true;
}
/**
* 会员卡激活赠送积分
* @param int $uid
* @return bool
*/
public function levelGiveIntegral(int $uid)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo($uid);
if (!$userInfo) {
return true;
}
$status = sys_config('level_activate_status');
if (!$status) {//是否需要激活
return true;
}
$status = sys_config('level_integral_status');
if (!$status) {//未开启
return true;
}
$integral = (int)sys_config('level_give_integral', []);
if (!$integral) {
return true;
}
$balance = bcadd((string)$userInfo['integral'], (string)$integral, 2);
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
$userBillServices->income('level_give_integral', $uid, (int)$integral, (int)$balance);
$userServices->update($uid, ['integral' => $balance]);
return true;
}
}

View File

@@ -0,0 +1,211 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\dao\user\UserInvoiceDao;
use app\services\BaseServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class UserInvoiceServices
* @package app\services\user
* @mixin UserInvoiceDao
*/
class UserInvoiceServices extends BaseServices
{
/**
* @var UserInvoiceDao
*/
#[Inject]
protected UserInvoiceDao $dao;
/**
* 检测系统设置发票功能
* @param bool $is_speclial
* @return bool|array
*/
public function invoiceFuncStatus(bool $is_speclial = true)
{
$invoice = (bool)sys_config('invoice_func_status', 0);
if ($is_speclial) {
$specialInvoice = sys_config('special_invoice_status', 0);
return ['invoice_func' => $invoice, 'special_invoice' => $invoice && $specialInvoice];
}
return $invoice;
}
/**
* 获取单个发票信息
* @param int $id
* @param int $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getInvoice(int $id, int $uid = 0)
{
$invoice = $this->dao->getOne(['id' => $id, 'is_del' => 0]);
if (!$invoice || ($uid && $invoice['uid'] != $uid)) {
return [];
}
return $invoice->toArray();
}
/**
* 检测该发票是否可用
* @param int $id
* @param int $uid
* @return bool|array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function checkInvoice(int $id, int $uid)
{
$invoice = $this->getInvoice($id, $uid);
if (!$invoice) {
throw new ValidateException('发票不存在或删除');
}
$invoice_func = $this->invoiceFuncStatus();
if (!$invoice_func['invoice_func']) {
throw new ValidateException('暂未开启开票,请联系管理员');
}
//专用发票
if ($invoice['type'] == 2) {
if (!$invoice_func['special_invoice']) {
throw new ValidateException('暂未开启专用发票,请联系管理员');
}
}
return $invoice;
}
/**
* 获取某个用户发票列表
* @param int $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserList(int $uid, $where)
{
[$page, $limit] = $this->getPageValue();
$where['is_del'] = 0;
$where['uid'] = $uid;
return $this->dao->getList($where, '*', $page, $limit);
}
/**
* 获取某个用户默认发票
* @param int $uid
* @param string $field
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserDefaultInvoice(int $uid, int $type, string $field = '*')
{
return $this->dao->getOne(['uid' => $uid, 'is_default' => 1, 'is_del' => 0, 'type' => $type], $field);
}
/**
* 添加|修改
* @param int $uid
* @param array $data
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function saveInvoice(int $uid, array $data)
{
$id = (int)$data['id'];
$data['uid'] = $uid;
unset($data['id']);
$invoice = $this->dao->get(['uid' => $uid, 'name' => $data['name'], 'drawer_phone' => $data['drawer_phone'], 'is_del' => 0]);
if ($id) {
if ($invoice && $id != $invoice['id']) {
throw new ValidateException('该发票已经存在');
}
if ($this->dao->update($id, $data, 'id')) {
if ($data['is_default']) {
$this->setDefaultInvoice($uid, $id);
}
return ['type' => 'edit', 'msg' => '修改发票成功', 'data' => []];
} else {
throw new ValidateException('修改失败或者您没有修改什么');
}
} else {
if ($invoice) {
throw new ValidateException('该发票已经存在');
}
if ($add_invoice = $this->dao->save($data)) {
$id = (int)$add_invoice['id'];
if ($data['is_default']) {
$this->setDefaultInvoice($uid, $id);
}
return ['type' => 'add', 'msg' => '添加发票成功', 'data' => ['id' => $id]];
} else {
throw new ValidateException('添加失败');
}
}
}
/**
* 设置默认发票
* @param int $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setDefaultInvoice(int $uid, int $id)
{
if (!$invoice = $this->getInvoice($id)) {
throw new ValidateException('发票不存在');
}
if ($invoice['uid'] != $uid) {
throw new ValidateException('数据错误');
}
if (!$this->dao->setDefault($uid, $id, $invoice['header_type'], $invoice['type'])) {
throw new ValidateException('设置默认发票失败');
}
return true;
}
/**
* 删除
* @param $id
* @throws \Exception
*/
public function delInvoice(int $uid, int $id)
{
if ($invoice = $this->getInvoice($id)) {
if ($invoice['uid'] != $uid) {
throw new ValidateException('数据错误');
}
if (!$this->dao->update($id, ['is_del' => 1])) {
throw new ValidateException('删除失败,请稍候再试!');
}
}
return true;
}
}

View File

@@ -0,0 +1,645 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\dao\user\UserMoneyDao;
use app\services\BaseServices;
use app\services\order\StoreOrderRefundServices;
use app\services\system\admin\SystemAdminServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
use crmeb\services\CacheService;
/**
* 用户余额
* Class UserMoneyServices
* @package app\services\user
* @mixin UserMoneyDao
*/
class UserMoneyServices extends BaseServices
{
/**
* 用户记录模板
* @var array[]
*/
protected array $incomeData = [
'pay_product' => [
'title' => '余额支付购买商品',
'type' => 'pay_product',
'mark' => '余额支付{%num%}元购买商品',
'status' => 1,
'pm' => 0
],
'pay_product_refund' => [
'title' => '商品退款',
'type' => 'pay_product_refund',
'mark' => '订单余额退款{%num%}元',
'status' => 1,
'pm' => 1
],
'system_add' => [
'title' => '系统增加余额',
'type' => 'system_add',
'mark' => '系统增加{%num%}余额',
'status' => 1,
'pm' => 1
],
'system_sub' => [
'title' => '系统减少余额',
'type' => 'system_sub',
'mark' => '系统扣除了{%num%}余额',
'status' => 1,
'pm' => 0
],
'user_recharge' => [
'title' => '用户充值余额',
'type' => 'recharge',
'mark' => '成功充值余额{%price%}元,赠送{%give_price%}元',
'status' => 1,
'pm' => 1
],
'user_recharge_refund' => [
'title' => '用户充值退款',
'type' => 'recharge_refund',
'mark' => '退款扣除用户余额{%num%}元',
'status' => 1,
'pm' => 0
],
'brokerage_to_nowMoney' => [
'title' => '佣金提现到余额',
'type' => 'extract',
'mark' => '佣金提现到余额{%num%}元',
'status' => 1,
'pm' => 1
],
'lottery_use_money' => [
'title' => '参与抽奖使用余额',
'type' => 'lottery_use',
'mark' => '参与抽奖使用{%num%}余额',
'status' => 1,
'pm' => 0
],
'lottery_give_money' => [
'title' => '抽奖中奖赠送余额',
'type' => 'lottery_add',
'mark' => '抽奖中奖赠送{%num%}余额',
'status' => 1,
'pm' => 1
],
'newcomer_give_money' => [
'title' => '新人礼赠送余额',
'type' => 'newcomer_add',
'mark' => '新人礼赠送{%num%}余额',
'status' => 1,
'pm' => 1
],
'level_give_money' => [
'title' => '会员卡激活赠送余额',
'type' => 'level_add',
'mark' => '会员卡激活赠送{%num%}余额',
'status' => 1,
'pm' => 1
],
'pay_integral_product' => [
'title' => '余额支付购买积分商品',
'type' => 'pay_integral_product',
'mark' => '余额支付{%num%}元购买积分商品',
'status' => 1,
'pm' => 0
],
'card_add_money' => [
'title' => '礼品卡充值余额',
'type' => 'card_add_money',
'mark' => '礼品卡充值余额{%num%}元',
'status' => 1,
'pm' => 1
],
'holiday_gift_money' => [
'title' => '节日有礼赠送余额',
'type' => 'holiday_gift_money',
'mark' => '节日有礼赠送{%num%}元',
'status' => 1,
'pm' => 1
],
];
/**
* 类型名称
* @var string[]
*/
protected array $typeName = [
'pay_product' => '商城购物',
'pay_product_refund' => '商城购物退款',
'system_add' => '系统充值',
'system_sub' => '系统扣除',
'recharge' => '用户充值',
'recharge_refund' => '用户充值退款',
'extract' => '佣金提现充值',
'lottery_use' => '抽奖使用',
'lottery_add' => '抽奖中奖充值',
'newcomer_add' => '新人礼赠送充值',
'level_add' => '会员卡激活赠送充值',
'card_add_money' => '礼品卡充值余额',
];
/**
* @var UserMoneyDao
*/
#[Inject]
protected UserMoneyDao $dao;
/**
* 获取用户记录总和
* @param $uid
* @param string $category
* @param array $type
* @return mixed
*/
public function getRecordCount(int $uid, $category = 'now_money', $type = [], $time = '', $pm = false)
{
$where = [];
$where['uid'] = $uid;
$where['category'] = $category;
$where['status'] = 1;
if (is_string($type) && strlen(trim($type))) {
$where['type'] = explode(',', $type);
}
if ($time) {
$where['time'] = $time;
}
if ($pm) {
$where['pm'] = 0;
}
return $this->dao->getBillSumColumn($where);
}
/**
* 获取资金列表
* @param array $where
* @param string $field
* @param int $limit
* @return array
*/
public function getMoneyList(array $where, string $field = '*', int $limit = 0)
{
$where_data = [];
if (isset($where['uid']) && $where['uid'] != '') {
$where_data['uid'] = $where['uid'];
}
if ($where['start_time'] != '' && $where['end_time'] != '') {
$where_data['time'] = str_replace('-', '/', $where['start_time']) . ' - ' . str_replace('-', '/', $where['end_time']);
}
if (isset($where['category']) && $where['category'] != '') {
$where_data['category'] = $where['category'];
}
if (isset($where['type']) && $where['type'] != '') {
$where_data['type'] = $where['type'];
}
$where_data['not_category'] = ['integral', 'exp', 'share'];
$where_data['not_type'] = ['gain', 'system_sub', 'deduction', 'sign'];
if (isset($where['nickname']) && $where['nickname'] != '') {
$where_data['like'] = $where['nickname'];
}
if (isset($where['excel']) && $where['excel'] != '') {
$where_data['excel'] = $where['excel'];
} else {
$where_data['excel'] = 0;
}
if ($limit) {
[$page] = $this->getPageValue();
} else {
[$page, $limit] = $this->getPageValue();
}
$data = $this->dao->getList($where_data, $field, $page, $limit, [
'user' => function ($query) {
$query->field('uid,nickname');
}]);
foreach ($data as &$item) {
$item['nickname'] = $item['user']['nickname'] ?? '';
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
unset($item['user']);
}
$count = $this->dao->count($where_data);
return compact('data', 'count');
}
/**
* 用户|所有资金变动列表
* @param int $uid
* @param array $where_time
* @param string $field
* @return array
*/
public function getUserMoneyList(int $uid = 0, $where_time = [], string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$where = [];
if ($uid) $where['uid'] = $uid;
if ($where_time) $where['add_time'] = $where_time;
$list = $this->dao->getList($where, $field, $page, $limit);
$count = $this->dao->count($where);
foreach ($list as &$item) {
$value = array_filter($this->incomeData, function ($value) use ($item) {
if ($item['type'] == $value['type']) {
return $item['title'];
}
});
$item['type_title'] = $value[$item['type']]['title'] ?? '未知类型';
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
if (in_array($item['type'], ['system_add', 'system_sub'])) {
$item['admin_name'] = app()->make(SystemAdminServices::class)->value(['id' => $item['link_id']], 'real_name');
} else {
$item['admin_name'] = '';
}
}
return compact('list', 'count');
}
/**
* 获取用户的充值总数
* @param int $uid
* @return float
*/
public function getRechargeSum(int $uid = 0, $time = [])
{
$where = ['uid' => $uid, 'pm' => 1, 'status' => 1, 'type' => ['system_add', 'recharge', 'extract', 'lottery_add', 'newcomer_add', 'level_add']];
if ($time) $where['add_time'] = $time;
return $this->dao->sum($where, 'number', true);
}
/**
* 用户|所有充值列表
* @param int $uid
* @param string $field
* @return array
*/
public function getRechargeList(int $uid = 0, $where_time = [], string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$where = ['category' => 'now_money', 'type' => 'recharge'];
if ($uid) $where['uid'] = $uid;
if ($where_time) $where['add_time'] = $where_time;
$list = $this->dao->getList($where, $field, $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 写入用户记录
* @param string $type 写入类型
* @param int $uid
* @param int|string|array $number
* @param int|string $balance
* @param int $link_id
* @return bool|mixed
*/
public function income(string $type, int $uid, $number, $balance, $link_id = 0, string $title = '', $mark = '')
{
$data = $this->incomeData[$type] ?? null;
if (!$data) {
return true;
}
$data['uid'] = $uid;
$data['balance'] = $balance ?? 0;
$data['link_id'] = $link_id;
if (is_array($number)) {
$key = array_keys($number);
$key = array_map(function ($item) {
return '{%' . $item . '%}';
}, $key);
$value = array_values($number);
$data['number'] = $number['number'] ?? 0;
$data['mark'] = str_replace($key, $value, $data['mark']);
} else {
$data['number'] = $number;
$data['mark'] = str_replace(['{%num%}'], (string)$number, $data['mark']);
}
if ($title) $data['title'] = $title;
if ($mark) $data['mark'] = $mark;
$data['add_time'] = time();
if ((float)$data['number']) {
return $this->dao->save($data);
}
return true;
}
/**
* 资金类型
* @return bool|mixed|null
*/
public function bill_type()
{
return CacheService::get('user_money_type_list', function () {
return ['list' => $this->dao->getMoneyType([])];
}, 600);
}
/**
* 用户余额记录列表
* @param int $uid
* @param int $type
* @param array $data
* @return array
*/
public function userMoneyList(int $uid, int $type, array $data = [])
{
$where = [];
$where['uid'] = $uid;
switch ($type) {
case 1:
$where['pm'] = 0;
break;
case 2:
$where['pm'] = 1;
break;
case 0:
default:
break;
}
[$page, $limit] = $this->getPageValue();
if ((isset($data['start']) && $data['start']) || (isset($data['stop']) && $data['stop'])) {
$where['time'] = [$data['start'], $data['stop']];
}
$list = $this->dao->getList($where, '*', $page, $limit);
$count = $this->dao->count($where);
$times = [];
if ($list) {
$typeName = $this->typeName;
foreach ($list as &$item) {
$item['time_key'] = $item['time'] = $item['add_time'] ? date('Y-m', (int)$item['add_time']) : '';
$item['day'] = $item['add_time'] ? date('Y-m-d', (int)$item['add_time']) : '';
$item['add_time'] = $item['add_time'] ? date('Y/m/d H:i', (int)$item['add_time']) : '';
$item['type_name'] = $typeName[$item['type'] ?? ''] ?? '未知类型';
if ($item['type'] == 'pay_product') {
/** @var StoreOrderRefundServices $refundSerives */
$refundSerives = app()->make(StoreOrderRefundServices::class);
$refund = $refundSerives->getOne(['store_order_id' => $item['link_id']]);
$item['refund_status'] = '';
if ($refund) {
if ($refund['refund_type'] == 6) {
$item['refund_status'] = '已退款';
} else {
$item['refund_status'] = '退款中';
}
}
}
if ($item['type'] == 'recharge') {
/** @var UserRechargeServices $rechargeSerives */
$orderSerives = app()->make(UserRechargeServices::class);
$refund_price = $orderSerives->value(['id' => $item['link_id']], 'refund_price');
if ($refund_price) {
$item['refund_status'] = '已退款';
} else {
$item['refund_status'] = '';
}
}
}
$times = array_merge(array_unique(array_column($list, 'time_key')));
}
return ['count' => $count, 'list' => $list, 'time' => $times];
}
/**
* 根据查询用户充值金额
* @param array $where
* @param string $rechargeSumField
* @param string $selectType
* @param string $group
* @return float|mixed
*/
public function getRechargeMoneyByWhere(array $where, string $rechargeSumField, string $selectType, string $group = "")
{
switch ($selectType) {
case "sum" :
return $this->dao->getWhereSumField($where, $rechargeSumField);
case "group" :
return $this->dao->getGroupField($where, $rechargeSumField, $group);
}
}
/**
* 新人礼赠送余额
* @param int $uid
* @return bool
*/
public function newcomerGiveMoney(int $uid)
{
if (!sys_config('newcomer_status')) {
return false;
}
$status = sys_config('register_money_status');
if (!$status) {//未开启
return true;
}
$money = (int)sys_config('register_give_money', []);
if (!$money) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo($uid);
if (!$userInfo) {
return true;
}
$balance = bcadd((string)$userInfo['now_money'], (string)$money);
$this->income('newcomer_give_money', $uid, $money, $balance);
$userServices->update($uid, ['now_money' => $balance]);
return true;
}
/**
* 会员卡激活赠送余额
* @param int $uid
* @return bool
*/
public function levelGiveMoney(int $uid)
{
$status = sys_config('level_activate_status');
if (!$status) {//是否需要激活
return true;
}
$status = sys_config('level_money_status');
if (!$status) {//未开启
return true;
}
$money = (int)sys_config('level_give_money', []);
if (!$money) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo($uid);
if (!$userInfo) {
return true;
}
$balance = bcadd((string)$userInfo['now_money'], (string)$money);
$this->income('level_give_money', $uid, $money, $balance);
$userServices->update($uid, ['now_money' => $balance]);
return true;
}
/**
* 余额统计基础
* @param $where
* @return array
*/
public function getBasic($where)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$data['now_balance'] = $userServices->sum(['status' => 1], 'now_money', true);
$data['add_balance'] = $this->dao->sum(['pm' => 1], 'number', true);
$data['sub_balance'] = $this->dao->sum(['pm' => 0], 'number', true);
return $data;
}
/**
* 余额趋势
* @param $where
* @return array
*/
public function getTrend($where)
{
$time = explode('-', $where['time']);
if (count($time) != 2) throw new AdminException('参数错误');
$dayCount = (strtotime($time[1]) - strtotime($time[0])) / 86400 + 1;
$data = [];
if ($dayCount == 1) {
$data = $this->trend($time, 0);
} elseif ($dayCount > 1 && $dayCount <= 31) {
$data = $this->trend($time, 1);
} elseif ($dayCount > 31 && $dayCount <= 92) {
$data = $this->trend($time, 3);
} elseif ($dayCount > 92) {
$data = $this->trend($time, 30);
}
return $data;
}
/**
* 余额趋势
* @param $time
* @param $num
* @param false $excel
* @return array
*/
public function trend($time, $num, $excel = false)
{
if ($num == 0) {
$xAxis = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
$timeType = '%H';
} elseif ($num != 0) {
$dt_start = strtotime($time[0]);
$dt_end = strtotime($time[1]);
while ($dt_start <= $dt_end) {
if ($num == 30) {
$xAxis[] = date('Y-m', $dt_start);
$dt_start = strtotime("+1 month", $dt_start);
$timeType = '%Y-%m';
} else {
$xAxis[] = date('m-d', $dt_start);
$dt_start = strtotime("+$num day", $dt_start);
$timeType = '%m-%d';
}
}
}
$time[1] = date("Y-m-d", strtotime("+1 day", strtotime($time[1])));
$point_add = array_column($this->dao->getBalanceTrend($time, $timeType, 'add_time', 'sum(number)', 'add'), 'num', 'days');
$point_sub = array_column($this->dao->getBalanceTrend($time, $timeType, 'add_time', 'sum(number)', 'sub'), 'num', 'days');
$data = $series = [];
foreach ($xAxis as $item) {
$data['余额积累'][] = isset($point_add[$item]) ? floatval($point_add[$item]) : 0;
$data['余额消耗'][] = isset($point_sub[$item]) ? floatval($point_sub[$item]) : 0;
}
foreach ($data as $key => $item) {
$series[] = [
'name' => $key,
'data' => $item,
'type' => 'line',
];
}
return compact('xAxis', 'series');
}
/**
* 余额来源
* @param $where
* @return array
*/
public function getChannel($where)
{
$bing_xdata = ['系统增加', '用户充值', '佣金提现', '抽奖赠送', '商品退款'];
$color = ['#64a1f4', '#3edeb5', '#70869f', '#ffc653', '#fc7d6a'];
$data = ['system_add', 'recharge', 'extract', 'lottery_add', 'pay_product_refund'];
$bing_data = [];
foreach ($data as $key => $item) {
$bing_data[] = [
'name' => $bing_xdata[$key],
'value' => $this->dao->sum(['pm' => 1, 'type' => $item, 'time' => $where['time']], 'number', true),
'itemStyle' => ['color' => $color[$key]]
];
}
$list = [];
$count = array_sum(array_column($bing_data, 'value'));
foreach ($bing_data as $key => $item) {
$list[] = [
'name' => $item['name'],
'value' => $item['value'],
'percent' => $count != 0 ? bcmul((string)bcdiv((string)$item['value'], (string)$count, 4), '100', 2) : 0,
];
}
array_multisort(array_column($list, 'value'), SORT_DESC, $list);
return compact('bing_xdata', 'bing_data', 'list');
}
/**
* 余额类型
* @param $where
* @return array
*/
public function getType($where)
{
$bing_xdata = ['系统减少', '充值退款', '购买商品'];
$color = ['#64a1f4', '#3edeb5', '#70869f'];
$data = ['system_sub', 'recharge_refund', 'pay_product'];
$bing_data = [];
foreach ($data as $key => $item) {
$bing_data[] = [
'name' => $bing_xdata[$key],
'value' => $this->dao->sum(['pm' => 0, 'type' => $item, 'time' => $where['time']], 'number', true),
'itemStyle' => ['color' => $color[$key]]
];
}
$list = [];
$count = array_sum(array_column($bing_data, 'value'));
foreach ($bing_data as $key => $item) {
$list[] = [
'name' => $item['name'],
'value' => $item['value'],
'percent' => $count != 0 ? bcmul((string)bcdiv((string)$item['value'], (string)$count, 4), '100', 2) : 0,
];
}
array_multisort(array_column($list, 'value'), SORT_DESC, $list);
return compact('bing_xdata', 'bing_data', 'list');
}
}

View File

@@ -0,0 +1,597 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\jobs\user\MicroPayOrderJob;
use app\services\BaseServices;
use app\dao\user\UserRechargeDao;
use app\services\pay\PayServices;
use app\services\pay\RechargeServices;
use app\services\system\config\SystemGroupDataServices;
use crmeb\exceptions\AdminException;
use crmeb\services\{AliPayService, FormBuilder as Form, wechat\Payment};
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
use app\services\order\StoreOrderCreateServices;
use app\services\wechat\WechatUserServices;
use app\webscoket\SocketPush;
/**
*
* Class UserRechargeServices
* @package app\services\user
* @mixin UserRechargeDao
*/
class UserRechargeServices extends BaseServices
{
/**
* @var UserRechargeDao
*/
#[Inject]
protected UserRechargeDao $dao;
/**
* 获取单条数据
* @param int $id
* @param array $field
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getRecharge(int $id, array $field = [])
{
return $this->dao->get($id, $field);
}
/**
* @param int $storeId
* @return int
*/
public function getRechargeCount(int $storeId)
{
return $this->dao->count(['store_id' => $storeId, 'paid' => 1]);
}
/**
* 获取统计数据
* @param array $where
* @param string $field
* @return float
*/
public function getRechargeSum(array $where, string $field = '')
{
$whereData = [];
if (isset($where['data'])) {
$whereData['time'] = $where['data'];
}
if (isset($where['paid']) && $where['paid'] != '') {
$whereData['paid'] = $where['paid'];
}
if (isset($where['nickname']) && $where['nickname']) {
$whereData['like'] = $where['nickname'];
}
if (isset($where['recharge_type']) && $where['recharge_type']) {
$whereData['recharge_type'] = $where['recharge_type'];
}
if (isset($where['store_id'])) {
$whereData['store_id'] = $where['store_id'];
}
return $this->dao->getWhereSumField($whereData, $field);
}
/**
* 获取充值列表
* @param array $where
* @param string $field
* @param int $limit
* @return array
*/
public function getRechargeList(array $where, string $field = '*', int $limit = 0, array $with = [])
{
$whereData = $where;
if (isset($where['data'])) {
$whereData['time'] = $where['data'];
unset($whereData['data']);
}
if (isset($where['nickname']) && $where['nickname']) {
$whereData['like'] = $where['nickname'];
unset($whereData['nickname']);
}
if ($limit) {
[$page] = $this->getPageValue();
} else {
[$page, $limit] = $this->getPageValue();
}
$list = $this->dao->getList($whereData, $field, $page, $limit, $with);
$count = $this->dao->count($whereData);
foreach ($list as &$item) {
switch ($item['recharge_type']) {
case 'routine':
$item['_recharge_type'] = '小程序充值';
break;
case 'weixin':
$item['_recharge_type'] = '公众号充值';
break;
case 'alipay':
$item['_recharge_type'] = '支付宝充值';
break;
case 'balance':
$item['_recharge_type'] = '佣金转入';
break;
case 'store':
$item['_recharge_type'] = '门店余额充值';
break;
default:
$item['_recharge_type'] = '其他充值';
break;
}
$item['_pay_time'] = $item['pay_time'] ? date('Y-m-d H:i:s', $item['pay_time']) : '暂无';
$item['_add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '暂无';
$item['paid_type'] = $item['paid'] ? '已支付' : '待付款';
unset($item['user']);
}
return compact('list', 'count');
}
/**
* 获取用户充值数据
* @return array
*/
public function user_recharge(array $where)
{
$data = [];
$where['paid'] = 1;
$data['sumPrice'] = $this->getRechargeSum($where, 'price');
$data['sumRefundPrice'] = $this->getRechargeSum($where, 'refund_price');
$where['recharge_type'] = 'routine';
$data['sumRoutinePrice'] = $this->getRechargeSum($where, 'price');
$where['recharge_type'] = 'weixin';
$data['sumWeixinPrice'] = $this->getRechargeSum($where, 'price');
return [
[
'name' => '充值总金额',
'field' => '元',
'count' => $data['sumPrice'],
'className' => 'logo-yen',
'col' => 6,
],
[
'name' => '充值退款金额',
'field' => '元',
'count' => $data['sumRefundPrice'],
'className' => 'logo-usd',
'col' => 6,
],
[
'name' => '小程序充值金额',
'field' => '元',
'count' => $data['sumRoutinePrice'],
'className' => 'logo-bitcoin',
'col' => 6,
],
[
'name' => '公众号充值金额',
'field' => '元',
'count' => $data['sumWeixinPrice'],
'className' => 'ios-bicycle',
'col' => 6,
],
];
}
/**
* 退款表单
* @param int $id
* @return mixed
*/
public function refund_edit(int $id)
{
$UserRecharge = $this->getRecharge($id);
if (!$UserRecharge) {
throw new AdminException('数据不存在!');
}
if ($UserRecharge['paid'] != 1) {
throw new AdminException('订单未支付');
}
if ($UserRecharge['price'] == $UserRecharge['refund_price']) {
throw new AdminException('已退完支付金额!不能再退款了');
}
if ($UserRecharge['recharge_type'] == 'balance') {
throw new AdminException('佣金转入余额,不能退款');
}
$f = array();
$f[] = Form::input('order_id', '退款单号', $UserRecharge->getData('order_id'))->disabled(true);
$f[] = Form::radio('refund_price', '状态', 1)->options([['label' => '本金(扣赠送余额)', 'value' => 1], ['label' => '仅本金', 'value' => 0]]);
// $f[] = Form::number('refund_price', '退款金额', (float)$UserRecharge->getData('price'))->min(0)->max($UserRecharge->getData('price'));
// if ($UserRecharge['store_id']) {
// return create_form('退款', $f, Url::buildUrl('/order/recharge/' . $id), 'PUT');
// } else {
return create_form('退款', $f, Url::buildUrl('/finance/recharge/' . $id), 'PUT');
// }
}
/**
* 充值退款操作
* @param int $id
* @param $refund_price
* @return mixed
*/
public function refund_update(int $id, string $refund_price)
{
$UserRecharge = $this->getRecharge($id);
if (!$UserRecharge) {
throw new AdminException('数据不存在!');
}
if ($UserRecharge['price'] == $UserRecharge['refund_price']) {
throw new AdminException('已退完支付金额!不能再退款了');
}
if ($UserRecharge['recharge_type'] == 'balance') {
throw new AdminException('佣金转入余额,不能退款');
}
$UserRecharge = $UserRecharge->toArray();
// $data['refund_price'] = bcadd($refund_price, $UserRecharge['refund_price'], 2);
$data['refund_price'] = $UserRecharge['price'];
// $bj = bccomp((string)$UserRecharge['price'], (string)$data['refund_price'], 2);
// if ($bj < 0) {
// throw new AdminException('退款金额大于支付金额,请修改退款金额');
// }
$refund_data['pay_price'] = $UserRecharge['price'];
$refund_data['refund_price'] = $UserRecharge['price'];
// $refund_data['refund_account']='REFUND_SOURCE_RECHARGE_FUNDS';
if ($refund_price == 1) {
$number = bcadd($UserRecharge['price'], $UserRecharge['give_price'], 2);
} else {
$number = $UserRecharge['price'];
}
try {
$recharge_type = $UserRecharge['recharge_type'];
switch ($recharge_type) {
case 'alipay'://支付宝
mt_srand();
$refund_id = $refundData['refund_id'] ?? $UserRecharge['order_id'] . rand(100, 999);
//支付宝退款
AliPayService::instance()->refund($UserRecharge['order_id'], $refund_data['refund_price'], $refund_id);
break;
case 'wexin'://微信
$pay_routine_open = (bool)sys_config('pay_routine_open', 0);
if ($pay_routine_open) {
$refund_data['refund_no'] = $UserRecharge['order_id']; // 退款订单号
/** @var WechatUserServices $wechatUserServices */
$wechatUserServices = app()->make(WechatUserServices::class);
$refund_data['open_id'] = $wechatUserServices->value(['uid' => (int)$UserRecharge['uid']], 'openid');
$refund_data['routine_order_id'] = $UserRecharge['order_id'];
$refund_data['pay_routine_open'] = true;
}
$transaction_id = $UserRecharge['trade_no'];
if (!$transaction_id) {
$refund_data['type'] = 'out_trade_no';
$transaction_id = $UserRecharge['order_id'];
} else {
$refund_data['type'] = 'transaction_id';
}
if (!sys_config('wechat_appid')) {
//APP退款
Payment::instance()->setAccessEnd(Payment::APP)->payOrderRefund($transaction_id, $refund_data);
} else {
//微信公众号退款
Payment::instance()->setAccessEnd(Payment::WEB)->payOrderRefund($transaction_id, $refund_data);
}
break;
case 'routine'://小程序
$transaction_id = $UserRecharge['trade_no'];
if (!$transaction_id) {
$refund_data['type'] = 'out_trade_no';
$transaction_id = $UserRecharge['order_id'];
} else {
$refund_data['type'] = 'transaction_id';
}
Payment::instance()->setAccessEnd(Payment::MINI)->payOrderRefund($transaction_id, $refund_data);
break;
case 'cash'://现金
break;
}
} catch (\Exception $e) {
throw new AdminException($e->getMessage());
}
if (!$this->dao->update($id, $data)) {
throw new AdminException('修改提现数据失败');
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo((int)$UserRecharge['uid']);
if ($userInfo['now_money'] > $number) {
$now_money = bcsub((string)$userInfo['now_money'], $number, 2);
} else {
$number = $userInfo['now_money'];
$now_money = 0;
}
//修改用户余额
$userServices->update((int)$UserRecharge['uid'], ['now_money' => $now_money], 'uid');
$UserRecharge['nickname'] = $userInfo['nickname'];
$UserRecharge['phone'] = $userInfo['phone'];
/** @var UserMoneyServices $userMoneyServices */
$userMoneyServices = app()->make(UserMoneyServices::class);
//保存余额记录
$userMoneyServices->income('user_recharge_refund', $UserRecharge['uid'], $number, $now_money, $id);
//充值退款事件
event('user.rechargeRefund', [$UserRecharge, $data]);
return true;
}
/**
* 删除
* @param int $id
* @return bool
*/
public function delRecharge(int $id)
{
$rechargInfo = $this->getRecharge($id);
if (!$rechargInfo) throw new AdminException('订单未找到');
if ($rechargInfo->paid) {
throw new AdminException('已支付的订单记录无法删除');
}
if ($this->dao->delete($id))
return true;
else
throw new AdminException('删除失败');
}
/**
* 生成充值订单号
* @return bool|string
*/
public function getOrderId()
{
return 'wx' . date('YmdHis', time()) . substr(implode('', array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
/**
* 导入佣金到余额
* @param int $uid
* @param $price
* @param string $type recharge : 充值流程 extract:提现流程
* @return bool
*/
public function importNowMoney(int $uid, $price, string $type = 'recharge')
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo($uid);
if (!$user) {
throw new ValidateException('数据不存在');
}
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$broken_commission = $userBrokerageServices->getUserFrozenPrice($uid);
$commissionCount = bcsub((string)$user['brokerage_price'], (string)$broken_commission, 2);
if ($price > $commissionCount && $type == 'recharge') {
throw new ValidateException('转入金额不能大于可提现佣金!');
}
return $this->transaction(function () use ($uid, $user, $price, $userServices, $type) {
$edit_data = [];
$edit_data['now_money'] = bcadd((string)$user['now_money'], (string)$price, 2);
//写入充值记录
$rechargeInfo = [
'uid' => $uid,
'order_id' => $this->getOrderId(),
'recharge_type' => 'balance',
'price' => $price,
'give_price' => 0,
'paid' => 1,
'pay_time' => time(),
'add_time' => time()
];
//写入充值记录
$re = $this->dao->save($rechargeInfo);
/** @var UserMoneyServices $userMoneyServices */
$userMoneyServices = app()->make(UserMoneyServices::class);
//余额记录
$userMoneyServices->income('brokerage_to_nowMoney', $uid, $price, $edit_data['now_money'], $re['id']);
//佣金充值写入提现记录
if ($type == 'recharge') {
//修改用户佣金
$edit_data['brokerage_price'] = $user['brokerage_price'] > $price ? bcsub((string)$user['brokerage_price'], (string)$price, 2) : 0;
$extractInfo = [
'uid' => $uid,
'real_name' => $user['nickname'],
'extract_type' => 'balance',
'extract_price' => $price,
'balance' => $edit_data['brokerage_price'],
'add_time' => time(),
'status' => 1
];
/** @var UserExtractServices $userExtract */
$userExtract = app()->make(UserExtractServices::class);
//写入提现记录
$userExtract->save($extractInfo);
//佣金提现记录
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$userBrokerageServices->income('brokerage_to_nowMoney', $uid, $price, $edit_data['brokerage_price'], $re['id']);
}
//修改用户佣金、余额信息
$userServices->update($uid, $edit_data, 'uid');
});
}
/**
* 申请充值
* @param int $uid
* @param $price
* @param $recharId
* @param $type
* @param $from
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
*/
public function recharge(int $uid, $price, $recharId, $type, $from)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo($uid);
if (!$user) {
throw new ValidateException('数据不存在');
}
$paid_price = 0;
if ($recharId && $type != 1) {
/** @var SystemGroupDataServices $systemGroupData */
$systemGroupData = app()->make(SystemGroupDataServices::class);
$data = $systemGroupData->getDateValue($recharId);
if (!$data) {
throw new ValidateException('您选择的充值方式已下架!');
} else {
$paid_price = $data['give_money'] ?? 0;
}
$price = $data['price'];
}
switch ((int)$type) {
case 0: //支付充值余额
/** @var StoreOrderCreateServices $orderCreateServices */
$orderCreateServices = app()->make(StoreOrderCreateServices::class);
$recharge_data = [];
$recharge_data['order_id'] = $orderCreateServices->getNewOrderId('cz');
$recharge_data['uid'] = $uid;
$recharge_data['price'] = $price;
$recharge_data['recharge_type'] = $from;
$recharge_data['paid'] = 0;
$recharge_data['add_time'] = time();
$recharge_data['give_price'] = $paid_price;
$recharge_data['channel_type'] = $user['user_type'];
if (!$rechargeOrder = $this->dao->save($recharge_data)) {
throw new ValidateException('充值订单生成失败');
}
return ['msg' => '', 'type' => $from, 'data' => $rechargeOrder];
break;
case 1: //佣金转入余额
$this->importNowMoney($uid, $price);
return ['msg' => '转入余额成功', 'type' => $from, 'data' => []];
break;
default:
throw new ValidateException('缺少参数');
break;
}
}
/**
* 用户充值成功
* @param $orderId
* @return bool
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function rechargeSuccess($orderId, array $other = [])
{
$order = $this->dao->getOne(['order_id' => $orderId, 'paid' => 0]);
if (!$order) {
throw new ValidateException('订单失效或者不存在');
}
$order = $order->toArray();
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo((int)$order['uid']);
if (!$user) {
throw new ValidateException('数据不存在');
}
$price = bcadd((string)$order['price'], (string)$order['give_price'], 2);
if (!$this->dao->update($order['id'], ['paid' => 1, 'pay_time' => time(), 'trade_no' => $other['trade_no'] ?? ''], 'id')) {
throw new ValidateException('修改订单失败');
}
$now_money = bcadd((string)$user['now_money'], (string)$price, 2);
/** @var UserMoneyServices $userMoneyServices */
$userMoneyServices = app()->make(UserMoneyServices::class);
$userMoneyServices->income('user_recharge', $user['uid'], ['number' => $price, 'price' => $order['price'], 'give_price' => $order['give_price']], $now_money, $order['id']);
if (!$userServices->update((int)$order['uid'], ['now_money' => $now_money], 'uid')) {
throw new ValidateException('修改用户信息失败');
}
$order['nickname'] = $user['nickname'];
$order['phone'] = $user['phone'];
$order['trade_no'] = $other['trade_no'] ?? '';
$order['pay_time'] = time();
//用户充值成功事件
event('user.recharge', [$order, $now_money]);
return true;
}
/**
* 根据查询用户充值金额
* @param array $where
* @return float|int
*/
public function getRechargeMoneyByWhere(array $where, string $rechargeSumField, string $selectType, string $group = "")
{
switch ($selectType) {
case "sum" :
return $this->dao->getWhereSumField($where, $rechargeSumField);
case "group" :
return $this->dao->getGroupField($where, $rechargeSumField, $group);
}
}
/**
* 充值每日统计数据
* @param int $store_id
* @param int $staff_id
* @param array $time
* @return array
*/
public function getDataPriceCount(int $store_id, int $staff_id = 0, $time = [])
{
[$page, $limit] = $this->getPageValue();
$where = ['paid' => 1, 'time' => $time];
if ($staff_id) {
$where['staff_id'] = $staff_id;
}
return $this->dao->getDataPriceCount($where, ['sum(price) as price', 'count(id) as count', 'FROM_UNIXTIME(add_time, \'%m-%d\') as time'], $page, $limit);
}
/**
* 门店充值统计详情列表
* @param int $store_id
* @param int $staff_id
* @param array $time
* @return array|array[]
*/
public function time(int $store_id, int $staff_id, array $time = [])
{
if (!$time) {
return [[], []];
}
[$start, $stop, $front, $front_stop] = $time;
$where = ['paid' => 1];
if ($staff_id) $where['staff_id'] = $staff_id;
$frontPrice = $this->dao->sum($where + ['time' => [$front, $front_stop]], 'price', true);
$nowPrice = $this->dao->sum($where + ['time' => [$start, $stop]], 'price', true);
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where + ['time' => [$start, $stop]], 'id,uid,order_id,price,add_time', $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
}
return [[$nowPrice, $frontPrice], $list];
}
}

View File

@@ -0,0 +1,295 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user;
use app\dao\user\UserRelationDao;
use app\services\activity\video\VideoServices;
use app\services\BaseServices;
use app\services\product\product\StoreProductServices;
use app\services\user\level\SystemUserLevelServices;
use app\services\user\member\MemberCardServices;
use think\annotation\Inject;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
/**
* Class UserRelationServices
* @package app\services\user
* @mixin UserRelationDao
*/
class UserRelationServices extends BaseServices
{
const CATEGORY_PRODUCT = 'product';//商品
const CATEGORY_ARTICLE = 'article';//文章
const CATEGORY_REPLY = 'reply';//评价
const CATEGORY_COMMENT = 'comment';//评价回复
const CATEGORY_VIDEO = 'video';//短视频
const CATEGORY_VIDEO_COMMENT = 'video_comment';//短视频评价
const TYPE_COLLECT = 'collect';//收藏
const TYPE_LIKE = 'like';//点赞
const TYPE_SHARE = 'share';//分享
const TYPE_PLAY = 'play';//播放
const TYPE_NAME = [
'collect' => '收藏',
'like' => '点赞',
'share' => '分享',
'play' => '播放',
];
/**
* @var UserRelationDao
*/
#[Inject]
protected UserRelationDao $dao;
/**
* 用户是否点赞或收藏商品
* @param array $where
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function isProductRelation(array $where)
{
$res = $this->dao->getOne($where, 'id');
if ($res) {
return true;
} else {
return false;
}
}
/**
* @param array $where
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2022/11/17
*/
public function isProductRelationCache(array $where)
{
return $this->cacheTag()->remember(md5(json_encode($where)), function () use ($where) {
return $this->isProductRelation($where);
});
}
/**
* 获取用户收藏数量
* @param int $uid
* @param int $relationId
* @param string $type
* @param string $category
* @return int
*/
public function getUserCount(int $uid, int $relationId = 0, string $type = self::TYPE_COLLECT, string $category = self::CATEGORY_PRODUCT)
{
$where = ['uid' => $uid];
if ($type) {
$where['type'] = $type;
}
if ($category) {
$where['category'] = $category;
}
if ($relationId) {
$where['relation_id'] = $relationId;
}
return $this->dao->count($where);
}
/**
* @param int $uid
* @return mixed
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function getUserRelationList(int $uid, string $category = self::CATEGORY_PRODUCT, string $type = self::TYPE_COLLECT)
{
$where['uid'] = $uid;
$where['type'] = $type;
$where['category'] = $category;
[$page, $limit] = $this->getPageValue();
$with = [];
switch ($category) {
case 'product':
$with = ['product'];
break;
case 'video':
$with = ['video'];
break;
}
//短视频未启用
if ($category == 'video' && !sys_config('video_func_status', 1)) {
$list = [];
} else {
$list = $this->dao->getList($where, 'relation_id,category', $with, $page, $limit);
}
$result = [];
foreach ($list as $k => $item) {
switch ($category) {
case 'product':
if (isset($item['product']) && isset($item['product']['id'])) {
$product = $item['product'];
$discount = 100;
/** @var UserServices $user */
$user = app()->make(UserServices::class);
$userInfo = $user->getUserCacheInfo($uid);
//用户等级是否开启
/** @var SystemUserLevelServices $systemLevel */
$systemLevel = app()->make(SystemUserLevelServices::class);
$levelInfo = $systemLevel->getLevelCache((int)($userInfo['level'] ?? 0));
if (sys_config('member_func_status', 1) && $levelInfo) {
$discount = $levelInfo['discount'] ?? 100;
}
$level_name = $levelInfo['name'] ?? '';
/** @var MemberCardServices $memberCardService */
$memberCardService = app()->make(MemberCardServices::class);
$vipStatus = $memberCardService->isOpenMemberCardCache('vip_price') && sys_config('svip_price_status', 1);
/** @var StoreProductServices $productServices */
$productServices = app()->make(StoreProductServices::class);
$minData = $productServices->getMinPrice($uid, $product, $discount);
$vip_price = $level_price = 0.00;
if (($minData['price_type'] ?? '') == 'member') {
$vip_price = $minData['vip_price'] ?? 0;
if (!$product['is_vip'] || !$vipStatus) {
$vip_price = 0;
}
} else {
$level_price = $minData['vip_price'] ?? 0;
}
$data = [
'id' => $product['id'] ?? 0,
'product_id' => $item['relation_id'],
'store_name' => $product['store_name'] ?? 0,
'price_type' => $minData['price_type'],
'price' => $product['price'] ?? 0,
'is_presale_product' => $product['is_presale_product'] ?? 0,
'vip_price' => $vip_price ?? 0,
'level_name' => $level_name,
'level_price' => $level_price,
'freight' => $product['freight'] ?? 0,
'ot_price' => $product['ot_price'] ?? 0,
'sales' => $product['sales'] ?? 0,
'image' => get_thumb_water($product['image'] ?? 0),
'is_del' => $product['is_del'] ?? 0,
'is_show' => $product['is_show'] ?? 0,
'is_fail' => $product['is_del'] && $product['is_show'],
'activity' => $product['activity'] ?? ''
];
$result[] = $data;
}
break;
case 'video':
if (isset($item['video']) && isset($item['video']['id'])) {
$video = $item['video'];
$data = [
'id' => $video['id'] ?? 0,
'video_id' => $item['relation_id'],
'image' => $video['image'] ?? '',
'site_name' => sys_config('site_name'),
'wap_login_logo' => sys_config('wap_login_logo'),
'desc' => $video['desc'] ?? '',
'video_url' => $video['video_url'] ?? '',
'like_num' => $video['like_num'] ?? 0
];
$result[] = $data;
}
break;
}
}
if ($result && $category == 'product') {
/** @var StoreProductServices $productServices */
$productServices = app()->make(StoreProductServices::class);
$result = $productServices->getActivityList($result);
$result = $productServices->getProductPromotions($result);
}
return $result;
}
/**
* 添加|取消 点赞 收藏
* @param int $uid
* @param array $productIds
* @param string $relationType
* @param string $category
* @param bool $isDec
* @return bool
*/
public function productRelation(int $uid, array $productIds, string $relationType = self::TYPE_COLLECT, string $category = self::CATEGORY_PRODUCT, bool $isDec = false)
{
$relationType = strtolower($relationType);
$category = strtolower($category);
if ($isDec) {//取消
$storeProductRelation = $this->dao->delete(['uid' => $uid, 'relation_id' => $productIds, 'type' => $relationType, 'category' => $category]);
if (!$storeProductRelation) throw new ValidateException('取消失败');
if ($category == 'video') {
/** @var VideoServices $videoServices */
$videoServices = app()->make(VideoServices::class);
foreach ($productIds as $id) {
$videoServices->bcDec($id, $relationType . '_num', 1);
}
}
$ids = $productIds;
} else {
$relationId = $this->dao->getColumn([['uid', '=', $uid], ['relation_id', 'IN', $productIds], ['type', '=', $relationType], ['category', '=', $category]], 'relation_id');
$data = ['uid' => $uid, 'add_time' => time(), 'type' => $relationType, 'category' => $category];
$dataAll = [];
$ids = [];
foreach ($productIds as $key => $product_id) {
if (in_array($product_id, $relationId)) {
continue;
}
$ids[] = $product_id;
$data['relation_id'] = $product_id;
$dataAll[] = $data;
}
if ($dataAll) {
if (!$this->dao->saveAll($dataAll)) {
throw new ValidateException('添加失败');
}
}
}
if ($ids && $category == 'product') {
event('product.collect', [$uid, $ids, $isDec]);
}
$this->cacheTag()->clear();
return true;
}
/**
* 计算商品收藏数
* @param int $id
* @return bool
* @throws DbException
*/
public function computedProductCollect(int $id)
{
/** @var StoreProductServices $productServices */
$productServices = app()->make(StoreProductServices::class);
$productInfo = $productServices->get($id, ['id', 'collect']);
if ($productInfo) {
$collect = $this->dao->count(['relation_id' => $id, 'type' => self::TYPE_COLLECT, 'category'=> self::CATEGORY_PRODUCT]);
$productServices->update(['id' => $id], ['collect' => $collect]);
$productServices->cacheTag()->clear();
}
return true;
}
}

View File

@@ -0,0 +1,129 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\services\product\product\StoreProductServices;
use app\dao\user\UserSearchDao;
use think\annotation\Inject;
/**
*
* Class UserLabelServices
* @package app\services\user
* @mixin UserSearchDao
*/
class UserSearchServices extends BaseServices
{
/**
* @var UserSearchDao
*/
#[Inject]
protected UserSearchDao $dao;
/**
* 分词搜索得到ids
* @param int $uid
* @param string $keyword
* @param array $where
* @param bool $isSearch
* @return array|mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function vicSearch(int $uid, string $keyword, array $where, bool $isSearch = true)
{
//分词
// try {
// $vicWordArr = \crmeb\services\VicWordService::instance()->getWord($keyword);
// } catch (\Throwable $e) {
// $vicWordArr = [];
// }
$vicWordArr = [];
if ($vicWordArr) {
$vicword = $vicWordArr;
unset($where['store_name']);
$where['keyword'] = $vicWordArr;
}
$result = $this->dao->getKeywordResult(0, $keyword);
$ids = [];
if ($result && isset($result['result']) && $result['result']) {//之前查询结果记录
$ids = $result['result'];
} else {//分词查询
}
//搜索没有记录
if (!$ids && $where) {
//查出所有结果ids存搜索记录表
/** @var StoreProductServices $services */
$services = app()->make(StoreProductServices::class);
$idsArr = $services->getSearchList($where, 0, 0, ['id'], '', []);
if ($idsArr) {
$ids = array_column($idsArr, 'id');
}
}
$vicword = [];
// $vicword = is_string($vicword) ? [$vicword] : $vicword;
if ($isSearch) {
$this->saveUserSearch($uid, $keyword, $vicword, $ids);
}
return $ids;
}
/**
* 获取用户搜索关键词列表
* @param int $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserList(int $uid)
{
if (!$uid) {
return [];
}
[$page, $limit] = $this->getPageValue();
return $this->dao->getList(['uid' => $uid, 'is_del' => 0], 'id,keyword,num', $page, $limit,'add_time desc,num desc');
}
/**
* 用户增加搜索记录
* @param int $uid
* @param string $key
* @param array $result
*/
public function saveUserSearch(int $uid, string $keyword, array $vicword, array $result)
{
$result = json_encode($result);
$vicword = json_encode($vicword, JSON_UNESCAPED_UNICODE);
$userkeyword = $this->dao->getKeywordResult($uid, $keyword, 0);
$data = [];
$data['result'] = $result;
$data['vicword'] = $vicword;
$data['add_time'] = time();
if ($userkeyword) {
$data['num'] = $userkeyword['num'] + 1;
$this->dao->update(['id' => $userkeyword['id']], $data);
} else {
$data['uid'] = $uid;
$data['keyword'] = $keyword;
$this->dao->save($data);
}
return true;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,545 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\jobs\user\UserLevelJob;
use app\services\BaseServices;
use app\services\system\SystemSignRewardServices;
use app\services\user\member\MemberCardServices;
use app\dao\user\UserSignDao;
use crmeb\exceptions\ApiException;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
*
* Class UserSignServices
* @package app\services\user
* @mixin UserSignDao
*/
class UserSignServices extends BaseServices
{
/**
* @var UserSignDao
*/
#[Inject]
protected UserSignDao $dao;
/**
* 返回签到列表数据
* @param $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function signConfig($uid)
{
//获取周签到还是月签到
$signMode = (int)sys_config('sign_mode', 1);
//获取签到列表
$startDate = $signMode == 1 ? strtotime('this week Monday') : strtotime('first day of this month midnight');
$endDate = $signMode == 1 ? strtotime('this week Sunday') : strtotime('last day of this month midnight');
$dateList = range($startDate, $endDate, 86400);
$checkSign = $this->getIsSign($uid, 'today');
$sign_give_point = (int)sys_config('sign_give_point', 0);
$sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;
[$signList, $continuousSignDays, $cumulativeSignDays, $nextContinuousDays, $nextCumulativeDays] = $this->signDataFilling($signMode, $uid, $sign_give_point, $sign_exp, $dateList, '');
//格式化签到数据
// $signList = array_chunk($signList, 7);
//获取用户签到提醒状态
$signRemindStatus = app()->make(UserServices::class)->value(['uid' => $uid], 'sign_remind');
//是否显示签到提醒开关
$signRemindSwitch = (int)sys_config('sign_remind', 0);
//签到功能是否关闭
$signStatus = (int)sys_config('sign_status', 0);
$signData['sign_point'] = $sign_give_point;
$signData['sign_exp'] = $sign_exp;
return compact('signList', 'continuousSignDays', 'cumulativeSignDays', 'nextContinuousDays', 'nextCumulativeDays', 'signMode', 'checkSign', 'signRemindStatus', 'signRemindSwitch', 'signStatus', 'signData');
}
/**
* 日历数据
* @param $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getCalendarDate($uid, $time)
{
$startDate = strtotime(date('Y-m-01', strtotime($time)));
$endDate = strtotime(date('Y-m-d', strtotime(date('Y-m-01', strtotime($time)) . ' +1 month -1 day')));
$dateList = range($startDate, $endDate, 86400);
$checkSign = $this->getIsSign($uid, 'today');
$sign_give_point = (int)sys_config('sign_give_point', 0);
$sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;
$today = date("Y-m-d"); // 今日日期
$w = date('w', $startDate); //月初周几
[$signList, $continuousSignDays, $cumulativeSignDays, $nextContinuousDays, $nextCumulativeDays] = $this->signDataFilling(0, $uid, $sign_give_point, $sign_exp, $dateList, $time);
return compact('today', 'w', 'signList', 'continuousSignDays', 'cumulativeSignDays', 'nextContinuousDays', 'nextCumulativeDays', 'checkSign');
}
/**
* 整理签到列表数据
* @param $signMode
* @param $uid
* @param $sign_give_point
* @param $sign_exp
* @param $dateList
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function signDataFilling($signMode, $uid, $sign_give_point, $sign_exp, $dateList, $time)
{
if (!$time) $time = date('Y-m', time());
//获取已经签到的列表
$list = $this->dao->getUserSignList($signMode, $uid, $time);
$sign_mode = sys_config('sign_mode', -1);
//获取累积签到和连续签到
$cumulativeSignDays = $this->dao->getCumulativeDays($uid, $sign_mode); //获取累积签到天数
$continuousSignDays = app()->make(UserServices::class)->value(['uid' => $uid], 'sign_num');//获取连续签到天数
//获取累积签到和连续签到奖励
$nextCumulativeSignRewardList = app()->make(SystemSignRewardServices::class)->selectList(['type' => 1], '*', 1, 200, 'days asc')->toArray(); //累计签到奖励
$nextContinuousSignRewardList = app()->make(SystemSignRewardServices::class)->selectList(['type' => 0], '*', 1, 200, 'days asc')->toArray(); //连续签到奖励
//下一次连续签到奖励还需签到几天
$nextContinuousDays = 0;
foreach ($nextContinuousSignRewardList as $continuousNext) {
if ($continuousSignDays < $continuousNext['days']) {
$nextContinuousDays = $continuousNext['days'] - $continuousSignDays > 0 ?: 1;
break;
}
}
$nextCumulativeDays = 0;
foreach ($nextCumulativeSignRewardList as $cumulativeNext) {
if ($cumulativeSignDays < $cumulativeNext['days']) {
$nextCumulativeDays = $cumulativeNext['days'] - $cumulativeSignDays > 0 ?: 1;
break;
}
}
//整理签到列表数据
$signList = [];
$i = 0;
foreach ($dateList as $key => $time) {
$day = date('m/d', $time);
if ($day[0] == '0') $day = substr($day, 1);
$signList[$key]['day'] = $day;
$signList[$key]['is_sign'] = false;
//判断当前签到日期
$signList[$key]['sign_day'] = date('Y-m-d', $time) == date('Y-m-d', time());
//判断今日是否签到
foreach ($list as $value) {
if (date('Y-m-d', $time) == date('Y-m-d', $value['add_time'])) {
$signList[$key]['is_sign'] = true;
break;
}
}
//处理处理签到类型展示,type 1积分,2经验
$signList[$key]['type'] = $sign_give_point == 0 && sys_config('member_func_status', 1) == 1 && $sign_exp > 0 ? 2 : 1;
$signList[$key]['point'] = $sign_give_point;
$signList[$key]['days'] = 0; //连续、累计 奖励天数
$signList[$key]['sign_type'] = 0; // 3连续签到奖励,4累计签到奖励
if (date('Y-m-d', $time) >= date('Y-m-d', time())) {
// 连续签到奖励
foreach ($nextContinuousSignRewardList as $continuous) {
if (($continuous['days'] - $continuousSignDays) == $i) {
$signList[$key]['sign_type'] = 3;
$signList[$key]['days'] = $continuous['days'];
$signList[$key]['point'] = $continuous['point'];
}
}
// 累计签到奖励
foreach ($nextCumulativeSignRewardList as $cumulative) {
if (($cumulative['days'] - $cumulativeSignDays) == $i) {
$signList[$key]['sign_type'] = 4;
$signList[$key]['days'] = $cumulative['days'];
$signList[$key]['point'] = $cumulative['point'];
}
}
$i++;
}
}
return [$signList, $continuousSignDays, $cumulativeSignDays, $nextContinuousDays, $nextCumulativeDays];
}
/**
* 获取用户是否签到
* @param int $uid
* @param string $type
* @return bool
*/
public function getIsSign(int $uid, string $type = 'today')
{
return (bool)$this->dao->count(['uid' => $uid, 'time' => $type]);
}
/**
* 获取用户累计签到次数
* @Parma int $uid 用户id
* @return int
* */
public function getSignSumDay(int $uid)
{
return $this->dao->count(['uid' => $uid]);
}
/**
* 设置签到数据
* @param int $uid 用户uid
* @param string $title 签到说明
* @param int $number 签到获得积分
* @param int $integral_balance
* @param int $exp_balance
* @param int $exp_num
* @return bool
* @throws \think\Exception
*/
public function setSignData($uid, $title = '', $number = 0, $up_num = 0, $integral_balance = 0, $exp_balance = 0, $exp_num = 0)
{
$data = [];
$data['uid'] = $uid;
$data['title'] = $title;
$data['number'] = $number;
$data['svip_number'] = $up_num;
$data['balance'] = $integral_balance;
$data['exp_num'] = $exp_num;
$data['exp_balance'] = $exp_balance;
$data['add_time'] = time();
if (!$this->dao->save($data)) {
throw new ValidateException('添加签到数据失败');
}
/** @var UserBillServices $userBill */
$userBill = app()->make(UserBillServices::class);
$data['mark'] = $title;
unset($data['exp_num'], $data['exp_balance']);
$userBill->incomeIntegral($uid, 'sign', $data);
if ($exp_num) {
$data['number'] = $exp_num;
$data['category'] = 'exp';
$data['type'] = 'sign';
$data['title'] = $data['mark'] = '签到奖励';
$data['balance'] = $exp_balance + $exp_num;
$data['pm'] = 1;
$data['status'] = 1;
if (!$userBill->save($data)) {
throw new ValidateException('赠送经验失败');
}
//检测会员等级
UserLevelJob::dispatch([$uid]);
}
return true;
}
/**
* 获取用户签到列表
* @param int $uid
* @param string $field
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserSignList(int $uid, string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList(['uid' => $uid], $field, $page, $limit);
foreach ($list as &$item) {
$item['svip_title'] = $item['svip_number'] > 0 ? 'SVIP +' . $item['svip_number'] . ' 积分' : '';
$item['add_time'] = $item['add_time'] ? date('Y-m-d', $item['add_time']) : '';
}
return $list;
}
/**
* 用户签到
* @param $uid
* @return bool|int|mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function sign(int $uid)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
//检测用户是否存在
$user = $userServices->getUserInfo($uid);
if (!$user) {
throw new ApiException('用户不存在');
}
//签到是否关闭
if (!sys_config('sign_status', 1)) {
throw new ApiException('签到暂未开放');
}
//检测今天是否已经签到
if ($this->getIsSign($uid, 'today')) {
throw new ApiException('今日已签到');
}
$title = '签到奖励';
//检测昨天是否签到,如果没有签到,连续签到清0
if (!$this->getIsSign($uid, 'yesterday')) $user->sign_num = 0;
//获取签到周期配置,如果周签到,每周一清空连续签到记录,如果月签到,每月一日清空连续签到记录
$sign_mode = sys_config('sign_mode', -1);
if ($sign_mode == 1 && date('w') == 1) $user->sign_num = 0;
if ($sign_mode == 0 && date('d') == 1) $user->sign_num = 0;
//连续签到天数
$user->sign_num += 1;
$continuousDays = $user->sign_num;
//累积签到天数
$cumulativeDays = $this->dao->getCumulativeDays($uid, $sign_mode) + 1;
//基础签到奖励
$sign_point = sys_config('sign_give_point', 0);
$sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;
//连续签到和累积签到奖励
$signRewardsServices = app()->make(SystemSignRewardServices::class);
[$continuousStatus, $continuousRewardPoint, $continuousRewardExp] = $signRewardsServices->getSignRewards(0, $continuousDays);
[$cumulativeStatus, $cumulativeRewardPoint, $cumulativeRewardExp] = $signRewardsServices->getSignRewards(1, $cumulativeDays);
if ($continuousStatus && $cumulativeStatus) {
$sign_point = $continuousRewardPoint + $cumulativeRewardPoint;
$sign_exp = $continuousRewardExp + $cumulativeRewardExp;
} elseif ($continuousStatus) {
$sign_point = $continuousRewardPoint;
$sign_exp = $continuousRewardExp;
} elseif ($cumulativeStatus) {
$sign_point = $cumulativeRewardPoint;
$sign_exp = $cumulativeRewardExp;
}
$up_num = 0;
//会员签到积分会员奖励
if ($user->is_money_level > 0) {
//看是否开启签到积分翻倍奖励
/** @var MemberCardServices $memberCardService */
$memberCardService = app()->make(MemberCardServices::class);
$sign_rule_number = $memberCardService->isOpenMemberCard('sign');
if ($sign_rule_number) {
$up_num = (int)$sign_rule_number * $sign_point - $sign_point;
$sign_point = (int)$sign_rule_number * $sign_point;
// if (!$this->getIsSign($uid, 'yesterday')) $title = '签到奖励(SVIP+' . $up_num . '积分)';
}
}
//增加签到数据
$this->transaction(function () use ($uid, $title, $sign_point, $user, $sign_exp, $up_num) {
$this->setSignData($uid, $title, $sign_point, $up_num, $user['integral'], (int)$user['exp'], $sign_exp);
if ($sign_point) $user->integral = (int)$user->integral + (int)$sign_point;
if ($sign_exp && $user['level_status']) {//有经验 && 用户等级已经激活
$user->exp = (int)$user->exp + (int)$sign_exp;
}
if (!$user->save()) {
throw new ApiException('修改用户数据失败');
}
});
return compact('sign_point', 'sign_exp');
}
/**
* 签到用户信息
* @param int $uid
* @param $sign
* @param $integral
* @param $all
* @return mixed
*/
public function signUser(int $uid, $sign, $integral, $all)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserCacheInfo($uid);
if (!$user) {
throw new ValidateException('数据不存在');
}
$data = ['uid' => $user['uid'], 'nickname' => $user['nickname'], 'phone' => $user['phone'], 'now_money' => $user['now_money'], 'integral' => $user['integral'], 'is_promoter' => $user['is_promoter'] ?? 0];
//是否统计签到
if ($sign || $all) {
$data['sum_sgin_day'] = $this->getSignSumDay($uid);
$data['is_day_sgin'] = $this->getIsSign($uid);
$data['is_YesterDay_sgin'] = $this->getIsSign($uid, 'yesterday');
if (!$data['is_day_sgin'] && !$data['is_YesterDay_sgin']) {
$data['sign_num'] = 0;
}
}
/** @var UserIntegralServices $userIntegralServices */
$userIntegralServices = app()->make(UserIntegralServices::class);
[$clear_integral, $clear_time] = $userIntegralServices->getUserClearIntegral($uid, $user);
$data['clear_integral'] = $clear_integral;
$data['clear_time'] = $clear_time;
//是否统计积分使用情况
if ($integral || $all) {
/** @var UserBillServices $userBill */
$userBill = app()->make(UserBillServices::class);
$data['sum_integral'] = intval($userBill->getRecordCount($uid, 'integral', [], '', true));
$data['deduction_integral'] = intval($userBill->getRecordCount($uid, 'integral') ?? 0);
$data['today_integral'] = intval($userBill->getRecordCount($uid, 'integral', [], 'today', true));
$data['frozen_integral'] = $userBill->getBillSum(['uid' => $uid, 'is_frozen' => 1]);
$data['integral_effective_status'] = (int)sys_config('integral_effective_status');
$data['clear_end'] = '';
if ($data['integral_effective_status']) {
[$clear_end, $start, $end] = $userIntegralServices->getTime();
$data['clear_end'] = date('Y-m-d', $clear_end);
}
}
if (!$user['is_promoter']) {
$data['is_promoter'] = (int)sys_config('store_brokerage_statu') == 2;
}
return $data;
}
/**
* 获取签到
* @param $uid
* @return array
*/
public function getSignMonthList($uid)
{
[$page, $limit] = $this->getPageValue();
$data = $this->dao->getListGroup(['uid' => $uid], 'FROM_UNIXTIME(add_time,"%Y-%m") as time,group_concat(id SEPARATOR ",") ids', $page, $limit, 'time');
$list = [];
if ($data) {
$ids = array_unique(array_column($data, 'ids'));
$dataIdsList = $this->dao->getList(['id' => $ids], 'FROM_UNIXTIME(add_time,"%Y-%m-%d") as add_time,title,number,exp_num,id,uid', 0, 0);
foreach ($data as $item) {
$value['month'] = $item['time'];
$value['list'] = array_merge(array_filter($dataIdsList, function ($val) use ($item) {
if (in_array($val['id'], explode(',', $item['ids']))) {
return $val;
}
}));
$list[] = $value;
}
}
return $list;
}
/**
* 签到提醒设置
* @param $uid
* @param $status
* @return bool
*/
public function setSignRemind($uid, $status)
{
app()->make(UserServices::class)->update($uid, ['sign_remind' => $status]);
return true;
}
/**
* 用户签到提醒
* @return bool
*/
public function userSignRemind()
{
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$where = ['is_del' => 0, 'status' => 1, 'sign_remind' => 1];
$userList = $userService->getColumn($where, 'uid,phone,sign_remind', 'uid');
foreach ($userList as $key => $item) {
if (!$this->getIsSign($item['uid'], 'today')) {
event('notice.notice', [$item, 'sign_remind_time']);
}
}
return true;
}
/**
* 首页diy签到数据
* @param int $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function homeDiysignData(int $uid = 0)
{
//获取签到列表
$startDate = strtotime('this week Monday');
$endDate = strtotime('this week Sunday');
$dateList = range($startDate, $endDate, 86400);
$checkSign = $this->getIsSign($uid, 'today');
$sign_give_point = (int)sys_config('sign_give_point', 0);
$sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;
//获取连续签到奖励
$nextContinuousSignRewardList = app()->make(SystemSignRewardServices::class)->selectList(['type' => 0], '*', 1, 1, 'days asc')->toArray(); //连续签到奖励
$list = [];
if ($uid) {
//获取已经签到的列表
$list = $this->dao->getUserSignList(1, $uid, '');
}
//整理签到列表数据
$signList = [];
foreach ($dateList as $key => $time) {
$day = date('m/d', $time);
if ($day[0] == '0') $day = substr($day, 1);
$signList[$key]['day'] = $day;
$signList[$key]['is_sign'] = false;
//判断当前签到日期
$signList[$key]['sign_day'] = date('Y-m-d', $time) == date('Y-m-d', time());
if ($list) {
//判断今日是否签到
foreach ($list as $value) {
if (date('Y-m-d', $time) == date('Y-m-d', $value['add_time'])) {
$signList[$key]['is_sign'] = true;
break;
}
}
}
//处理处理签到类型展示,type 1积分,2经验
$signList[$key]['type'] = $sign_give_point == 0 && sys_config('member_func_status', 1) == 1 && $sign_exp > 0 ? 2 : 1;
$signList[$key]['point'] = $sign_give_point;
}
//格式化签到数据
$signList = array_chunk($signList, 7);
//签到功能是否关闭
$signStatus = (int)sys_config('sign_status', 0);
return compact('signList', 'nextContinuousSignRewardList', 'checkSign', 'signStatus', 'sign_give_point');
}
}

View File

@@ -0,0 +1,209 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\dao\user\UserSpreadDao;
use app\services\order\store\BranchOrderServices;
use app\services\user\level\SystemUserLevelServices;
use think\annotation\Inject;
/**
* Class UserSpreadServices
* @package app\services\user
* @mixin UserSpreadDao
*/
class UserSpreadServices extends BaseServices
{
/**
* @var UserSpreadDao
*/
#[Inject]
protected UserSpreadDao $dao;
/**
* 记录推广关系
* @param int $uid
* @param int $spread_uid
* @param int $spread_time
* @param int $admin_id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setSpread(int $uid, int $spread_uid, int $spread_time = 0, int $admin_id = 0)
{
if (!$uid || !$spread_uid) return false;
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if (!$userServices->userExist($uid)) {
return false;
}
if (!$userServices->userExist($spread_uid)) {
return false;
}
$data = ['uid' => $uid, 'spread_uid' => $spread_uid, 'spread_time' => $spread_time ?: time(), 'admin_id' => $admin_id];
if ($this->dao->save($data)) {
return true;
} else {
return false;
}
}
/**
* 查询推广用户uids
* @param int $uid
* @param int $type 1:一级2:二级 0:所有
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSpreadUids(int $uid, int $type = 0, array $where = [])
{
if (!$uid) return [];
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if (!$userServices->userExist($uid)) {
return [];
}
if ($where && isset($where['time'])) {
$where['timeKey'] = 'spread_time';
}
$where['spread_uid'] = $uid;
$spread_one = $this->dao->getSpreadUids($where);
if ($type == 1) {
return $spread_one;
}
$where['spread_uid'] = $spread_one;
$spread_two = $this->dao->getSpreadUids($where);
if ($type == 2) {
return $spread_two;
}
return array_unique(array_merge($spread_one, $spread_two));
}
/**
* 门店推广统计详情列表
* @param int $store_id
* @param int $staff_id
* @param array $time
* @return array|array[]
*/
public function time(int $store_id, int $staff_id, array $time = [])
{
if (!$time) {
return [[], []];
}
[$start, $stop, $front, $front_stop] = $time;
$where = ['store_id' => $store_id];
if ($staff_id) {
$where['staff_id'] = $staff_id;
}
$frontPrice = $this->dao->count($where + ['time' => [$front, $front_stop], 'timeKey' => 'spread_time']);
$nowPrice = $this->dao->count($where + ['time' => [$start, $stop], 'timeKey' => 'spread_time']);
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where + ['time' => [$start, $stop], 'timeKey' => 'spread_time'], '*', ['user'], $page, $limit);
/** @var BranchOrderServices $order */
$order = app()->make(BranchOrderServices::class);
$order_where = ['time' => $time, 'is_del' => 0, 'is_system_del' => 0, 'paid' => 1, 'pid' => 0, 'refund_status' => [0, 3]];
foreach ($list as &$item) {
$item['spread_time'] = $item['spread_time'] ? date('Y-m-d H:i:s', $item['spread_time']) : '';
$orderCount = $order->column($order_where + ['uid' => $item['uid']], 'count(`id`) as count,sum(`pay_price`) as price');
$item['order_count'] = $orderCount[0]['count'] ?? 0;
$item['order_price'] = $orderCount[0]['price'] ?? 0.00;
}
return [[$nowPrice, $frontPrice], $list];
}
/**
* 获取推广列表
* @param int $uid
* @param UserServices $userServices
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSpreadList(array $where, string $field = '*', array $with = ['user'], bool $type = true)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $field, $with, $page, $limit);
$count = $this->dao->count($where);
/** @var BranchOrderServices $order */
$order = app()->make(BranchOrderServices::class);
foreach ($list as &$item) {
$item['spread_time'] = $item['spread_time'] ? date('Y-m-d H:i:s', $item['spread_time']) : '';
$item['type'] = $item['admin_id'] ? ('手动变更(' . ($item['real_name'] ?? '') . ')') : '自动变更';
if ($type) {
$orderCount = $order->column(['uid' => $item['uid'], 'is_del' => 0, 'is_system_del' => 0, 'paid' => 1, 'pid' => 0, 'refund_status' => [0, 3]], 'count(`id`) as count,sum(`pay_price`) as price');
$item['order_count'] = $orderCount[0]['count'] ?? 0;
$item['order_price'] = $orderCount[0]['price'] ?? 0.00;
}
}
return compact('list', 'count');
}
/**
* 获取好友uids 我推广的 推广我的
* @param int $uid
* @return array
*/
public function getFriendUids(int $uid)
{
$result = [];
if ($uid) {
$spread = $this->dao->getColumn(['spread_uid' => $uid], 'uid');
$sup_spread = $this->dao->getColumn(['uid' => $uid], 'spread_uid');
$result = array_unique(array_merge($spread, $sup_spread));
}
return $result;
}
/**
* 获取好友
* @param int $id
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getFriendList(int $uid, string $field = 'uid,nickname,level,add_time')
{
$uids = $this->getFriendUids($uid);
$list = [];
$count = 0;
if ($uids) {
[$page, $limit] = $this->getPageValue();
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$list = $userServices->getList(['uid' => $uids], $field, $page, $limit);
/** @var SystemUserLevelServices $systemLevelServices */
$systemLevelServices = app()->make(SystemUserLevelServices::class);
$systemLevelList = $systemLevelServices->getWhereLevelList([], 'id,name');
if ($systemLevelList) $systemLevelServices = array_combine(array_column($systemLevelList, 'id'), $systemLevelList);
foreach ($list as &$item) {
$item['type'] = $systemLevelServices[$item['level']]['name'] ?? '暂无';
$item['add_time'] = $item['add_time'] && is_numeric($item['add_time']) ? date('Y-m-d H:i:s', $item['add_time']) : '';
}
$count = $this->dao->count(['spread_uid' => $uid]);
}
return compact('list', 'count');
}
}

View File

@@ -0,0 +1,70 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\dao\user\UserStoreOrderDao;
use think\annotation\Inject;
/**
*
* Class UserStoreOrderServices
* @package app\services\user
* @mixin UserStoreOrderDao
*/
class UserStoreOrderServices extends BaseServices
{
/**
* @var UserStoreOrderDao
*/
#[Inject]
protected UserStoreOrderDao $dao;
/**
* @author 等风来
* @email 136327134@qq.com
* @date 2023/9/18
* @param $uid
* @param string $orderBy
* @param string $keyword
* @param array $time
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserSpreadCountList($uid, $orderBy = '', $keyword = '', $time = [])
{
if ($orderBy === '') {
$orderBy = 'u.add_time desc';
}
$where = [];
$where[] = ['u.uid', 'IN', $uid];
if (strlen(trim($keyword))) {
$where[] = ['u.nickname|u.phone', 'LIKE', "%$keyword%"];
}
if ($time[0] || $time[1]) {
$where[] = ['spread_time', 'between', $time];
}
[$page, $limit] = $this->getPageValue();
$field = "u.uid,u.nickname,u.avatar,from_unixtime(u.add_time,'%Y/%m/%d') as time,u.spread_time,u.spread_count as childCount,p.orderCount,p.numberCount";
$list = $this->dao->getUserSpreadCountList($where, $field, $orderBy, $page, $limit);
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
foreach ($list as &$item) {
$item['childCount'] = count($userServices->getUserSpredadUids($item['uid'], 1)) ?? 0;
}
return $list;
}
}

View File

@@ -0,0 +1,53 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\dao\user\UserUserBrokerageDao;
use think\annotation\Inject;
/**
* 用户关联佣金
* Class UserUserBrokerageServices
* @package app\services\user
* @mixin UserUserBrokerageDao
*/
class UserUserBrokerageServices extends BaseServices
{
/**
* @var UserUserBrokerageDao
*/
#[Inject]
protected UserUserBrokerageDao $dao;
/**
* 获取佣金列表
* @param array $where
* @param string $field
* @param string $order
* @param int $limit
* @return array
*/
public function getBrokerageList(array $where, string $field = '*', string $order = '', int $limit = 0)
{
if ($limit) {
[$page] = $this->getPageValue();
} else {
[$page, $limit] = $this->getPageValue();
}
$list = $this->dao->getList($where, $field, $order, $page, $limit);
$count = $this->dao->getCount($where);
return [$count, $list];
}
}

View File

@@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\dao\user\UserVisitDao;
use think\annotation\Inject;
use think\facade\Log;
/**
*
* Class UserVisitServices
* @package app\services\user
* @mixin UserVisitDao
*/
class UserVisitServices extends BaseServices
{
/**
* @var UserVisitDao
*/
#[Inject]
protected UserVisitDao $dao;
/**
* 登录后记录访问记录
* @param array|object $user
* @return mixed
*/
public function loginSaveVisit($user)
{
try {
$data = [
'url' => '/pages/index/index',
'uid' => $user['uid'] ?? 0,
'ip' => request()->ip(),
'add_time' => time(),
'province' => $user['province'] ?? '',
'channel_type' => $user['user_type'] ?? 'h5'
];
if (!$data['uid']) {
return false;
}
return $this->dao->save($data);
} catch (\Throwable $e) {
Log::error('登录记录访问日志错误,错误原因:' . $e->getMessage());
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user;
use app\services\BaseServices;
use app\dao\user\UserWechatUserDao;
use think\annotation\Inject;
/**
* Class UserWechatuserServices
* @package app\services\user
* @mixin UserWechatUserDao
*/
class UserWechatuserServices extends BaseServices
{
/**
* @var UserWechatUserDao
*/
#[Inject]
protected UserWechatUserDao $dao;
/**
* 自定义简单查询总数
* @param array $where
* @return int
*/
public function getCount(array $where): int
{
return $this->dao->getCount($where);
}
/**
* 复杂条件搜索列表
* @param array $where
* @param string $field
* @return array
*/
public function getWhereUserList(array $where, string $field): array
{
[$page, $limit] = $this->getPageValue();
$order_string = '';
$order_arr = ['asc', 'desc'];
if (isset($where['now_money']) && in_array($where['now_money'], $order_arr)) {
$order_string = 'now_money ' . $where['now_money'];
}
$list = $this->dao->getListByModel($where, $field, $order_string, $page, $limit);
$count = $this->dao->getCountByWhere($where);
return [$list, $count];
}
}

View File

@@ -0,0 +1,161 @@
<?php
declare (strict_types=1);
namespace app\services\user\channel;
use app\dao\user\channel\ChannelMerchantIdentityDao;
use app\dao\user\UserBillDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\facade\Route as Url;
/**
* 采购商身份服务
* Class ChannelMerchantIdentityServices
* @package app\services\user\channel
* @mixin ChannelMerchantIdentityDao
*/
class ChannelMerchantIdentityServices extends BaseServices
{
/**
* @var ChannelMerchantIdentityDao
*/
#[Inject]
protected ChannelMerchantIdentityDao $dao;
/**
* 获取采购商身份列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 获取所有采购商身份
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getAllIdentities(array $where = [])
{
$where['is_del'] = 0;
return $this->dao->search($where,false)->column('name,id');
}
/**
* 添加采购商身份
* @param array $data
* @return bool
*/
public function add(array $data)
{
if ($this->dao->isNameExist($data['name'])) {
throw new AdminException('该身份名称已存在');
}
// if ($this->dao->isLevelExist($data['level'])) {
// throw new AdminException('该等级已存在');
// }
$data['add_time'] = time();
$res = $this->dao->save($data);
return $res != false;;
}
/**
* 编辑采购商身份
* @param int $id
* @param array $data
* @return bool
*/
public function edit(int $id, array $data)
{
if ($this->dao->isNameExist($data['name'], $id)) {
throw new AdminException('该身份名称已存在');
}
// if ($this->dao->isLevelExist($data['level'], $id)) {
// throw new AdminException('该等级已存在');
// }
$res = $this->dao->update($id, $data);
return $res != false;
}
/**
* 删除采购商身份
* @param int $id
* @return bool
*/
public function delete(int $id)
{
return $this->dao->delete($id);
}
/**
* 获取采购商身份表单
* @param array $data
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function getForm(array $data = [])
{
$f = [];
$f[] = Form::input('name', '身份名称:', $data['name'] ?? '')->required();
$f[] = Form::number('discount', '享受折扣:', $data['discount'] ?? 100)->info('采购商采购商品的折扣(1-100)例:95折输入95')->min(0)->max(100)->precision(0)->required();
$f[] = Form::radio('is_show', '是否显示:', $data['is_show'] ?? 1)->options([
['label' => '显示', 'value' => 1],
['label' => '隐藏', 'value' => 0],
]);
return $f;
}
/**
* 获取创建数据表单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createForm(int $id = 0)
{
if ($id) {
$info = $this->dao->get($id);
$info = $info ? $info->toArray() : [];
$action = Url::buildUrl('/channel/identity/' . $id)->build();
$title = '编辑采购商身份';
$method = 'PUT';
} else {
$info = [];
$action = Url::buildUrl('/channel/identity')->build();
$title = '添加采购商身份';
$method = 'POST';
}
return create_form($title, $this->getForm($info), $action, $method);
}
/**
* 设置状态
* @param int $id
* @param int $status
* @return \crmeb\basic\BaseModel
* User: liusl
* DateTime: 2025/3/27 上午9:59
*/
public function setStatus(int $id, int $status)
{
if (!$id) {
return $this->fail('参数错误');
}
return $this->dao->update($id, ['is_show' => $status]);
}
}

View File

@@ -0,0 +1,377 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user\channel;
use app\dao\user\channel\ChannelMerchantDao;
use app\services\BaseServices;
use app\services\order\StoreOrderServices;
use app\services\user\UserServices;
use crmeb\services\FormBuilder as Form;
use FormBuilder\Factory\Iview;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
/**
* 采购商服务类
* Class ChannelMerchantServices
* @package app\services\user\channel
* @mixin ChannelMerchantDao
*/
class ChannelMerchantServices extends BaseServices
{
/**
* @var ChannelMerchantDao
*/
#[Inject]
protected ChannelMerchantDao $dao;
/**
* 获取采购商信息
* @param int $id
* @return array|\think\Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author wuhaotian
* @email 442384644@qq.com
* @date 2025/5/10
*/
public function read(int $id)
{
$info = $this->dao->get($id);
if (!$info) {
return app('json')->fail('数据不存在');
}
$info = $info->toArray();
$info['avatar'] = app()->make(UserServices::class)->getUserCacheInfo($info['uid'])['avatar'] ?? '';
$orderData = app()->make(StoreOrderServices::class)->getOneData(['uid' => $info['uid'], 'channel' => 1, ['pid', '<=', 0], 'paid' => 1, 'is_del' => 0, 'is_system_del' => 0], 'COUNT(*) as order_count,SUM(pay_price) as order_price');
$info['order_count'] = $orderData['order_count'] ?? 0;
$info['order_price'] = $orderData['order_price'] ?? 0;
return $info;
}
/**
* 获取采购商列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getChannelList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getChannelList($where, $page, $limit);
$userService = app()->make(UserServices::class);
$identityService = app()->make(ChannelMerchantIdentityServices::class);
$identityIds = array_column($list, 'channel_type');
$identityList = $identityService->search(['id' => $identityIds, 'is_del' => 0])->column('name,discount', 'id');
$userIds = array_column($list, 'uid');
$userList = $userService->getColumn([['uid', 'in', $userIds]], 'uid,nickname,avatar,phone', 'uid');
foreach ($list as &$item) {
$item['identity_name'] = $identityList[$item['channel_type']]['name'] ?? '';
$item['identity_discount'] = $identityList[$item['channel_type']]['discount'] ?? '';
$item['user_nickname'] = $userList[$item['uid']]['nickname'] ?? '';
$item['user_avatar'] = $userList[$item['uid']]['avatar'] ?? '';
$item['user_phone'] = $userList[$item['uid']]['phone'] ?? '';
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 保存采购商数据
* @param array $data
* @return mixed
*/
public function saveChannel(array $data)
{
$data['add_time'] = time();
if ($this->dao->count(['uid' => $data['uid'], 'is_del' => 0])) {
throw new ValidateException('该用户已经是采购商了,无需重复添加!');
}
$res = $this->dao->save($data);
return $res && $this->updateUserChannelFlag($data['uid'], 1);
}
/**
* 修改采购商数据
* @param int $id
* @param array $data
* @return mixed
*/
public function updateChannel(int $id, array $data)
{
return $this->dao->update($id, $data);
}
/**
* 审核采购商
* @param int $id
* @param int $verifyStatus
* @param string $reason
* @param int $channelType
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function verifyChannel(int $id, int $verifyStatus, int $channelType = 0, string $reason = '')
{
$channel = $this->dao->getInfo($id);
if (!$channel) {
throw new ValidateException('采购商不存在');
}
if ($channel['verify_status'] == $verifyStatus) {
throw new ValidateException('请勿重复审核');
}
$updateData = [
'verify_status' => $verifyStatus,
'verify_time' => time(),
];
// 如果是审核通过,需要更新采购商身份和状态
if ($verifyStatus == 1) {
$updateData['channel_type'] = $channelType;
$updateData['status'] = 1; // 审核通过后自动开启
}
// 如果有备注,更新备注
if ($verifyStatus == 2) {
$updateData['reject'] = $reason;
}
$this->dao->update($id, $updateData);
// 审核通过后,更新用户表的采购商标识
if ($verifyStatus == 1) {
$this->updateUserChannelFlag($channel['uid'], 1);
}
return true;
}
/**
* 更新用户采购商标识
* @param int $uid 用户ID
* @param int $isChannel 采购商标识 0-否 1-是
* @return bool
*/
public function updateUserChannelFlag(int $uid, int $isChannel)
{
$userServices = app()->make(UserServices::class);
$userServices->update($uid, ['is_channel' => $isChannel]);
$userServices->cacheTag()->clear();
return true;
}
/**
* 创建审核表单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function createVerifyForm(int $id)
{
$channel = $this->dao->getInfo($id);
if (!$channel) {
throw new ValidateException('采购商不存在');
}
$identityService = app()->make(ChannelMerchantIdentityServices::class);
$identity = $identityService->getAllIdentities(['is_show' => 1]);
$options = [];
foreach ($identity as $v) {
$options[] = [
'label' => $v['name'],
'value' => $v['id']
];
}
// 创建表单
$form = [];
// 基本信息展示(只读)
$form[] = Form::hidden('id', $id);
$form[] = Form::input('channel_name', '采购商名称:', $channel['channel_name'])->disabled(true);
$form[] = Form::input('real_name', '联系人:', $channel['real_name'])->disabled(true);
$form[] = Form::input('phone', '联系电话:', $channel['phone'])->disabled(true);
// 审核状态选择
$form[] = Form::radio('verify_status', '审核结果:', 1)
->options([
['label' => '通过', 'value' => 1],
['label' => '拒绝', 'value' => 2]
])
->control([
[
'value' => 1,
'rule' => [
Form::select('channel_type', '采购商身份:', $channel['channel_type'] ?: 1)->setOptions($options)->filterable(true)->appendValidate(Iview::validateInt()->message('请选择采购商身份')->required())
]
],
[
'value' => 2,
'rule' => [
Form::textarea('reject', '拒绝原因:', '')
->placeholder('请填写拒绝原因')
->required('请填写拒绝原因')
]
]
]);
return create_form('采购商审核', $form, Url::buildUrl('/channel/merchant/verify'), 'POST');
}
/**
* 修改采购商状态
* @param int $id
* @param int $status
* @return mixed
*/
public function setStatus(int $id, int $status)
{
if (!$this->dao->getInfo($id)) {
throw new ValidateException('采购商不存在');
}
$this->updateUserChannelFlag($id, $status);
return $this->dao->update($id, ['status' => $status]);
}
/**
* 删除采购商
* @param int $id
* @return mixed
*/
public function deleteChannel(int $id)
{
if (!$info = $this->dao->getInfo($id)) {
throw new ValidateException('采购商不存在');
}
$this->updateUserChannelFlag($info['uid'], 0);
return $this->dao->update($id, ['is_del' => 1]);
}
/**
* 获取表单
* @param array $formData
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createForm(array $formData = [])
{
$id = $formData['id'] ?? 0;
$form = [];
if (!$formData) {
$form[] = Form::frameImage('image', '商城用户:', $this->url('admin/system.user/list', ['fodder' => 'image'], true))
->icon('ios-add')->width('960px')->height('550px')->modal(['footer-hide' => true])->Props(['srcKey' => 'image']);
$form[] = Form::hidden('uid', 0);
}
$form = $form + [
Form::hidden('id', $id),
Form::input('channel_name', '采购商名称:', $formData['channel_name'] ?? '')->required('请填写采购商名称'),
Form::input('real_name', '联系人:', $formData['real_name'] ?? '')->required('请填写联系人'),
Form::input('phone', '联系电话:', $formData['phone'] ?? '')->required('请填写联系电话'),
Form::cityArea('province', '所在地区:', $formData['province'] ?? '')->type('city_area')->info('请选择省市区')->required('请选择地区'),
Form::input('address', '详细地址:', $formData['address'] ?? '')->required('请填写详细地址'),
Form::radio('channel_type', '采购商身份:', $formData['channel_type'] ?? 1)->options([
['label' => '省级', 'value' => 1],
['label' => '市级', 'value' => 2],
['label' => '区级', 'value' => 3],
]),
Form::frameImages('certificate', '资质照片:', url('admin/widget.images/index', ['fodder' => 'certificate']), $formData['certificate'] ?? [])->maxLength(5),
Form::radio('status', '状态', $formData['status'] ?? 1)->options([
['label' => '开启', 'value' => 1],
['label' => '关闭', 'value' => 0],
]),
Form::textarea('admin_remark', '备注信息:', $formData['remark'] ?? ''),
];
return create_form($id ? '编辑采购商' : '添加采购商', $form, Url::buildUrl('/channel/merchant/save'), 'POST');
}
/**
* 申请成为采购商
* @param array $data
* @param array $userInfo
* @param int $id
* @return bool
*/
public function apply(array $data, int $id = 0)
{
// 检查是否有正在审核的申请
$existApply = $this->dao->getChannelInfo(['uid' => $data['uid'], 'verify_status' => 0]);
if ($existApply) {
throw new ValidateException('您有正在审核的申请,请耐心等待');
}
$data['province_ids'] = is_array($data['province_ids']) ? implode(',', $data['province_ids']) : '';
$channelData = [
'uid' => $data['uid'],
'channel_name' => $data['channel_name'] ?? '',
'real_name' => $data['real_name'],
'phone' => $data['phone'],
'province' => $data['province'] ?? '',
'province_ids' => $data['province_ids'],
'address' => $data['address'] ?? '',
'certificate' => $data['certificate'] ?? '',
'remark' => $data['remark'] ?? '',
'verify_status' => 0,
'status' => 0,
];
if ($id) {
$result = $this->dao->update($id, $channelData);
} else {
$channelData['add_time'] = time();
$result = $this->dao->save($channelData);
}
if (!$result) {
throw new ValidateException('申请提交失败');
}
return true;
}
/**
* 是否采购商
* @param $uid
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2025/4/8 下午3:07
*/
public function isChannel(int $uid, bool $is_discount = false)
{
if (!$uid) {
return false;
}
$info = $this->dao->getChannelInfo(['uid' => $uid, 'status' => 1, 'is_del' => 0]);
if (!$info) {
return false;
}
if ($is_discount) {
$iden_info = app()->make(ChannelMerchantIdentityServices::class)->search(['id' => $info['channel_type']])->find();
$discount = $iden_info['discount'] ?? 100;
$discount = bcmul($discount, 0.01, 2);
$iden_name = $iden_info['name'] ?? '';
$channel_type = $info['channel_type'];
return compact('iden_name', 'discount', 'channel_type');
}
return true;
}
}

View File

@@ -0,0 +1,154 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user\group;
use app\services\BaseServices;
use app\dao\user\group\UserGroupDao;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\facade\Route as Url;
/**
* 用户分组
* Class UserGroupServices
* @package app\services\user\group
* @mixin UserGroupDao
*/
class UserGroupServices extends BaseServices
{
/**
* @var UserGroupDao
*/
#[Inject]
protected UserGroupDao $dao;
/**
* 获取某一个分组
* @param int $id
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getGroup(int $id)
{
return $this->dao->get($id);
}
/**
* 获取分组列表
* @param string $field
* @param bool $is_page
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getGroupList(string $field = 'id,group_name', bool $is_page = false)
{
$page = $limit = 0;
if ($is_page) {
[$page, $limit] = $this->getPageValue();
$count = $this->dao->count([]);
}
$list = $this->dao->getList([], $field, $page, $limit);
return $is_page ? compact('list', 'count') : $list;
}
/**
* 获取一些用户的分组名称
* @param array $ids
* @return array
*/
public function getUsersGroupName(array $ids)
{
return $this->dao->getColumn([['id', 'IN', $ids]], 'group_name', 'id');
}
/**
* 添加/修改分组页面
* @param int $id
* @return string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function add(int $id)
{
$group = $this->getGroup($id);
$field = array();
if (!$group) {
$title = '添加分组';
$field[] = Form::input('group_name', '分组名称:', '')->maxlength(20)->required();
} else {
$title = '修改分组';
$field[] = Form::hidden('id', $id);
$field[] = Form::input('group_name', '分组名称:', $group->getData('group_name'))->maxlength(20)->required();
}
return create_form($title, $field, Url::buildUrl('/user/user_group/save'), 'POST');
}
/**
* 添加|修改
* @param int $id
* @param array $data
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function save(int $id, array $data)
{
$groupName = $this->dao->getOne(['group_name' => $data['group_name']]);
if ($id) {
if (!$this->getGroup($id)) {
throw new AdminException('数据不存在');
}
if ($groupName && $id != $groupName['id']) {
throw new AdminException('该分组已经存在');
}
if ($this->dao->update($id, $data)) {
return true;
} else {
throw new AdminException('修改失败或者您没有修改什么!');
}
} else {
unset($data['id']);
if ($groupName) {
throw new AdminException('该分组已经存在');
}
if ($this->dao->save($data)) {
return true;
} else {
throw new AdminException('添加失败!');
}
}
}
/**
* 删除
* @param int $id
*/
public function delGroup(int $id)
{
if ($this->getGroup($id)) {
if (!$this->dao->delete($id)) {
throw new AdminException('删除失败,请稍候再试!');
}
}
return '删除成功!';
}
}

View File

@@ -0,0 +1,263 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user\label;
use app\services\BaseServices;
use app\services\user\UserServices;
use app\jobs\user\UserAutoLabelJob;
use think\facade\Log;
use crmeb\traits\ServicesTrait;
/**
* 用户标签批量处理服务
* Class UserLabelBatchProcessServices
* @package app\services\user\label
*/
class UserLabelBatchProcessServices extends BaseServices
{
use ServicesTrait;
/**
* 批量大小
*/
const BATCH_SIZE = 1000;
/**
* 处理标签变更后的用户批量检查
* @param array $labelIds 标签ID数组
* @param string $action 操作类型:create(新建) 或 update(修改)
* @return bool
*/
public function processLabelChange(array $labelIds, string $action = 'update')
{
try {
Log::info('开始处理标签变更批量用户检查:' . var_export([
'label_ids' => $labelIds,
'action' => $action
], true));
// 获取所有用户ID
$userIds = $this->getAllUserIds();
if (empty($userIds)) {
Log::info('没有找到用户,跳过处理');
return true;
}
// 初始化处理状态缓存
$cacheKey = "label_batch_process_" . implode('_', $labelIds);
$statusData = [
'label_ids' => $labelIds,
'status' => 'processing',
'processed_count' => 0,
'total_count' => count($userIds),
'start_time' => date('Y-m-d H:i:s'),
'end_time' => null,
'message' => '正在处理中...'
];
cache($cacheKey, $statusData, 3600); // 缓存1小时
// 分批处理用户
$batches = array_chunk($userIds, self::BATCH_SIZE);
$totalBatches = count($batches);
Log::info('开始分批处理用户:' . var_export([
'total_users' => count($userIds),
'batch_size' => self::BATCH_SIZE,
'total_batches' => $totalBatches
], true));
$processedCount = 0;
foreach ($batches as $index => $batchUserIds) {
$this->processBatchUsers($batchUserIds, $labelIds, $action, $index + 1, $totalBatches);
$processedCount += count($batchUserIds);
// 更新处理状态
$statusData['processed_count'] = $processedCount;
$statusData['message'] = "已处理 {$processedCount}/{$statusData['total_count']} 个用户";
cache($cacheKey, $statusData, 3600);
}
// 更新最终状态
$statusData['status'] = 'completed';
$statusData['end_time'] = date('Y-m-d H:i:s');
$statusData['message'] = '处理完成';
cache($cacheKey, $statusData, 3600);
Log::info('标签变更批量用户检查处理完成:' . var_export([
'label_ids' => $labelIds,
'total_users' => count($userIds)
], true));
return true;
} catch (\Throwable $e) {
// 更新错误状态
$cacheKey = "label_batch_process_" . implode('_', $labelIds);
$errorStatus = [
'label_ids' => $labelIds,
'status' => 'error',
'processed_count' => 0,
'total_count' => 0,
'start_time' => date('Y-m-d H:i:s'),
'end_time' => date('Y-m-d H:i:s'),
'message' => '处理失败:' . $e->getMessage()
];
cache($cacheKey, $errorStatus, 3600);
Log::error('标签变更批量用户检查处理失败:' . var_export([
'label_ids' => $labelIds,
'action' => $action,
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
], true));
return false;
}
}
/**
* 获取所有用户ID
* @return array
*/
protected function getAllUserIds(): array
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
// 获取所有启用的用户ID
$where = [
'status' => 1, // 启用状态
'is_del' => 0 // 未删除
];
// 分批获取用户ID,避免内存溢出
$userIds = [];
$page = 1;
$limit = 5000; // 每次获取5000个用户ID
do {
$users = $userServices->getList($where, 'uid', $page, $limit);
$batchIds = array_column($users, 'uid');
$userIds = array_merge($userIds, $batchIds);
$page++;
} while (count($batchIds) === $limit);
return array_unique($userIds);
}
/**
* 处理批次用户
* @param array $userIds 用户ID数组
* @param array $labelIds 标签ID数组
* @param string $action 操作类型
* @param int $batchIndex 当前批次索引
* @param int $totalBatches 总批次数
*/
protected function processBatchUsers(array $userIds, array $labelIds, string $action, int $batchIndex, int $totalBatches)
{
try {
Log::info('处理用户批次:' . var_export([
'batch_index' => $batchIndex,
'total_batches' => $totalBatches,
'user_ids' => $userIds,
'user_count' => count($userIds),
'label_ids' => $labelIds
], true));
// 批量分发自动标签检查任务,每100个用户一个任务
$batchSize = 100;
$userBatches = array_chunk($userIds, $batchSize);
foreach ($userBatches as $userBatch) {
// 分发批量处理任务,传入用户数组而不是单个用户
UserAutoLabelJob::dispatch([
$userBatch,
'label_change', // 触发类型:标签变更
[
'action' => $action,
'changed_labels' => $labelIds,
'timestamp' => time()
],
$labelIds // 只检查变更的标签
]);
}
Log::info('批次用户处理完成:' . var_export([
'batch_index' => $batchIndex,
'user_batches' => count($userBatches),
'dispatched_jobs' => count($userBatches),
'total_users' => count($userIds)
], true));
} catch (\Throwable $e) {
Log::error('处理用户批次失败:' . var_export([
'batch_index' => $batchIndex,
'user_count' => count($userIds),
'error' => $e->getMessage()
], true));
}
}
/**
* 处理单个标签的用户检查
* @param int $labelId 标签ID
* @param string $action 操作类型
* @return bool
*/
public function processSingleLabel(int $labelId, string $action = 'update')
{
return $this->processLabelChange([$labelId], $action);
}
/**
* 获取批量处理状态
* @param int $labelId
* @return array
*/
public function getBatchProcessStatus(int $labelId): array
{
try {
// 从缓存中获取处理状态
$cacheKey = "label_batch_process_{$labelId}";
$status = cache($cacheKey);
if (!$status) {
return [
'label_id' => $labelId,
'status' => 'not_started',
'processed_count' => 0,
'total_count' => 0,
'start_time' => null,
'end_time' => null,
'message' => '未找到处理记录'
];
}
return $status;
} catch (\Exception $e) {
Log::error('获取批量处理状态失败:' . var_export([
'label_id' => $labelId,
'error' => $e->getMessage()
], true));
return [
'label_id' => $labelId,
'status' => 'error',
'processed_count' => 0,
'total_count' => 0,
'start_time' => null,
'end_time' => null,
'message' => '获取状态失败:' . $e->getMessage()
];
}
}
}

View File

@@ -0,0 +1,175 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user\label;
use app\dao\other\CategoryDao;
use app\services\BaseServices;
use crmeb\services\CacheService;
use crmeb\services\FormBuilder;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 用户标签分类
* Class UserLabelCateServices
* @package app\services\user\label
* @mixin CategoryDao
*/
class UserLabelCateServices extends BaseServices
{
/**
* 标签分类缓存
* @var string
*/
protected string $cacheName = 'label_list_all';
/**
* @var CategoryDao
*/
#[Inject]
protected CategoryDao $dao;
/**
* 获取标签分类
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLabelList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getCateList($where, $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 获取tree数据
* @param array $where
* @param array|string[] $field
* @param array $with
* @return array
*/
public function getLabelTree(array $where, array $field = ['*'], array $with = [])
{
return $this->dao->getCateList($where, 0, 0, $field, $with);
}
/**
* 删除分类缓存
* @param int $type
* @param int $relation_id
* @return bool
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function deleteCateCache(int $type = 0, int $relation_id = 0)
{
$key = $this->cacheName . '_' . $type . '_' . $relation_id;
return CacheService::delete($key);
}
/**
* 获取标签全部分类
* @param int $type
* @param int $relation_id
* @return bool|mixed|null
*/
public function getLabelCateAll(int $type = 0, int $relation_id = 0)
{
$key = $this->cacheName . '_' . $type . '_' . $relation_id;
return CacheService::get($key, function () use ($type, $relation_id) {
return $this->dao->getCateList(['type' => $type, 'owner_id' => 0, 'relation_id' => $relation_id, 'group' => 0]);
});
}
/**
* 标签分类表单
* @param array $cataData
* @return mixed
*/
public function labelCateForm(array $cataData = [])
{
$f[] = FormBuilder::input('name', '分类名称:', $cataData['name'] ?? '')->maxlength(20)->required();
$f[] = FormBuilder::number('sort', '排序:', (int)($cataData['sort'] ?? 0))->min(0);
return $f;
}
/**
* 创建表单
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createForm()
{
return create_form('添加标签分类', $this->labelCateForm(), $this->url('/user/user_label_cate'), 'POST');
}
/**
* 修改分类标签表单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateForm(int $id)
{
$labelCate = $this->dao->get($id);
if (!$labelCate) {
throw new ValidateException('分类标签没有查到');
}
return create_form('编辑标签分类', $this->labelCateForm($labelCate->toArray()), $this->url('user/user_label_cate/' . $id), 'PUT');
}
/**
* 用户标签列表
* @param int $uid
* @param int $type
* @param int $relation_id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserLabel(int $uid = 0, int $type = 0, int $relation_id = 0)
{
$list = $this->dao->getAll(['group' => 0, 'type' => $type, 'relation_id' => $relation_id], ['label']);
$labelIds = [];
if ($uid) {
/** @var UserLabelRelationServices $services */
$services = app()->make(UserLabelRelationServices::class);
$labelIds = $services->getUserLabels($uid, $type, $relation_id);
}
foreach ($list as $key => &$item) {
if (is_array($item['label'])) {
if (!$item['label']) {
unset($list[$key]);
continue;
}
foreach ($item['label'] as &$value) {
if (in_array($value['id'], $labelIds)) {
$value['disabled'] = true;
} else {
$value['disabled'] = false;
}
}
}
}
return array_merge($list);
}
}

View File

@@ -0,0 +1,250 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user\label;
use app\services\BaseServices;
use app\dao\user\label\UserLabelExtendRelationDao;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use FormBuilder\Factory\Iview;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 标签规则扩展表
* Class UserLabelExtendRelationServices
* @package app\services\user\label
* @mixin UserLabelExtendRelationDao
*/
class UserLabelExtendRelationServices extends BaseServices
{
/**
* @var UserLabelExtendRelationDao
*/
#[Inject]
protected UserLabelExtendRelationDao $dao;
/**
* 获取某一条标签扩展关联关系
* @param $id
* @return array|\think\Model|null
*/
public function getLabelExtendRelation($id)
{
return $this->dao->get($id);
}
/**
* 获取所有标签扩展关联关系
* @param array $where
* @param array|string[] $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLabelExtendRelationList(array $where = [], array $field = ['*'])
{
return $this->dao->getList(0, 0, $where, $field);
}
/**
* 获取列表
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($page, $limit, $where);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 根据左侧ID获取关联关系
* @param int $leftId
* @param int $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getByLeftId(int $leftId, int $type = 0): array
{
return $this->dao->getByLeftId($leftId, $type);
}
/**
* 根据右侧ID获取关联关系
* @param int $rightId
* @param int $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getByRightId(int $rightId, int $type = 0): array
{
return $this->dao->getByRightId($rightId, $type);
}
/**
* 根据类型获取关联关系
* @param int $type
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getByType(int $type, array $where = []): array
{
return $this->dao->getByType($type, $where);
}
/**
* 添加修改标签扩展关联关系表单
* @param int $id
* @param int $leftId
* @return mixed
*/
public function add(int $id, int $leftId = 0)
{
$labelExtendRelation = $this->getLabelExtendRelation($id);
$field = array();
// 类型选项
$typeOptions = [
['value' => 1, 'label' => '商品'],
['value' => 2, 'label' => '分类'],
['value' => 3, 'label' => '标签']
];
/** @var UserLabelExtendServices $labelExtendService */
$labelExtendService = app()->make(UserLabelExtendServices::class);
$labelExtendOptions = [];
foreach ($labelExtendService->getLabelExtendList() as $item) {
$labelExtendOptions[] = ['value' => $item['id'], 'label' => '标签扩展规则-' . $item['id']];
}
if (!$labelExtendRelation) {
$title = '添加标签扩展关联关系';
$field[] = Form::select('left_id', '关联标签扩展', $leftId)->setOptions($labelExtendOptions)->filterable(true)->appendValidate(Iview::validateInt()->message('请选择关联标签扩展')->required());
} else {
$title = '编辑标签扩展关联关系';
$field[] = Form::select('left_id', '关联标签扩展', $labelExtendRelation['left_id'])->setOptions($labelExtendOptions)->filterable(true)->appendValidate(Iview::validateInt()->message('请选择关联标签扩展')->required());
}
$field[] = Form::select('type', '类型', $labelExtendRelation['type'] ?? 0)->setOptions($typeOptions)->appendValidate(Iview::validateInt()->message('请选择类型')->required());
$field[] = Form::number('right_id', '关联ID', $labelExtendRelation['right_id'] ?? 0)->appendValidate(Iview::validateInt()->message('请输入关联ID')->required());
return create_form($title, $field, '/admin/user/label_extend_relation/save/' . $id, 'POST');
}
/**
* 保存标签扩展关联关系
* @param int $id
* @param array $data
* @return mixed
*/
public function save(int $id, array $data)
{
if ($id) {
$res = $this->dao->update($id, $data);
if (!$res) throw new AdminException('修改失败');
} else {
$res = $this->dao->save($data);
if (!$res) throw new AdminException('添加失败');
}
return $res;
}
/**
* 删除标签扩展关联关系
* @param int $id
* @return bool
*/
public function del(int $id)
{
$labelExtendRelation = $this->getLabelExtendRelation($id);
if (!$labelExtendRelation) {
throw new AdminException('标签扩展关联关系不存在');
}
return $this->dao->delete($id);
}
/**
* 批量保存关联关系
* @param int $leftId
* @param int $type
* @param array $rightIds
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function saveBatch(int $leftId, int $type, array $rightIds): bool
{
// 先删除原有关联关系
$this->dao->deleteByLeftId($leftId, $type);
// 批量保存新的关联关系
if (!empty($rightIds)) {
$data = [];
foreach ($rightIds as $rightId) {
$data[] = [
'type' => $type,
'left_id' => $leftId,
'right_id' => $rightId
];
}
return $this->dao->saveAll($data);
}
return true;
}
/**
* 获取关联的右侧ID列表
* @param int $leftId
* @param int $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getRightIds(int $leftId, int $type = 0): array
{
$list = $this->getByLeftId($leftId, $type);
return array_column($list, 'right_id');
}
/**
* 获取关联的左侧ID列表
* @param int $rightId
* @param int $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLeftIds(int $rightId, int $type = 0): array
{
$list = $this->getByRightId($rightId, $type);
return array_column($list, 'left_id');
}
}

View File

@@ -0,0 +1,242 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user\label;
use app\services\BaseServices;
use app\dao\user\label\UserLabelExtendDao;
use app\services\product\category\StoreProductCategoryServices;
use app\services\product\label\StoreProductLabelServices;
use app\services\product\product\StoreProductServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use FormBuilder\Factory\Iview;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 标签规则关联表
* Class UserLabelExtendServices
* @package app\services\user\label
* @mixin UserLabelExtendDao
*/
class UserLabelExtendServices extends BaseServices
{
/**
* @var UserLabelExtendDao
*/
#[Inject]
protected UserLabelExtendDao $dao;
/**
* 获取某一条标签扩展规则
* @param $id
* @return array|\think\Model|null
*/
public function getLabelExtend($id)
{
return $this->dao->get($id);
}
/**
* 获取所有标签扩展规则
* @param array $where
* @param array|string[] $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLabelExtendList(array $where = [], array $field = ['*'])
{
return $this->dao->getList(0, 0, $where, $field);
}
/**
* 获取列表
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($page, $limit, $where);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 根据标签ID获取扩展规则
* @param int $labelId
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getByLabelId(int $labelId): array
{
return $this->dao->getByLabelId($labelId);
}
/**
* 根据规则类型获取扩展规则
* @param int $ruleType
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getByRuleType(int $ruleType, array $where = []): array
{
return $this->dao->getByRuleType($ruleType, $where);
}
/**
* 添加修改标签扩展规则表单
* @param int $id
* @param int $labelId
* @return mixed
*/
public function add(int $id, int $labelId = 0)
{
$labelExtend = $this->getLabelExtend($id);
$field = array();
/** @var UserLabelServices $labelService */
$labelService = app()->make(UserLabelServices::class);
$labelOptions = [];
foreach ($labelService->getLabelList() as $item) {
$labelOptions[] = ['value' => $item['id'], 'label' => $item['label_name']];
}
// 规则类型选项
$ruleTypeOptions = [
['value' => 1, 'label' => '商品'],
['value' => 2, 'label' => '资产'],
['value' => 3, 'label' => '交易'],
['value' => 4, 'label' => '客户(所有)']
];
// 规则子类型选项
$subTypeOptions = [
['value' => 1, 'label' => '积分'],
['value' => 2, 'label' => '余额']
];
// 余额类型选项
$balanceTypeOptions = [
['value' => 1, 'label' => '充值'],
['value' => 2, 'label' => '消耗']
];
// 次数/金额选项
$operationTypeOptions = [
['value' => 1, 'label' => '次数'],
['value' => 2, 'label' => '金额']
];
// 时间维度选项
$timeDimensionOptions = [
['value' => 1, 'label' => '历史'],
['value' => 2, 'label' => '最近'],
['value' => 3, 'label' => '累计']
];
// 商品维度选项
$specifyDimensionOptions = [
['value' => 1, 'label' => '商品'],
['value' => 2, 'label' => '分类'],
['value' => 3, 'label' => '标签']
];
// 客户身份选项
$customerIdentityOptions = [
['value' => 1, 'label' => '注册时间'],
['value' => 2, 'label' => '访问时间'],
['value' => 3, 'label' => '用户等级'],
['value' => 4, 'label' => '客户身份']
];
if (!$labelExtend) {
$title = '添加标签扩展规则';
$field[] = Form::select('label_id', '关联标签', $labelId)->setOptions($labelOptions)->filterable(true)->appendValidate(Iview::validateInt()->message('请选择关联标签')->required());
} else {
$title = '编辑标签扩展规则';
$field[] = Form::select('label_id', '关联标签', $labelExtend['label_id'])->setOptions($labelOptions)->filterable(true)->appendValidate(Iview::validateInt()->message('请选择关联标签')->required());
}
$field[] = Form::select('rule_type', '规则类型', $labelExtend['rule_type'] ?? 0)->setOptions($ruleTypeOptions)->appendValidate(Iview::validateInt()->message('请选择规则类型')->required());
$field[] = Form::select('sub_type', '规则子类型', $labelExtend['sub_type'] ?? 0)->setOptions($subTypeOptions);
$field[] = Form::select('balance_type', '余额类型', $labelExtend['balance_type'] ?? 0)->setOptions($balanceTypeOptions);
$field[] = Form::select('operation_type', '次数/金额', $labelExtend['operation_type'] ?? 0)->setOptions($operationTypeOptions);
$field[] = Form::select('time_dimension', '时间维度', $labelExtend['time_dimension'] ?? 0)->setOptions($timeDimensionOptions);
$field[] = Form::number('time_value', '时间值(天)', $labelExtend['time_value'] ?? 0);
$field[] = Form::select('specify_dimension', '商品维度', $labelExtend['specify_dimension'] ?? 0)->setOptions($specifyDimensionOptions);
$field[] = Form::number('amount_value_min', '金额/数值最小', $labelExtend['amount_value_min'] ?? 0)->precision(2);
$field[] = Form::number('amount_value_max', '金额/数值最大', $labelExtend['amount_value_max'] ?? 0)->precision(2);
$field[] = Form::number('operation_times_min', '操作次数最小', $labelExtend['operation_times_min'] ?? 0);
$field[] = Form::number('operation_times_max', '操作次数最大', $labelExtend['operation_times_max'] ?? 0);
$field[] = Form::select('customer_identity', '客户身份', $labelExtend['customer_identity'] ?? 0)->setOptions($customerIdentityOptions);
$field[] = Form::number('customer_num', '客户身份数据', $labelExtend['customer_num'] ?? 0);
return create_form($title, $field, '/admin/user/label_extend/save/' . $id, 'POST');
}
/**
* 删除标签扩展规则
* @param int $id
* @return bool
*/
public function del(int $id)
{
$labelExtend = $this->getLabelExtend($id);
if (!$labelExtend) {
throw new AdminException('标签扩展规则不存在');
}
return $this->dao->delete($id);
}
/**
* 根据商品维度获取商品/分类/标签信息
* @param int $id 标签扩展ID
* @return array
*/
public function getLabelSpecify(int $id)
{
$labelExtend = $this->getLabelExtend($id);
if (!$labelExtend) {
return [];
}
$product_ids = $labelExtend['product_ids'] ? explode(',', $labelExtend['product_ids']) : [];
$data = [];
switch ($labelExtend['specify_dimension']) {
case 1:
//商品
$data = app()->make(StoreProductServices::class)->search([])->whereIn('id', $product_ids)->column('id,store_name,image');
break;
case 2:
//分类
$data = app()->make(StoreProductCategoryServices::class)->search([])->whereIn('id', $product_ids)->column('id,cate_name');
break;
case 3:
//标签
$data = app()->make(StoreProductLabelServices::class)->search([])->whereIn('id', $product_ids)->column('id,label_name');
break;
}
return $data;
}
}

View File

@@ -0,0 +1,174 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user\label;
use app\services\BaseServices;
use app\dao\user\label\UserLabelRelationDao;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
/**
* 用户关联标签
* Class UserLabelRelationServices
* @package app\services\user\label
* @mixin UserLabelRelationDao
*/
class UserLabelRelationServices extends BaseServices
{
/**
* @var UserLabelRelationDao
*/
#[Inject]
protected UserLabelRelationDao $dao;
/**
* 获取某个用户标签ids
* @param int $uid
* @param int $type
* @param int $relation_id
* @return array
*/
public function getUserLabels(int $uid, int $type = 0, int $relation_id = 0)
{
return $this->dao->getColumn(['uid' => $uid, 'type' => $type, 'relation_id' => $relation_id], 'label_id', '');
}
/**
* 用户设置标签
* @param $uids
* @param array $labels
* @param int $type
* @param int $relation_id
* @param bool $group
* @return bool
*/
public function setUserLable($uids, array $labels, int $type = 0, int $relation_id = 0, bool $group = false, $label_type = 0)
{
if (!$uids) {
return true;
}
if (!is_array($uids)) {
$uids = [$uids];
}
//增加标签
$data = [];
foreach ($uids as $uid) {
if ($label_type == 0) {
//用户已经存在的标签
$user_label_ids = $this->dao->delete([
['uid', '=', $uid],
['type', '=', $type],
['relation_id', '=', $relation_id]
]);
if ($labels) {
foreach ($labels as $label) {
$label = (int)$label;
if (!$label) continue;
$data[] = ['uid' => $uid, 'label_id' => $label, 'type' => $type, 'relation_id' => $relation_id];
}
}
} elseif ($label_type == 1) {
foreach ($labels as $label) {
$label = (int)$label;
if (!$label) continue;
$data[] = ['uid' => $uid, 'label_id' => $label, 'type' => $type, 'relation_id' => $relation_id];
}
} else {
$this->dao->delete([
['uid', '=', $uid],
['label_id', 'in', $labels],
['type', '=', $type],
['relation_id', '=', $relation_id]
]);
}
}
if ($data) {
if (!$this->dao->saveAll($data))
throw new AdminException('设置标签失败');
}
return true;
}
/**
* 取消用户标签
* @param int $uid
* @param array $labels
* @param int $type
* @param int $relation_id
* @return bool
*/
public function unUserLabel(int $uid, array $labels = [], int $type = 0, int $relation_id = 0)
{
$where = [
['uid', '=', $uid],
['type', '=', $type],
['relation_id', '=', $relation_id]
];
//不传入 清空用户所有标签
if (count($labels)) {
$where[] = ['label_id', 'in', $labels];
}
$this->dao->delete($where);
return true;
}
/**
* 获取用户标签
* @param array $uids
* @param int $type
* @param int $relation_id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUserLabelList(array $uids, int $type = 0, int $relation_id = 0)
{
return $this->dao->getLabelList($uids, $type, $relation_id);
}
/**
* 批量获取用户标签关系映射
* @param array $uids 用户ID数组
* @param int $type 类型
* @param int $relation_id 关联ID
* @return array 返回格式:[uid => [label_id1, label_id2, ...]]
*/
public function getUserLabelsMap(array $uids, int $type = 0, int $relation_id = 0): array
{
if (empty($uids)) {
return [];
}
$where = [
'uid' => $uids,
'type' => $type,
'relation_id' => $relation_id
];
$relations = $this->dao->search($where)->field(['uid', 'label_id'])->select()->toArray();
$userLabelsMap = [];
foreach ($uids as $uid) {
$userLabelsMap[$uid] = [];
}
foreach ($relations as $relation) {
$userLabelsMap[$relation['uid']][] = $relation['label_id'];
}
return $userLabelsMap;
}
}

View File

@@ -0,0 +1,554 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user\label;
use app\jobs\user\UserLabelJob;
use app\services\BaseServices;
use app\dao\user\label\UserLabelDao;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use crmeb\services\wechat\Work;
use FormBuilder\Factory\Iview;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
/**
* 用户标签
* Class UserLabelServices
* @package app\services\user\label
* @mixin UserLabelDao
*/
class UserLabelServices extends BaseServices
{
/**
* @var UserLabelDao
*/
#[Inject]
protected UserLabelDao $dao;
/**
* 获取某一本标签
* @param $id
* @return array|\think\Model|null
*/
public function getLable($id)
{
return $this->dao->get($id);
}
/**
* 获取所有用户标签
* @param array $where
* @param array|string[] $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLabelList(array $where = [], array $field = ['*'])
{
return $this->dao->getList(0, 0, $where, $field);
}
/**
* 获取列表
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($page, $limit, $where, ['*'], ['category']);
$ids = array_column($list, 'id');
$userCount = app()->make(UserLabelRelationServices::class)->getLabelUserCount($ids ?: []);
$count = $this->dao->count($where);
foreach ($list as &$item) {
$item['user_count'] = $userCount[$item['id']] ?? 0;
}
return compact('list', 'count');
}
/**
* 添加修改标签表单
* @param int $id
* @param int $type
* @param int $relation_id
* @param int $label_cate
* @return mixed
*/
public function add(int $id, int $type = 0, int $relation_id = 0, int $label_cate = 0)
{
$label = $this->getLable($id);
$field = array();
/** @var UserLabelCateServices $service */
$service = app()->make(UserLabelCateServices::class);
$options = [];
foreach ($service->getLabelCateAll($type, $relation_id) as $item) {
$options[] = ['value' => $item['id'], 'label' => $item['name']];
}
if (!$label) {
$title = '添加标签';
$field[] = Form::select('label_cate', '标签分类', $label_cate)->setOptions($options)->filterable(true)->appendValidate(Iview::validateInt()->message('请选择标签分类')->required());
$field[] = Form::input('label_name', '标签名称', '')->maxlength(20)->required('请填写标签名称');
} else {
$title = '修改标签';
$field[] = Form::select('label_cate', '分类', (int)$label->getData('label_cate'))->setOptions($options)->filterable(true)->appendValidate(Iview::validateInt()->message('请选择标签分类')->required());
$field[] = Form::hidden('id', $label->getData('id'));
$field[] = Form::input('label_name', '标签名称', $label->getData('label_name'))->maxlength(20)->required('请填写标签名称');
}
return create_form($title, $field, Url::buildUrl('/user/user_label/save'), 'POST');
}
/**
* 保存标签表单数据
* @param int $id
* @param array $data
* @param int $type
* @param int $relation_id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function saveData(int $id, array $data, int $type = 0, int $relation_id = 0)
{
if (!$data['label_cate']) {
throw new ValidateException('请选择标签分类');
}
$processedIds = [];
$action = $id ? 'update' : 'create';
$this->transaction(function () use ($id, $data, $type, $relation_id, &$processedIds) {
$data['type'] = $type;
$data['relation_id'] = $relation_id;
unset($data['id']);
if ($id) {
if (!$this->getLable($id)) {
throw new AdminException('数据不存在');
}
$this->dao->update($id, $data);
//删除旧标签
app()->make(UserLabelExtendServices::class)->searchDel(['label_id' => $id]);
app()->make(UserLabelExtendRelationServices::class)->search(['left_id' => $id])->delete();
$processedIds = [$id];
} else {
//组合标签数据
$processedIds = [];
foreach ($data['label_name'] as $key => $item) {
$allLabelData = [
'label_name' => $item,
'label_cate' => $data['label_cate'],
'type' => $data['type'],
'relation_id' => $data['relation_id'],
'label_type' => $data['label_type'],
'is_product' => $data['is_product'],
'is_property' => $data['is_property'],
'is_trade' => $data['is_trade'],
'is_customer' => $data['is_customer'],
'is_condition' => $data['is_condition'],
'status' => $data['status'],
'add_time' => time(),
];
$processedIds[] = $this->dao->save($allLabelData)->id;
}
}
//批量保存
$this->saveExtend($processedIds, $data);
return true;
});
// 如果是自动标签,触发批量用户处理
if (!empty($processedIds) && isset($data['label_type']) && $data['label_type'] == 2) {
try {
/** @var UserLabelBatchProcessServices $batchProcessServices */
$batchProcessServices = app()->make(UserLabelBatchProcessServices::class);
$batchProcessServices->processLabelChange($processedIds, $action);
} catch (\Throwable $e) {
// 记录错误但不影响标签保存
\think\facade\Log::error('触发标签批量处理失败', [
'label_ids' => $processedIds,
'action' => $action,
'error' => $e->getMessage()
]);
}
}
return true;
}
/**
* 获取标签详情
* @param int $id 标签ID
* @return array
* @throws AdminException
*/
public function getInfo($id)
{
$info = $this->getLable($id);
if (!$info) {
throw new AdminException('数据不存在');
}
$info = $info->toArray();
$userLabelExtendServices = app()->make(UserLabelExtendServices::class);
$extendList = $userLabelExtendServices->getLabelExtendList(['label_id' => $id]);
// getLabelSpecify
$specifyId = 0;
foreach ($extendList as &$item) {
switch ($item['rule_type']) {
case 1:
$specifyId = $item['id'];
$product_ids = $item['product_ids'] ? explode(',', $item['product_ids']) : [];
$info['product'] = [
'specify_dimension' => $item['specify_dimension'],
'time_dimension' => $item['time_dimension'],
'time_value' => $item['time_value'],
'ids' => $product_ids,
];
break;
case 2:
$info['property']['property_rule'][] = $item;
break;
case 3:
$info['trade']['trade_rule'][] = $item;
break;
case 4:
if ($item['customer_time_start']) {
$item['customer_time_start'] = date('Y-m-d', $item['customer_time_start']);
}
if ($item['customer_time_end']) {
$item['customer_time_end'] = date('Y-m-d', $item['customer_time_end']);
}
$item['time_val'] = [$item['customer_time_start'], $item['customer_time_end']];
$info['customer']['customer_rule'][] = $item;
break;
}
}
$info['specifyData'] = [];
if ($specifyId) {
$info['specifyData'] = $userLabelExtendServices->getLabelSpecify($specifyId);
}
return $info;
}
/**
* 保存标签扩展规则
* @param array $ids 标签ID数组
* @param array $data 扩展规则数据
* @return bool
*/
public function saveExtend(array $ids, array $data)
{
if (count($ids) == 0 || count($data) == 0) {
return true;
}
$extendData = [];
$relationData = [];
foreach ($ids as $id) {
//组合商品数据
if ($data['is_product'] == 1) {
$product = $data['product'];
foreach ($product['ids'] as $productId) {
$relationData[] = [
'type' => $product['specify_dimension'],
'left_id' => $id,
'right_id' => $productId,
];
}
$extendData[] = [
'label_id' => $id,
'rule_type' => 1,
'time_dimension' => $product['time_dimension'],
'time_value' => $product['time_value'],
'specify_dimension' => $product['specify_dimension'],
'product_ids' => $product['ids'] ? implode(',', $product['ids']) : '',
];
}
//资产
if ($data['is_property'] == 1) {
$property = $data['property'];
foreach ($property['property_rule'] as $item) {
$extendData[] = [
'label_id' => $id,
'rule_type' => 2,
'sub_type' => $item['sub_type'],
'balance_type' => $item['balance_type'],
'operation_type' => $item['operation_type'],
'time_dimension' => $item['time_dimension'],
'amount_value_max' => $item['amount_value_max'],
'amount_value_min' => $item['amount_value_min'],
'operation_times_max' => $item['operation_times_max'],
'operation_times_min' => $item['operation_times_min'],
];
}
}
//交易
if ($data['is_trade'] == 1) {
$trade_rule = $data['trade']['trade_rule'];
foreach ($trade_rule as $item) {
$extendData[] = [
'label_id' => $id,
'rule_type' => 3,
'amount_value_max' => $item['amount_value_max'],
'amount_value_min' => $item['amount_value_min'],
'operation_times_max' => $item['operation_times_max'],
'operation_times_min' => $item['operation_times_min'],
'operation_type' => $item['operation_type'],
'time_dimension' => $item['time_dimension'],
];
}
}
//客户
if ($data['is_customer'] == 1) {
$customer_rule = $data['customer']['customer_rule'];
foreach ($customer_rule as $item) {
$time_val = $item['time_val'];
$customer_time_start = isset($item['time_val'][0]) && $item['time_val'][0] ? strtotime($time_val[0]) : 0;
$customer_time_end = isset($item['time_val'][1]) && $item['time_val'][1] ? strtotime($time_val[1]) + 86399 : 0;
$extendData[] = [
'label_id' => $id,
'rule_type' => 4,
'customer_identity' => $item['customer_identity'],
'customer_num' => $item['customer_num'],
'customer_time_start' => $customer_time_start,
'customer_time_end' => $customer_time_end,
];
}
}
}
foreach ($extendData as $v) {
app()->make(UserLabelExtendServices::class)->save($v);
}
if (count($relationData) > 0) {
app()->make(UserLabelExtendRelationServices::class)->saveAll($relationData);
}
return true;
}
/**
* 删除
* @param $id
* @throws \Exception
*/
public function delLabel(int $id)
{
if ($this->getLable($id)) {
//删除旧标签
app()->make(UserLabelExtendServices::class)->searchDel(['label_id' => $id]);
app()->make(UserLabelExtendRelationServices::class)->search(['left_id' => $id])->delete();
app()->make(UserLabelRelationServices::class)->search(['label_id' => $id])->delete();
if (!$this->dao->delete($id)) {
throw new AdminException('删除失败,请稍候再试!');
}
}
return true;
}
/**
* 同步标签
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function authWorkClientLabel()
{
/** @var UserLabelCateServices $cateService */
$cateService = app()->make(UserLabelCateServices::class);
$data = $cateService->getLabelList(['group' => 0, 'owner_id' => 0]);
if ($data['list']) {
foreach ($data['list'] as $item) {
UserLabelJob::dispatchDo('authLabel', [$item['id'], $item['name']]);
}
}
UserLabelJob::dispatchSece(count($data['list']) + 1, 'authWorkLabel');
return true;
}
/**
* 同步平台标签到企业微信客户
* @param int $cateId
* @param string $groupName
* @return bool
*/
public function addCorpClientLabel(int $cateId, string $groupName)
{
try {
$list = $this->dao->getList(0, 0, ['not_tag_id' => 1, 'type' => 0, 'label_cate' => $cateId], ['label_name as name', 'id']);
if (!$list) {
return true;
}
$data = [];
foreach ($list as $item) {
$data[] = ['name' => $item['name']];
}
$res = Work::addCorpTag($groupName, $data);
/** @var UserLabelCateServices $categoryService */
$categoryService = app()->make(UserLabelCateServices::class);
$categoryService->update($cateId, ['other' => $res['tag_group']['group_id']]);
foreach ($res['tag_group']['tag'] ?? [] as $item) {
foreach ($list as $value) {
if ($item['name'] == $value['name']) {
$this->dao->update($value['id'], ['tag_id' => $item['id']]);
}
}
}
return true;
} catch (\Throwable $e) {
return false;
}
}
/**
* 客户标签同步到平台
* @param array $tagIds
* @param array $group
* @return bool
*/
public function authWorkLabel(array $tagIds = [], array $group = [])
{
$res = Work::getCorpTags($tagIds, $group);
$tagGroup = $res['tag_group'] ?? [];
$cateData = [];
$labelData = [];
$groupIds = [];
/** @var UserLabelCateServices $cateService */
$cateService = app()->make(UserLabelCateServices::class);
$this->transaction(function () use ($tagGroup, $cateData, $cateService, $labelData, $groupIds) {
foreach ($tagGroup as $item) {
if ($id = $cateService->value(['other' => $item['group_id']], 'id')) {
$cateService->update(['id' => $id], ['name' => $item['group_name'], 'other' => $item['group_id'], 'sort' => $item['order']]);
} else {
$cateData[] = [
'name' => $item['group_name'],
'sort' => $item['order'],
'add_time' => $item['create_time'],
'other' => $item['group_id'],
'group' => 0
];
}
$groupIds[] = $item['group_id'];
foreach ($item['tag'] as $tag) {
if ($labelId = $this->dao->value(['tag_id' => $tag['id']], 'id')) {
$this->dao->update($labelId, ['tag_id' => $tag['id']]);
} else {
$labelData[$item['group_id']][] = [
'label_name' => $tag['name'],
'type' => 0,
'tag_id' => $tag['id'],
];
}
}
}
if ($cateData) {
$cateService->saveAll($cateData);
}
$cateIds = $cateService->getColumn([
['other', 'in', $groupIds],
['type', '=', 0],
['owner_id', '=', 0],
['group', '=', 0],
], 'id', 'other');
if ($labelData) {
$saveData = [];
foreach ($labelData as $groupId => $labels) {
$cateId = $cateIds[$groupId];
foreach ($labels as $label) {
$label['label_cate'] = $cateId;
$saveData[] = $label;
}
}
$this->dao->saveAll($saveData);
}
});
$cateService->deleteCateCache();
return true;
}
/**
* 获取同步企业微信的标签数据
* @return array
*/
public function getWorkLabel()
{
/** @var UserLabelCateServices $cateService */
$cateService = app()->make(UserLabelCateServices::class);
$list = $cateService->getLabelTree(['type' => 0, 'owner_id' => 0, 'group' => 0, 'other' => true], ['name', 'id', 'other as value'], [
'label' => function ($query) {
$query->where('tag_id', '<>', '')->where('type', 0)->field(['id', 'label_cate', 'tag_id as value', 'label_name as label']);
}
]);
foreach ($list as &$item) {
$label = $item['label'];
$item['children'] = $label;
unset($item['label']);
$item['label'] = $item['name'];
}
return $list;
}
/**
* 企业微信创建客户标签事件
* @param string $corpId
* @param string $strId
* @param string $type
* @return bool
*/
public function createUserLabel(string $corpId, string $strId, string $type)
{
return $this->authWorkLabel($type === 'tag' ? [$strId] : [], $type === 'tag_group' ? [$strId] : []);
}
/**
* 企业微信更新客户标签事件
* @param string $corpId
* @param string $strId
* @param string $type
* @return bool
*/
public function updateUserLabel(string $corpId, string $strId, string $type)
{
return $this->authWorkLabel($type === 'tag' ? [$strId] : [], $type === 'tag_group' ? [$strId] : []);
}
/**
* 删除标签
* @param string $corpId
* @param string $strId
* @param string $type
*/
public function deleteUserLabel(string $corpId, string $strId, string $type)
{
if ('tag' === $type) {
$this->dao->delete(['tag_id' => $strId]);
} else if ('tag_group' === $type) {
/** @var UserLabelCateServices $cateService */
$cateService = app()->make(UserLabelCateServices::class);
$cateInfo = $cateService->get(['type' => 0, 'owner_id' => 0, 'group' => 0, 'other' => $strId]);
if ($cateInfo) {
$this->dao->delete(['label_cate' => $cateInfo->id, 'type' => 0]);
$cateInfo->delete();
}
}
}
}

View File

@@ -0,0 +1,189 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user\level;
use app\jobs\user\UserLevelJob;
use app\services\BaseServices;
use app\dao\user\level\SystemUserLevelDao;
use app\services\user\UserServices;
use think\annotation\Inject;
/**
* 系统设置用户等级
* Class SystemUserLevelServices
* @package app\services\user\level
* @mixin SystemUserLevelDao
*/
class SystemUserLevelServices extends BaseServices
{
/**
* @var SystemUserLevelDao
*/
#[Inject]
protected SystemUserLevelDao $dao;
/**
* 单个等级
* @param int $id
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLevel(int $id, string $field = '*')
{
return $this->dao->getOne(['id' => $id, 'is_del' => 0], $field);
}
/**
* 获取会员等级信息缓存
* @param int $id
* @return array|false|mixed|string|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author 等风来
* @email 136327134@qq.com
* @date 2022/11/14
*/
public function getLevelCache(int $id)
{
$level = $this->dao->cacheRemember($id, function () use ($id) {
$level = $this->getLevel($id);
if ($level) {
return $level->toArray();
} else {
return null;
}
});
return $level;
}
/**
* 获取某条件等级
* @param array $where
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getWhereLevel(array $where, string $field = '*')
{
return $this->dao->getOne($where, $field);
}
/**
* 获取所有等级列表
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLevelList(array $where, string $field = '*',$is_page = true)
{
$where_data = [];
if (isset($where['is_show']) && $where['is_show'] !== '') $where_data[] = ['is_show', '=', $where['is_show']];
if (isset($where['title']) && $where['title']) $where_data[] = ['name', 'LIKE', "%$where[title]%"];
$where_data[] = ['is_del', '=', '0'];
$page = $limit = 0;
if($is_page){
[$page, $limit] = $this->getPageValue();
}
$list = $this->dao->getList($where_data, $field ?? '*', $page, $limit);
foreach ($list as &$item) {
$item['image'] = set_file_url($item['image']);
$item['icon'] = set_file_url($item['icon']);
}
$count = $this->dao->getCount($where_data);
return $is_page ? compact('list', 'count') : $list;
}
/**
* 获取条件的会员等级列表
* @param array $where
* @param string $field
*/
public function getWhereLevelList(array $where, string $field = '*')
{
if ($where) {
$whereData = [['is_show', '=', 1], ['is_del', '=', 0], $where];
} else {
$whereData = [['is_show', '=', 1], ['is_del', '=', 0]];
}
return $this->dao->getList($whereData, $field ?? '*');
}
/**
* 获取一些用户等级名称
* @param $ids
* @return array
*/
public function getUsersLevel($ids)
{
return $this->dao->getColumn([['id', 'IN', $ids]], 'name', 'id');
}
/**
* 获取会员等级列表
* @param int $leval_id
* @return array
*/
public function getLevelListAndGrade(int $leval_id = 0, string $field = 'name,discount,image,icon,explain,id,grade,is_forever,valid_date,exp_num')
{
$list = $this->dao->getList(['is_del' => 0, 'is_show' => 1], $field);
if ($list) {
$listNew = array_combine(array_column($list, 'id'), $list);
$grade = $listNew[$leval_id]['grade'] ?? 0;
foreach ($list as &$item) {
if ($grade < $item['grade'])
$item['is_clear'] = true;
else
$item['is_clear'] = false;
$item['task_list'] = [];
}
}
return $list;
}
/**
* 获取用户等级折扣
* @param int $uid
* @param int $id
* @return null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getDiscount(int $uid, int $id)
{
$discount = null;
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getOne(['uid' => $uid], 'uid,level,level_status');
//需要会员是激活状态
if ($id && $userInfo && $userInfo['level_status']) {
$level = $this->dao->getOne(['id' => $id, 'is_del' => 0, 'is_show' => 1], 'id,discount');
if (!$level) {//用户存在等级ID 但是被删除或者下架重新检测升级
UserLevelJob::dispatch([$uid]);
$level = $this->dao->getOne(['id' => $userInfo['level'], 'is_del' => 0, 'is_show' => 1], 'id,discount');
}
$discount = $level['discount'] ?? null;
}
return $discount;
}
}

View File

@@ -0,0 +1,680 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\user\level;
use app\jobs\user\UserLevelJob;
use app\services\activity\coupon\StoreCouponIssueServices;
use app\services\activity\coupon\StoreCouponUserServices;
use app\services\BaseServices;
use app\services\user\UserServices;
use app\services\user\UserBillServices;
use app\services\user\UserSignServices;
use app\dao\user\level\UserLevelDao;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use crmeb\services\SystemConfigService;
use FormBuilder\Factory\Iview;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
/**
* 用户等级
* Class UserLevelServices
* @package app\services\user\level
* @mixin UserLevelDao
*/
class UserLevelServices extends BaseServices
{
/**
* @var UserLevelDao
*/
#[Inject]
protected UserLevelDao $dao;
/**
* 某些条件获取单个
* @param array $where
* @param string $field
* @return mixed
*/
public function getWhereLevel(array $where, string $field = '*')
{
return $this->getOne($where, $field);
}
/**
* 获取一些用户等级信息
* @param array $uids
* @param string $field
* @param string $key
* @return array
*/
public function getUsersLevelInfo(array $uids)
{
return $this->dao->getColumn([['uid', 'in', $uids]], 'level_id,is_forever,valid_time,grade', 'uid');
}
/**
* 清除会员等级
* @param $uids
* @return \crmeb\basic\BaseModel|mixed
*/
public function delUserLevel($uids)
{
$where = [];
if (is_array($uids)) {
$where[] = ['uid', 'IN', $uids];
$re = $this->dao->batchUpdate($uids, ['is_del' => 1, 'status' => 0], 'uid');
} else {
$where[] = ['uid', '=', $uids];
$re = $this->dao->update($uids, ['is_del' => 1, 'status' => 0], 'uid');
}
if (!$re)
throw new AdminException('修改会员信息失败');
$where[] = ['category', 'IN', ['exp']];
/** @var UserBillServices $userbillServices */
$userbillServices = app()->make(UserBillServices::class);
$userbillServices->update($where, ['status' => -1]);
return true;
}
/**
* 个人中心获取用户等级信息
* @param int $uid
* @return array
*/
public function homeGetUserLevel(int $uid, $userInfo = [])
{
$data = ['vip' => false, 'vip_id' => 0, 'vip_icon' => '', 'vip_name' => '', 'vip_discount' => 100];
//用户存在
if ($uid && sys_config('member_func_status', 0)) {
if (!$userInfo) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo($uid);
}
if ($userInfo) {
$levelInfo = $this->getUerLevelInfoByUid($uid);
if (!$levelInfo) {//不存在等级 展示最低等级
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
$alllevelInfo = $systemUserLevel->getList([['is_del', '=', 0], ['is_show', '=', 1]], 'id,name,icon,grade,discount', 1, 1);
$levelInfo = $alllevelInfo[0] ?? [];
if ($levelInfo) {
$levelInfo['id'] = 0;
$levelInfo['icon'] = set_file_url($levelInfo['icon'] ?? '');
}
}
if ($levelInfo) {
$data['vip'] = true;
$data['vip_id'] = $levelInfo['id'];
$data['vip_icon'] = $levelInfo['icon'];
$data['vip_name'] = $levelInfo['name'];
$data['vip_discount'] = $levelInfo['discount'] ? bcdiv((string)$levelInfo['discount'], '10', 1) : 0;
}
}
}
return $data;
}
/**
* 根据用户uid 获取会员详细信息
* @param int $uid
* @param string $field
*/
public function getUerLevelInfoByUid(int $uid, string $field = '')
{
$userLevelInfo = $this->dao->getUserLevel($uid);
$data = [];
if ($userLevelInfo) {
$data = ['id' => $userLevelInfo['id'], 'level_id' => $userLevelInfo['level_id'], 'add_time' => $userLevelInfo['add_time']];
$data['discount'] = $userLevelInfo['levelInfo']['discount'] ?? 0;
$data['name'] = $userLevelInfo['levelInfo']['name'] ?? '';
$data['money'] = $userLevelInfo['levelInfo']['money'] ?? 0;
$data['icon'] = set_file_url($userLevelInfo['levelInfo']['icon'] ?? '');
$data['image'] = set_file_url($userLevelInfo['levelInfo']['image'] ?? '');
$data['is_pay'] = $userLevelInfo['levelInfo']['is_pay'] ?? 0;
$data['grade'] = $userLevelInfo['levelInfo']['grade'] ?? 0;
$data['exp_num'] = $userLevelInfo['levelInfo']['exp_num'] ?? 0;
}
if ($field) return $data[$field] ?? '';
return $data;
}
/**
* 设置会员等级
* @param $uid 用户uid
* @param $level_id 等级id
* @return UserLevel|bool|\think\Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function setUserLevel(int $uid, int $level_id, $vipinfo = [])
{
/** @var SystemUserLevelServices $systemLevelServices */
$systemLevelServices = app()->make(SystemUserLevelServices::class);
if (!$vipinfo) {
$vipinfo = $systemLevelServices->getLevel($level_id);
if (!$vipinfo) {
throw new AdminException('会员等级不存在');
}
}
/** @var UserServices $user */
$user = app()->make(UserServices::class);
$userinfo = $user->getUserInfo($uid);
//把之前等级作废
$this->dao->update(['uid' => $uid], ['status' => 0, 'is_del' => 1]);
//检查是否购买过
$uservipinfo = $this->getWhereLevel(['uid' => $uid, 'level_id' => $level_id]);
$data['mark'] = '尊敬的用户' . $userinfo['nickname'] . '在' . date('Y-m-d H:i:s', time()) . '成为了' . $vipinfo['name'];
$data['add_time'] = time();
if ($uservipinfo) {
$data['status'] = 1;
$data['is_del'] = 0;
if (!$this->dao->update(['id' => $uservipinfo['id']], $data))
throw new AdminException('修改会员信息失败');
} else {
$data = array_merge($data, [
'is_forever' => $vipinfo->is_forever,
'status' => 1,
'is_del' => 0,
'grade' => $vipinfo->grade,
'uid' => $uid,
'level_id' => $level_id,
'discount' => $vipinfo->discount,
]);
$data['valid_time'] = 0;
if (!$this->dao->save($data)) throw new AdminException('写入会员信息失败');
}
if (!$user->update(['uid' => $uid], ['level' => $level_id, 'exp' => $vipinfo['exp_num'], 'level_status' => 1]))
throw new AdminException('修改用户会员等级失败');
//自动打标签
event('user.auto.label', [$uid, '', [], []]);
return true;
}
/**
* 会员列表
* @param $where
* @return mixed
*/
public function getSytemList($where)
{
/** @var SystemUserLevelServices $systemLevelServices */
$systemLevelServices = app()->make(SystemUserLevelServices::class);
return $systemLevelServices->getLevelList($where);
}
/**
* 获取添加修改需要表单数据
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function edit(int $id)
{
if ($id) {
$vipinfo = app()->make(SystemUserLevelServices::class)->getlevel($id);
if (!$vipinfo) {
throw new AdminException('数据不存在');
}
$field[] = Form::hidden('id', $id);
$msg = '编辑会员等级';
} else {
$msg = '添加会员等级';
}
$field[] = Form::input('name', '等级名称:', $vipinfo['name'] ?? '')->required('请填写等级名称');
// $field[] = Form::number('valid_date', '有效时间(天)', $vipinfo['valid_date'] ?? 0)->min(0)->col(12);
$field[] = Form::number('grade', '等级:', $vipinfo['grade'] ?? 0)->min(0)->precision(0);
$field[] = Form::number('discount', '享受折扣:', $vipinfo['discount'] ?? 100)->min(0)->max(100)->info('输入折扣数100,代表划线价,90代表9折')->placeholder('输入折扣数100,代表原价,90代表9折');
$field[] = Form::number('exp_num', '解锁需经验值达到:', $vipinfo['exp_num'] ?? 0)->min(0)->precision(0);
$field[] = Form::frameImage('icon', '卡片(656*300):', Url::buildUrl('admin/widget.images/index', array('fodder' => 'icon')), $vipinfo['icon'] ?? '')->icon('ios-add')->width('960px')->height('505px')->modal(['footer-hide' => true])->appendValidate(Iview::validateStr()->required()->message('请选择图标'));
$field[] = Form::frameImage('image', '卡背景(750*990):', Url::buildUrl('admin/widget.images/index', array('fodder' => 'image')), $vipinfo['image'] ?? '')->icon('ios-add')->width('960px')->height('505px')->modal(['footer-hide' => true])->appendValidate(Iview::validateStr()->required()->message('请选择背景'));
$field[] = Form::color('color', '字体颜色:', $vipinfo['color'] ?? '#000000')->required('请选择字体颜色');
$field[] = Form::radio('is_show', '是否显示:', $vipinfo['is_show'] ?? 0)->options([['label' => '显示', 'value' => 1], ['label' => '隐藏', 'value' => 0]]);
$field[] = Form::textarea('explain', '等级说明:', $vipinfo['explain'] ?? '')->required('请添加等级说明');
return create_form($msg, $field, Url::buildUrl('/user/user_level'), 'POST');
}
/*
* 会员等级添加或者修改
* @param $id 修改的等级id
* @return json
* */
public function save(int $id, array $data)
{
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
$levelOne = $systemUserLevel->getWhereLevel(['is_del' => 0, 'grade' => $data['grade']]);
$levelTwo = $systemUserLevel->getWhereLevel(['is_del' => 0, 'exp_num' => $data['exp_num']]);
$levelThree = $systemUserLevel->getWhereLevel(['is_del' => 0, 'name' => $data['name']]);
$levelPre = $systemUserLevel->getPreLevel($data['grade']);
$levelNext = $systemUserLevel->getNextLevel($data['grade']);
if ($levelPre && $data['exp_num'] <= $levelPre['exp_num']) {
throw new AdminException('会员经验必须大于上一等级设置的经验');
}
if ($levelNext && $data['exp_num'] >= $levelNext['exp_num']) {
throw new AdminException('会员经验必须小于下一等级设置的经验');
}
//修改
if ($id) {
if (($levelOne && $levelOne['id'] != $id) || ($levelThree && $levelThree['id'] != $id)) {
throw new AdminException('已检测到您设置过的会员等级,此等级不可重复');
}
if ($levelTwo && $levelTwo['id'] != $id) {
throw new AdminException('已检测到您设置过该会员经验值,经验值不可重复');
}
if (!$systemUserLevel->update($id, $data)) {
throw new AdminException('修改失败');
}
$data['id'] = $id;
$systemUserLevel->cacheUpdate($data);
return '修改成功';
} else {
if ($levelOne || $levelThree) {
throw new AdminException('已检测到您设置过的会员等级,此等级不可重复');
}
if ($levelTwo) {
throw new AdminException('已检测到您设置过该会员经验值,经验值不可重复');
}
//新增
$data['add_time'] = time();
$res = $systemUserLevel->save($data);
if (!$res) {
throw new AdminException('添加失败');
}
$data['id'] = $res->id;
$systemUserLevel->cacheUpdate($data);
return '添加成功';
}
}
/**
* 假删除
* @param int $id
* @return mixed
*/
public function delLevel(int $id)
{
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
$level = $systemUserLevel->getWhereLevel(['id' => $id]);
if ($level && $level['is_del'] != 1) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if ($userServices->count(['level' => $level['id']])) {
throw new AdminException('存在用户已是该等级,无法删除');
}
if (!$systemUserLevel->update($id, ['is_del' => 1]))
throw new AdminException('删除失败');
if (!$this->dao->update(['level_id' => $id], ['is_del' => 1])) {
throw new AdminException('删除失败');
}
$systemUserLevel->cacheDelById($id);
}
return '删除成功';
}
/**
* 设置是否显示
* @param int $id
* @param $is_show
* @return mixed
*/
public function setShow(int $id, int $is_show)
{
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
if (!$systemUserLevel->getWhereLevel(['id' => $id]))
throw new AdminException('数据不存在');
if ($systemUserLevel->update($id, ['is_show' => $is_show])) {
$systemUserLevel->cacheSaveValue($id, 'is_show', $is_show);
return $is_show == 1 ? '显示成功' : '隐藏成功';
} else {
throw new AdminException($is_show == 1 ? '显示失败' : '隐藏失败');
}
}
/**
* 快速修改
* @param int $id
* @param $is_show
* @return mixed
*/
public function setValue(int $id, array $data)
{
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
if (!$systemUserLevel->getWhereLevel(['id' => $id]))
throw new AdminException('数据不存在');
if ($systemUserLevel->update($id, [$data['field'] => $data['value']])) {
$systemUserLevel->cacheSaveValue($id, $data['field'], $data['value']);
return true;
} else {
throw new AdminException('保存失败');
}
}
/**
* 检测用户会员升级
* @param $uid
* @return bool
*/
public function detection(int $uid)
{
//商城会员是否开启
if (!sys_config('member_func_status')) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserCacheInfo($uid);
if (!$user) {
throw new ValidateException('没有此用户,无法检测升级会员');
}
if (!$user['level_status'] && sys_config('level_activate_status') == 0) {
$userServices->update($uid, ['level_status' => 1]);
$userServices->cacheTag()->clear();
}
//没有激活暂不升级
if (!$user['level_status']) {
return true;
}
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
$userAllLevel = $systemUserLevel->getList([['is_del', '=', 0], ['is_show', '=', 1], ['exp_num', '<=', (float)$user['exp']]]);
if (!$userAllLevel) {
return true;
}
$data = [];
$data['add_time'] = time();
$userLevel = $this->dao->getColumn(['uid' => $uid, 'status' => 1, 'is_del' => 0], 'level_id');
foreach ($userAllLevel as $vipinfo) {
if (in_array($vipinfo['id'], $userLevel)) {
continue;
}
$data['mark'] = '尊敬的用户' . $user['nickname'] . '在' . date('Y-m-d H:i:s', time()) . '成为了' . $vipinfo['name'];
$uservip = $this->dao->getOne(['uid' => $uid, 'level_id' => $vipinfo['id']], 'id');
if ($uservip) {
//降级在升级情况
$data['status'] = 1;
$data['is_del'] = 0;
if (!$this->dao->update($uservip['id'], $data, 'id')) {
throw new ValidateException('检测升级失败');
}
} else {
$data = array_merge($data, [
'is_forever' => $vipinfo['is_forever'],
'status' => 1,
'is_del' => 0,
'grade' => $vipinfo['grade'],
'uid' => $uid,
'level_id' => $vipinfo['id'],
'discount' => $vipinfo['discount'],
]);
if (!$this->dao->save($data)) {
throw new ValidateException('检测升级失败');
}
}
$data['add_time'] += 1;
}
if (!$userServices->update($uid, ['level' => end($userAllLevel)['id']], 'uid')) {
throw new ValidateException('检测升级失败');
}
return true;
}
/**
* 会员等级列表
* @param int $uid
*/
public function grade(int $uid)
{
//商城会员是否开启
if (!sys_config('member_func_status')) {
return [];
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserCacheInfo($uid);
if (!$user) {
throw new ValidateException('没有此用户,无法检测升级会员');
}
$userLevelInfo = $this->getUerLevelInfoByUid($uid);
if (empty($userLevelInfo)) {
$level_id = 0;
} else {
$level_id = $userLevelInfo['level_id'];
}
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
return $systemUserLevel->getLevelListAndGrade($level_id);
}
/**
* 获取会员信息
* @param int $uid
* @return array[]
*/
public function getUserLevelInfo(int $uid)
{
$data = ['user' => [], 'level_info' => [], 'level_list' => [], 'task' => []];
//商城会员是否开启
if (!sys_config('member_func_status')) {
return $data;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo($uid);
if (!$user) {
throw new ValidateException('没有此会员');
}
/** @var StoreCouponUserServices $storeCoupon */
$storeCoupon = app()->make(StoreCouponUserServices::class);
$user['couponCount'] = $storeCoupon->getUserValidCouponCount((int)$uid);
$data['user'] = $user;
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
$levelList = $systemUserLevel->getList(['is_del' => 0, 'is_show' => 1]);
$i = 0;
foreach ($levelList as &$level) {
$level['next_exp_num'] = $levelList[$i + 1]['exp_num'] ?? $level['exp_num'];
$level['image'] = set_file_url($level['image']);
$level['icon'] = set_file_url($level['icon']);
$i++;
}
$data['level_list'] = $levelList;
$levelInfo = $this->getUerLevelInfoByUid($uid);
if (!$levelInfo) {//不存在等级 展示最低等级
/** @var SystemUserLevelServices $systemUserLevel */
$systemUserLevel = app()->make(SystemUserLevelServices::class);
$alllevelInfo = $systemUserLevel->getList([['is_del', '=', 0], ['is_show', '=', 1]], 'id,name,icon,grade', 1, 1);
$levelInfo = $alllevelInfo[0] ?? [];
if ($levelInfo) {
$levelInfo['id'] = 0;
}
}
if ($levelInfo) {
$levelInfo['vip'] = true;
$levelInfo['vip_id'] = $levelInfo['id'];
$levelInfo['vip_icon'] = $levelInfo['icon'];
$levelInfo['vip_name'] = $levelInfo['name'];
}
$data['level_info'] = $levelInfo;
$i = 0;
foreach ($levelList as &$level) {
if ($level['grade'] < $levelInfo['grade']) {
$level['next_exp_num'] = $levelList[$i + 1]['exp_num'] ?? $level['exp_num'];
$level['lack_exp_num'] = 0;
} else {
$level['next_exp_num'] = $level['exp_num'];
if ($user['exp'] > $level['exp_num']) {
$level['lack_exp_num'] = 0;
} else {
$level['lack_exp_num'] = bcsub((string)$level['exp_num'], (string)$user['exp'], 0);
}
}
$level['image'] = set_file_url($level['image']);
$level['icon'] = set_file_url($level['icon']);
$i++;
}
$data['level_list'] = $levelList;
$data['level_info']['exp'] = $user['exp'] ?? 0;
/** @var UserBillServices $userBillservices */
$userBillservices = app()->make(UserBillServices::class);
$data['level_info']['today_exp'] = $userBillservices->getExpSum($uid, 'today');
$task = [];
/** @var UserSignServices $userSignServices */
$userSignServices = app()->make(UserSignServices::class);
$task['sign_count'] = $userSignServices->getSignSumDay($uid);
$config = SystemConfigService::more(['sign_give_exp', 'order_give_exp', 'invite_user_exp']);
$task['sign'] = $config['sign_give_exp'] ?? 0;
$task['order'] = $config['order_give_exp'] ?? 0;
$task['invite'] = $config['invite_user_exp'] ?? 0;
$data['task'] = $task;
//检测升级
UserLevelJob::dispatch([$uid]);
return $data;
}
/**
* 经验列表
* @param int $uid
* @return array
*/
public function expList(int $uid)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserCacheInfo($uid);
if (!$user) {
throw new ValidateException('没有此用户');
}
/** @var UserBillServices $userBill */
$userBill = app()->make(UserBillServices::class);
$data = $userBill->getExpList($uid, [], 'id,title,number,pm,add_time');
$list = $data['list'] ?? [];
$count = $data['count'] ?? 0;
$times = [];
if ($list) {
foreach ($list as $key => &$item) {
$addTime = strtotime($item['add_time']);
$item['time_key'] = $item['time'] = $addTime ? date('Y-m', (int)$addTime) : '';
}
$times = array_merge(array_unique(array_column($list, 'time_key')));
}
return ['count' => $count, 'list' => $list, 'time' => $times];
}
/**
* 获取激活会员卡需要的信息
* @return mixed
*/
public function getActivateInfo()
{
//商城会员是否开启
if (!sys_config('member_func_status')) {
throw new ValidateException('会员卡功能暂未开启');
}
//是否需要激活
if (!sys_config('level_activate_status')) {
throw new ValidateException('会员卡功能暂不需要激活');
}
return SystemConfigService::get('level_extend_info');
}
/**
* 激活会员卡
* @param int $uid
* @param array $data
* @return array
*/
public function userActivatelevel(int $uid, array $data)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo($uid);
if (!$user) {
throw new ValidateException('用户已注销,或不存在');
}
if ($user['level_status']) {
throw new ValidateException('不需要重复激活');
}
$extend_info = $userServices->handelExtendInfo($data, true) ?: [];
$update = ['level_status' => 1];
if ($extend_info) {
$default = $userServices->defaultExtendInfo;
$params = array_column($default, 'param');
$sex = $userServices->sex;
$update['level_extend_info'] = $extend_info;
foreach ($extend_info as $info) {
if (isset($info['param']) && in_array($info['param'], $params) && isset($info['value'])) {
if ($info['param'] == 'sex') {
$update['sex'] = $sex[$info['value']] ?? 0;
} elseif ($info['param'] == 'birthday') {
$update['birthday'] = $info['value'];
} else {
$update[$info['param']] = $info['value'];
}
}
}
}
$userServices->update($uid, $update);
$data = [];
//获取激活送好礼
$data = SystemConfigService::more([
'level_integral_status',
'level_give_integral',
'level_money_status',
'level_give_money',
'level_coupon_status',
'level_give_coupon',
]);
$ids = $data['level_give_coupon'] ?? [];
$data['level_give_coupon'] = [];
if ($data['level_coupon_status'] && $ids) {
/** @var StoreCouponIssueServices $couponServices */
$couponServices = app()->make(StoreCouponIssueServices::class);
$coupon = $couponServices->getList(['id' => $ids]);
$data['level_give_coupon'] = $coupon;
}
if (!$data['level_integral_status']) {
$data['level_give_integral'] = 0;
}
if (!$data['level_money_status']) {
$data['level_give_money'] = 0;
}
//激活会员卡事件
event('user.activate.level', [$uid]);
return $data;
}
}

View File

@@ -0,0 +1,156 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user\member;
use app\dao\user\member\MemberCardBatchDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
/**
* Class MemberCardBatchServices
* @package app\services\user\member
* @mixin MemberCardBatchDao
*/
class MemberCardBatchServices extends BaseServices
{
/**
* @var MemberCardBatchDao
*/
#[Inject]
protected MemberCardBatchDao $dao;
/**
* 获取会员卡批次列表
* @param array $where
*/
public function getList(array $where = [])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $page, $limit);
if ($list) {
foreach ($list as &$v) {
$v['add_time'] = date('Y-m-d H:i:s', $v['add_time']);
//$v['qrcode'] = json_decode($v['qrcode'], true);
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* @param int $id
* @param array $data
*/
public function save(int $id, array $data)
{
if (!$data['title']) throw new AdminException("请填写批次名称");
if (!$data['total_num']) throw new AdminException("请填写要生成卡的数量");
if (!is_numeric($data['total_num']) || $data['total_num'] < 0) throw new AdminException("卡片数量只能为正整数");
if ($data['total_num'] > 6000) throw new AdminException("单次制卡数量最高不得超过6000张");
if (!$data['use_day'] || !is_numeric($data['use_day'])) throw new AdminException("请填写免费使用天数");
if ($data['use_day'] < 0) throw new AdminException("免费使用天数只能为正整数");
/**
* 具体时间段试用,业务需要打开即可
*/
/* $use_start_time = strtotime($data['use_start_time']);
$use_end_time = strtotime($data['use_end_time']);
if (!$use_start_time) {
$use_start_time = strtotime(date('Y-m-d 00:00:00', strtotime('+1 day')));
}else{
$use_start_time = strtotime($data['use_start_time']);
}
if (!$use_end_time) {
$use_end_time = strtotime(date('Y-m-d 23:59:59', strtotime('+1 day')));
}else{
$use_end_time = strtotime($data['use_end_time']);
}
if ($use_end_time < time()) throw new AdminException("体验结束时间不能小于当天");
if ($use_end_time < $use_start_time) throw new AdminException("体验结束时间不能小于体验开始时间");
$data['use_start_time'] = $use_start_time;
$data['use_end_time'] = $use_end_time;*/
$data['use_day'] = abs(ceil($data['use_day']));
$data['total_num'] = abs(ceil($data['total_num']));
$data['add_time'] = time();
$this->transaction(function () use ($id, $data) {
if ($id) {
unset($data['total_num']);
$data['update_time'] = time();
return $this->dao->update($id, $data);
} else {
/** @var MemberCardServices $memberCardService */
$memberCardService = app()->make(MemberCardServices::class);
$res = $this->dao->save($data);
$add_card['card_batch_id'] = $res->id;
$add_card['total_num'] = $data['total_num'];
return $memberCardService->addCard($add_card);
}
});
}
/**
* 列表操作
* @param int $id
* @param array $data
*/
public function setValue(int $id, array $data)
{
if (!is_numeric($id) || !$id) throw new AdminException("参数缺失");
if (!isset($data['field']) || !isset($data['value']) || !$data['field']) throw new AdminException("参数错误");
$res = true;
if ($data['field'] == 'status') {
/** @var MemberCardServices $memberCardService */
$memberCardService = app()->make(MemberCardServices::class);
$res = $memberCardService->update(['card_batch_id' => $id], ['status' => $data['value']]);
}
if ($res) {
$this->dao->update($id, [$data['field'] => $data['value']]);
} else {
throw new AdminException("操作错误");
}
}
/**
* 获取单条卡批次资源
* @param array $uid
* @param string $field
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOne(int $bid, $field = '*')
{
if (is_string($field)) $field = explode(',', $field);
return $this->dao->get($bid, $field);
}
/**
* 批次卡数量统计
* @param int $id
* @param string $field
* @param int $inc
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function useCardSetInc(int $id, string $field, int $inc = 1)
{
return $this->dao->bcInc($id, $field, $inc);
}
}

View File

@@ -0,0 +1,453 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user\member;
use app\dao\user\member\MemberCardDao;
use app\services\BaseServices;
use app\services\order\OtherOrderServices;
use app\services\order\StoreOrderCreateServices;
use crmeb\exceptions\AdminException;
use crmeb\services\SystemConfigService;
use think\annotation\Inject;
use think\exception\ValidateException;
use app\services\user\UserServices;
/**
* Class MemberCardServices
* @package app\services\user\member
* @mixin MemberCardDao
*/
class MemberCardServices extends BaseServices
{
/**
* @var array|string[]
*/
public static array $_memberTypePrefix = ['month', 'quarter', 'year', 'ever', 'free'];
/**
* @var MemberCardDao
*/
#[Inject]
protected MemberCardDao $dao;
/**
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author 等风来
* @email 136327134@qq.com
* @date 2023/9/18
*/
public function getSearchList(array $where = [])
{
[$page, $limit] = $this->getPageValue();
$where['batch_card_id'] = $where['card_batch_id'];
if ($where['is_use'] != "") {
if ($where['is_use'] == 0) {
$where['use_time'] = 0;
} else {
$where['use_time'] = 1;
}
}
unset($where['is_use']);
$list = $this->dao->getSearchList($where, $page, $limit);
if ($list) {
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$userInfos = $userService->getColumn([['uid', 'in', array_unique(array_column($list, 'use_uid'))]], 'uid,real_name,nickname,phone', 'uid');
foreach ($list as $k => $v) {
$list[$k]['username'] = $list[$k]['phone'] = '';
$user_info = $userInfos[$v['use_uid']] ?? [];
if ($v['use_uid'] && $user_info) {
$list[$k]['username'] = $user_info['real_name'] ? $user_info['real_name'] : $user_info['nickname'];
$list[$k]['phone'] = $user_info ? $user_info['phone'] : "";
}
$list[$k]['add_time'] = date('Y-m-d H:i:s', $v['add_time']);
$list[$k]['use_time'] = $v['use_time'] != 0 ? date('Y-m-d H:i:s', $v['use_time']) : "未使用";
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 生成免费会员卡
* @param array $data
* @return bool
*/
public function addCard(array $data)
{
if (!isset($data['card_batch_id']) || !$data['card_batch_id'] || $data['card_batch_id'] == 0 || !isset($data['total_num']) || !$data['total_num'] || $data['total_num'] == 0) {
throw new AdminException("非法参数");
}
try {
if (!isset($data['total_num'])) throw new AdminException("数据缺失");
$num = $data['total_num'];
unset($data['total_num']);
for ($i = 0; $i < $num; $i++) {
$data['card_number'] = $this->makeRandomNumber("CR", $data['card_batch_id']);
$data['card_password'] = $this->makeRandomNumber();
$data['status'] = 1;
$data['add_time'] = time();
$res[] = $data;
}
//数据切片批量插入,提高性能。
$chunk_inster_card = array_chunk($res, 100, true);
foreach ($chunk_inster_card as $v) {
$this->dao->saveAll($v);
}
return true;
} catch (\Exception $exception) {
throw new AdminException("生成卡失败");
}
}
/**
* 获取制卡卡号随机数
* @param false $prefix
* @param false $random
* @return string
*/
public function makeRandomNumber($prefix = false, $random = false)
{
if (!$prefix) {
$prefix = "";
}
if (!$random || !is_numeric($random)) {
mt_srand();
$one_random = mt_rand(11111, 99999);
} else {
$one_random = sprintf("%05d", $random);
}
$date_random = date('ymd', time());
$random_tmp = strlen($one_random);
mt_srand();
$two_randow = str_pad(mt_rand(1, 99999), $random_tmp, '0', STR_PAD_LEFT);
if (!$random) {
return $two_randow;
} else {
return $prefix . $one_random . $date_random . $two_randow;
}
}
/**
* 领取会员卡
* @param array $data
* @param int $uid
*/
public function drawMemberCard(array $data, int $uid)
{
if (!$uid || !$data) throw new ValidateException('参数缺失!');
$isOpenMember = $this->isOpenMemberCardCache();
if (!$isOpenMember) throw new ValidateException('会员功能暂未开启!');
if (!isset($data['member_card_code']) || !$data['member_card_code']) throw new ValidateException('请输入会员卡号!');
if (!isset($data['member_card_code']) || !$data['member_card_pwd']) throw new ValidateException('请输入领取卡密!');
$card_info = $this->dao->getOneByWhere(['card_number' => trim($data['member_card_code'])]);
if (!$card_info) throw new ValidateException('会员卡不存在!');
/** @var MemberCardBatchServices $memberBatchServices */
$memberBatchServices = app()->make(MemberCardBatchServices::class);
$batch_info = $memberBatchServices->getOne($card_info['card_batch_id']);
if (!$batch_info) throw new ValidateException('会员卡未激活,暂无法使用.');
if ($batch_info->status != 1) throw new ValidateException('会员卡未激活,暂无法使用..');
if ($card_info['status'] == 0) throw new ValidateException('会员卡暂未激活');
if ($card_info['card_password'] != trim($data['member_card_pwd'])) throw new ValidateException('会员卡密码有误!');
if ($card_info['use_uid'] && $card_info['use_time']) throw new ValidateException('会员卡已使用!');
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user_info = $userServices->getUserInfo($uid);
if (!$user_info) throw new ValidateException('用户不存在,请重新登录');
if ($user_info->is_money_level > 0 && $user_info->is_ever_level == 1) throw new ValidateException('您已是永久会员,无需再领取,可以将此卡转送亲朋好友,一起享受优惠');
/**
* 批次卡具体使用期限,业务需要打开即可,勿删。
*/
//if ($batch_info->use_start_time > time()) throw new ValidateException('卡片未在合法有效期内,暂无法使用');
//if ($batch_info->use_end_time < time()) throw new ValidateException('卡片已经失效,无法使用');
if ($card_info->status != 1) throw new ValidateException('会员卡未激活,暂无法使用...');
$this->transaction(function () use ($card_info, $user_info, $batch_info, $memberBatchServices, $userServices, $data) {
$res1 = $this->dao->update($card_info->id, ['use_uid' => $user_info->uid, 'use_time' => time(), 'update_time' => time()], 'id');
if ($res1) {
$res2 = $memberBatchServices->useCardSetInc($batch_info->id, 'use_num', 1);
if ($user_info->overdue_time > time()) {
$overdue_time = bcadd(bcmul($batch_info->use_day, 86400, 0), $user_info->overdue_time, 0);
} else {
$overdue_time = bcadd(bcmul($batch_info->use_day, 86400, 0), time(), 0);
}
$channel_type = $data['from'];
/** @var OtherOrderServices $OtherOrderServices */
$OtherOrderServices = app()->make(OtherOrderServices::class);
$storeOrderCreateService = app()->make(StoreOrderCreateServices::class);
$record_data['uid'] = $user_info->uid;
$record_data['member_code'] = $card_info->card_number;
$record_data['use_day'] = $batch_info->use_day;
$record_data['overdue_time'] = $overdue_time;
$record_data['order_id'] = $storeOrderCreateService->getNewOrderId();
$record_data['channel_type'] = $channel_type;
$record_data['member_type'] = "free";
$record_data['vip_day'] = $batch_info->use_day;
$record_data['type'] = 2;
$record_data['paid'] = 1;
$record_data['pay_time'] = time();
$res3 = $OtherOrderServices->addOtherOrderData($record_data);
//if ($res3) $res4 = $userServices->update($user_info->uid, ['level' => 1, 'overdue_time' => $overdue_time, 'is_permanent' => 0], 'uid');
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$res4 = $userServices->setMemberOverdueTime($batch_info->use_day, $user_info->uid, 2, $record_data['member_type']);
$res5 = $res1 && $res2 && $res3 && $res4;
return $res5;
}
});
}
/** 验证是否存在此类型会员卡
* @param string $member_type
* @return bool
*/
public function checkmemberType(string $member_type)
{
$member_type_arr = $this->getMemberTypeInfo();
if (!array_key_exists($member_type, $member_type_arr)) throw new ValidateException('暂无此类型会员卡');
return true;
}
/** 获取会员权益和说明配置
* @return array
*/
public function getMemberRightsInfo()
{
/** @var MemberRightServices $memberRightService */
$memberRightService = app()->make(MemberRightServices::class);
$memberRight = $memberRightService->getSearchList(['status' => 1], ['id', 'right_type', 'title', 'show_title', 'image', 'explain','content', 'number']);
if ($memberRight['list']) {
foreach ($memberRight['list'] as $k => &$v) {
$v['title'] = $v['show_title'];
$v['pic'] = $v['image'];
unset($v['show_title'], $v['image']);
}
}
return ['member_right' => $memberRight['list']];
}
/**
* 获取会员卡配置
* @return array
*/
public function getMemberTypeInfo()
{
/** @var SystemConfigService $systemConfigService */
$systemConfigService = app()->make(SystemConfigService::class);
$data = [];
foreach (self::$_memberTypePrefix as $v) {
$data[$v] = $systemConfigService::more([$v . '_title', $v . '_vip_day', $v . '_pre_price', $v . '_price']);
}
return $data;
}
/**
* 会员卡数据处理
* @param int $uid
* @param bool $is_free
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function DoMemberType(int $uid = 0, bool $is_free = true)
{
$data = array();
/** @var MemberShipServices $memberShipService */
$memberShipService = app()->make(MemberShipServices::class);
if ($is_free) {
$where = [['is_del', '=', 0]];
} else {
$where = [['is_del', '=', 0], ['type', '<>', 'free']];
}
$list = $memberShipService->getApiList($where);
foreach ($list as $v) {
$data[] = [
'id' => $v['id'],
'title' => $v['title'],
'type' => $v['type'],
'vip_day' => $v['vip_day'],
'pre_price' => $v['pre_price'],
'is_label' => $v['is_label'],
'price' => $v['price'],
'overdue_time' => $memberShipService->getOverdueTime($uid, (int)$v['id'], [], $v, true)
];
}
return $data;
}
/**
* 会员类型数据
* @return array|bool
*/
public function getMemberTypeValue(int $uid = 0)
{
$member_type = $this->DoMemberType($uid);
if (!$member_type) return false;
foreach ($member_type as $k => $v) {
$new_member_data[$v['id']] = $v;
}
return $new_member_data;
}
/**
* 导出会员卡
* @param $where
* @return array|\think\Collection
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getExportData($where, int $limit = 0)
{
if ($limit) {
[$page] = $this->getPageValue();
} else {
[$page, $limit] = $this->getPageValue();
}
$data = $this->dao->getSearchList($where, $page, $limit);
if ($data) {
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$userInfos = $userService->getColumn([['uid', 'in', array_unique(array_column($data, 'use_uid'))]], 'uid,real_name,nickname,phone', 'uid');
foreach ($data as $k => $v) {
$data[$k]['use_time'] = $v['use_time'] != 0 ? date('Y-m-d H:i:s', $v['use_time']) : "";
$data[$k]['user_name'] = '';
$data[$k]['user_phone'] = '';
$userInfo = $userInfos[$v['use_uid']] ?? [];
if ($v['use_uid'] && $userInfo) {
$data[$k]['user_name'] = $userInfo['real_name'] ? $userInfo['real_name'] : $userInfo['nickname'];
$data[$k]['user_phone'] = $userInfo['phone'];
}
}
}
/** @var MemberCardBatchServices $batchService */
$batchService = app()->make(MemberCardBatchServices::class);
$batchInfo = $batchService->getOne($where['batch_card_id']);
$dataArray['title'] = $batchInfo ? $batchInfo['title'] : "";
$dataArray['data'] = $data;
return $dataArray;
}
/**
* 获取会员记录
* @param array $where
* @return array
*/
public function getSearchRecordList(array $where)
{
/** @var OtherOrderServices $otherOrderSevice */
$otherOrderSevice = app()->make(OtherOrderServices::class);
return $otherOrderSevice->getMemberRecord($where);
}
/**
* 看是会员权益时候开启|并返回数据
* @param string $rightType
* @param bool $get_number
* @param int $member_status
* @return bool|mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function isOpenMemberCard(string $rightType = '', bool $get_number = true, $member_status = -1)
{
if ($member_status == -1) {
$isOpen = sys_config('member_card_status', 1);
} else {
$isOpen = $member_status;
}
//如果传入权益类别,查看是否具有某权益
if (!$rightType) {
if ($isOpen) return true;
return false;
} else {
if ($isOpen) {
/** @var MemberRightServices $memberRightService */
$memberRightService = app()->make(MemberRightServices::class);
$memberRight = $memberRightService->getOne(['right_type' => $rightType], 'status,number');
if ($memberRight && $memberRight['status']) {
if ($get_number) {
$number = $memberRight['number'];
if (!$number) return false;
return $number;
}
return true;
}
}
return false;
}
}
/**
* 看是会员权益时候开启|并返回数据
* @param string $rightType
* @param bool $get_number
* @param int $member_status
* @param int $expire
* @return bool|mixed|null
* @throws \throwable
*/
public function isOpenMemberCardCache(string $rightType = '', bool $get_number = true, $member_status = -1, int $expire = 30)
{
if ($member_status == -1) {
$isOpen = sys_config('member_card_status', 1);
} else {
$isOpen = $member_status;
}
//如果传入权益类别,查看是否具有某权益
if (!$rightType) {
if ($isOpen) return true;
return false;
} else {
if ($isOpen) {
/** @var MemberRightServices $memberRightService */
$memberRightService = app()->make(MemberRightServices::class);
$memberRight = $memberRightService->getMemberRightCache($rightType);
if ($memberRight && $memberRight['status']) {
if ($get_number) {
$number = $memberRight['number'];
if (!$number) return false;
return $number;
}
return true;
}
}
return false;
}
}
/**
* 修改会员卡状态
* @param $id
* @param $status
* @return bool
*/
public function setStatus($id, $status)
{
$res = $this->dao->update($id, ['status' => $status]);
if ($res) return true;
return false;
}
}

View File

@@ -0,0 +1,174 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user\member;
use app\dao\user\member\MemberRightDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
/**
* Class MemberRightServices
* @package app\services\user\member
* @mixin MemberRightDao
*/
class MemberRightServices extends BaseServices
{
/**
* @var MemberRightDao
*/
#[Inject]
protected MemberRightDao $dao;
/**
* 获取会员权益
* @param array $where
* @param array $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSearchList(array $where = [], array $field = ['*'])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getSearchList($where, $page, $limit, $field);
foreach ($list as &$item) {
$item['image'] = set_file_url($item['image']);
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 编辑保存
* @param int $id
* @param array $data
*/
public function save(int $id, array $data)
{
if (!$data['right_type']) throw new AdminException("会员权益类型缺失");
if (!$id) throw new AdminException("id参数缺失");
if (!$data['title'] || !$data['show_title']) throw new AdminException("请设置权益名称");
if (!$data['image']) throw new AdminException("请上传会员权益图标");
switch ($data['right_type']) {
case "integral":
if (!$data['number']) throw new AdminException("请设置返还积分倍数");
if ($data['number'] < 0) throw new AdminException("返还积分倍数不能为负数");
$save['number'] = abs($data['number']);
break;
case "express" :
if (!$data['number']) throw new AdminException("请设置运费折扣");
if ($data['number'] < 0) throw new AdminException("运费折扣不能为负数");
$save['number'] = abs($data['number']);
break;
case "sign" :
if (!$data['number']) throw new AdminException("请设置签到积分倍数");
if ($data['number'] < 0) throw new AdminException("签到积分倍数不能为负数");
$save['number'] = abs($data['number']);
break;
case "offline" :
if (!$data['number']) throw new AdminException("请设置线下付款折扣");
if ($data['number'] < 0) throw new AdminException("线下付款不能为负数");
$save['number'] = abs($data['number']);
}
$save['show_title'] = $data['show_title'];
$save['image'] = $data['image'];
$save['status'] = $data['status'];
$save['sort'] = $data['sort'];
$this->dao->update($id, $data);
$right = $this->dao->get($id);
if ($right) {
$this->dao->cacheUpdate($right->toArray());
}
return true;
}
/**
* 添加权益内容
* @param int $id
* @param array $data
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function save_content(int $id, array $data)
{
if (!$id) throw new AdminException("id参数缺失");
if (!$data['content']) throw new AdminException("请添加权益内容");
$this->dao->update($id, $data);
$right = $this->dao->get($id);
if ($right) {
$this->dao->cacheUpdate($right->toArray());
}
return true;
}
/**
* 获取单条信息
* @param array $where
* @param string $field
* @return array|false|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOne(array $where, string $field = '*')
{
if (!$where) return false;
return $this->dao->getOne($where, $field);
}
/**
* 查看某权益是否开启
* @param $rightType
* @return bool
*/
public function getMemberRightStatus($rightType)
{
if (!$rightType) return false;
$status = $this->dao->value(['right_type' => $rightType], 'status');
if ($status) return true;
return false;
}
/**
* 在缓存中查找权益等级
* @param string $rightType
* @return false|mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author 等风来
* @email 136327134@qq.com
* @date 2022/11/10
*/
public function getMemberRightCache(string $rightType)
{
$list = $this->dao->cacheList();
if ($list === null || (is_array($list) && !$list)) {
$list = $this->dao->getSearchList(['status' => 1]);
$this->dao->cacheCreate($list);
}
foreach ($list as $item) {
if ($rightType == $item['right_type']) {
return $item;
}
}
return false;
}
}

View File

@@ -0,0 +1,210 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\user\member;
use app\dao\user\member\MemberShipDao;
use app\services\BaseServices;
use app\services\user\UserServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class MemberShipServices
* @package app\services\user\member
* @mixin MemberShipDao
*/
class MemberShipServices extends BaseServices
{
/**
* @var MemberShipDao
*/
#[Inject]
protected MemberShipDao $dao;
/**
* 获取单个付费会员信息
* @param int $id
* @param string $field
* @return array|\think\Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getMemberInfo(int $id, string $field = '*')
{
$member = $this->dao->getOne(['id' => $id, 'is_del' => 0], $field);
if (!$member) {
throw new ValidateException('该会员类型不存在或已删除');
}
return $member;
}
/**
* 后台获取会员类型
* @param array $where
* @return array
*/
public function getSearchList(array $where = [])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getSearchList($where, $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 获取会员卡api接口
* @return mixed
*/
public function getApiList(array $where)
{
return $this->dao->getApiList($where);
}
/**
* 卡类型编辑保存
* @param int $id
* @param array $data
*/
public function save(int $id, array $data)
{
if (!$data['title']) throw new AdminException("请填写会员名称");
if (!$data['type']) throw new AdminException("会员类型缺失");
if ($data['type'] == "ever") {
$data['vip_day'] = -1;
} else {
if (!$data['vip_day']) throw new AdminException("请填写体验天数");
if ($data['vip_day'] < 0) throw new AdminException("体验天数不能为负数");
}
if ($data['type'] == "free") {
// $data['price'] = 0.00;
$data['pre_price'] = 0.00;
} else {
if ($data['pre_price'] === '' || $data['price'] === '') throw new AdminException("请填写价格");
if ($data['pre_price'] == 0 || $data['price'] == 0) throw new AdminException("请填写价格");
}
if ($data['pre_price'] < 0 || $data['price'] < 0) throw new AdminException("价格不能为负数");
if ($data['pre_price'] > $data['price']) throw new AdminException("优惠价不能大于划线价");
if ($data['is_label']) {
$this->dao->update(['is_label' => 1], ['is_label' => 0]);
}
if ($id) {
$data['id'] = $id;
$this->dao->cacheUpdate($data);
return $this->dao->update($id, $data);
} else {
$res = $this->dao->save($data);
$data['id'] = $res->id;
$this->dao->cacheUpdate($data);
return $res;
}
}
/**
* 获取卡会员天数
* @param array $where
* @return mixed
*/
public function getVipDay(array $where)
{
return $this->dao->value($where, 'vip_day');
}
/**
* 修改会员类型状态
* @param $id
* @param $is_del
* @return bool
*/
public function setStatus($id, $is_del)
{
$res = $this->dao->update($id, ['is_del' => $is_del]);
if ($is_del) {
$this->cacheDelById($id);
} else {
$this->cacheSaveValue($id, 'is_del', 0);
}
if ($res) return true;
return false;
}
/**
* 查询会员类型select
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getShipSelect()
{
$menus = [];
foreach ($this->dao->getSearchList(['is_del' => 0], 0, 0, ['id', 'title']) as $menu) {
$menus[] = ['value' => $menu['id'], 'label' => $menu['title']];
}
$menus[] = ['value' => 'card', 'label' => '卡密激活'];
$menus[] = ['value' => 'admin', 'label' => '赠送'];
return $menus;
}
/**
* 获取过期时间
* @param int $uid
* @param int $id
* @param $userInfo
* @param $memberInfo
* @return false|string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOverdueTime(int $uid, int $id, $userInfo = [], $memberInfo = [], $is_date = false)
{
if (!$id) {
throw new ValidateException('缺少会员ID');
}
if (!$memberInfo) {
/** @var MemberShipServices $memberShipService */
$memberShipService = app()->make(MemberShipServices::class);
$memberInfo = $memberShipService->get($id);
}
if (!$memberInfo) {
throw new ValidateException('会员卡类型不存在');
}
if ($uid && !$userInfo) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo($uid);
}
$member_type = $memberInfo['type'];
$vip_day = $memberInfo['vip_day'];
if ($member_type == 'ever') {
$overdue_time = 0;
$is_ever_level = 1;
} else {
if (!$userInfo || $userInfo['is_money_level'] == 0) {
$overdue_time = bcadd(bcmul($vip_day, 86400, 0), time(), 0);
} else {
$overdue_time = bcadd(bcmul($vip_day, 86400, 0), $userInfo['overdue_time'], 0);
}
$is_ever_level = 0;
}
if ($is_ever_level == 1) {
$res = "永久会员";
} else {
$res = $is_date ? date('Y-m-d', $overdue_time) : $overdue_time;
}
return $res;
}
}