| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- <?php
- // +----------------------------------------------------------------------
- // | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
- // +----------------------------------------------------------------------
- // | 版权所有 2017~2021 LARAVEL研发中心
- // +----------------------------------------------------------------------
- // | 官方网站: http://www.laravel.cn
- // +----------------------------------------------------------------------
- // | Author: laravel开发员 <laravel.qq.com>
- // +----------------------------------------------------------------------
- namespace App\Services\Common;
- use App\Models\ActionLogModel;
- use App\Models\MemberModel;
- use App\Services\BaseService;
- use App\Services\RedisService;
- use Illuminate\Support\Facades\Cache;
- use Illuminate\Support\Facades\DB;
- use InvalidArgumentException;
- /**
- * 用户(会员)管理-服务类
- * @author laravel开发员
- * @since 2020/11/11
- * Class MemberService
- * @package App\Services\Common
- */
- class MemberService extends BaseService
- {
- public static $instance = null;
- /**
- * 构造函数
- * @author laravel开发员
- * @since 2020/11/11
- * MemberService constructor.
- */
- public function __construct()
- {
- $this->model = new MemberModel();
- }
- /**
- * 静态入口
- */
- public static function make()
- {
- if (!self::$instance) {
- self::$instance = new static();
- }
- return self::$instance;
- }
- /**
- * 列表
- * @param $params
- * @param int $pageSize
- * @return array
- */
- public function getDataList($params, $pageSize = 15)
- {
- $where = ['a.mark' => 1];
- $status = isset($params['status']) ? $params['status'] : 0;
- if ($status > 0) {
- $where['a.status'] = $status;
- }
- $query = $this->model->from('member as a')
- ->where($where)
- ->where(function ($query) use ($params) {
- $keyword = isset($params['keyword']) ? $params['keyword'] : '';
- if ($keyword) {
- $query->where('a.nickname', 'like', "%{$keyword}%")->orWhere('a.realname', 'like', "%{$keyword}%");
- }
- })
- ->where(function ($query) use ($params) {
- $mobile = isset($params['mobile']) ? trim($params['mobile']) : '';
- if ($mobile) {
- $query->where('a.mobile', 'like', "%{$mobile}%");
- }
- })
- ->where(function ($query) use ($params) {
- $confirmStatus = isset($params['confirm_status']) ? $params['confirm_status'] : -1;
- if ($confirmStatus == 0) {
- $query->whereIn('a.confirm_status', [2, 3]);
- } else if ($confirmStatus > 0) {
- $query->where('a.confirm_status', $confirmStatus);
- }
- })
- ->select(['a.*']);
- $confirmStatus = isset($params['confirm_status']) ? $params['confirm_status'] : -1;
- if ($confirmStatus == 0) {
- $query->orderBy('a.confirm_status', 'asc')->orderBy('a.create_time', 'desc');
- } else {
- $query->orderBy('a.create_time', 'desc')->orderBy('a.id', 'desc');
- }
- $list = $query->paginate($pageSize > 0 ? $pageSize : 9999999);
- $list = $list ? $list->toArray() : [];
- return [
- 'pageSize' => $pageSize,
- 'total' => isset($list['total']) ? $list['total'] : 0,
- 'list' => isset($list['data']) ? $list['data'] : []
- ];
- }
- /**
- * 按日期统计注册用户数
- * @param $beginAt
- * @param $endAt
- * @param int $status
- * @return mixed
- */
- public function getRegisterCount($beginAt = 0, $endAt = 0, $status = 1)
- {
- $cacheKey = "caches:members:count_{$beginAt}_{$endAt}_{$status}";
- $data = RedisService::get($cacheKey);
- if ($data) {
- return $data;
- }
- $where = ['mark' => 1, 'status' => $status];
- if ($status == 2) {
- $where['status'] = 1;
- }
- $data = $this->model->where($where)->where(function ($query) use ($beginAt, $endAt) {
- if ($beginAt && $endAt) {
- $query->whereBetween('create_time', [strtotime($beginAt), strtotime($endAt)]);
- } else if ($beginAt) {
- $query->where('create_time', '>=', strtotime($beginAt));
- }
- })->count('id');
- if ($data) {
- RedisService::set($cacheKey, $data, rand(300, 600));
- }
- }
- public function countUsers($type = 'today', $start_time = null, $end_time = null)
- {
- // 生成缓存的唯一键,依据 type 和时间范围
- $cacheKey = "user_count_{$type}";
- if ($start_time && $end_time) {
- $cacheKey .= "_{$start_time}_{$end_time}";
- }
- // 检查缓存中是否已经存在该统计数据
- $count = Cache::get($cacheKey);
- if ($count === null) {
- // 如果缓存中没有数据,则执行数据库查询
- $query = DB::table('member');
- // 如果 type 是 'all',则不进行时间筛选,查询所有记录
- if ($type === 'all') {
- // 不添加时间过滤条件,查询所有用户数量
- $count = $query->count();
- } else {
- // 根据传入的时间范围进行过滤
- if ($start_time && $end_time) {
- $query->whereBetween('create_time', [$start_time, $end_time]);
- }
- // 根据不同的统计类型进行筛选
- switch ($type) {
- case 'today':
- $query->whereDate('create_time', '=', now()->toDateString()); // 今天
- break;
- case 'yesterday':
- $query->whereDate('create_time', '=', now()->subDay()->toDateString()); // 昨天
- break;
- case 'month':
- $query->whereMonth('create_time', '=', now()->month) // 当前月
- ->whereYear('create_time', '=', now()->year);
- break;
- case 'last_month':
- $query->whereMonth('create_time', '=', now()->subMonth()->month) // 上个月
- ->whereYear('create_time', '=', now()->subMonth()->year);
- break;
- case 'year':
- $query->whereYear('create_time', '=', now()->year); // 当前年
- break;
- default:
- throw new InvalidArgumentException("无效的类型");
- }
- // 执行查询并获取统计数据
- $count = $query->count();
- }
- // 将查询结果缓存,缓存时间为 10 分钟
- // Cache::put($cacheKey, $count, now()->addMinutes(10));
- }
- return $count;
- }
- /**
- * 用户选项
- * @return array
- */
- public function options()
- {
- // 获取参数
- $param = request()->all();
- // 用户ID
- $keyword = getter($param, "keyword");
- $parentId = getter($param, "parent_id");
- $userId = getter($param, "user_id");
- $datas = $this->model->where(function ($query) use ($parentId) {
- if ($parentId) {
- $query->where(['id' => $parentId, 'mark' => 1]);
- } else {
- $query->where(['status' => 1, 'mark' => 1]);
- }
- })
- ->where(function ($query) use ($userId) {
- if ($userId) {
- $query->whereNotIn('id', [$userId]);
- }
- })
- ->where(function ($query) use ($keyword) {
- if ($keyword) {
- $query->where('nickname', 'like', "%{$keyword}%")
- ->orWhere('mobile', 'like', "%{$keyword}%");
- }
- })
- ->select(['id', 'realname', 'mobile', 'code', 'nickname', 'status'])
- ->get();
- return $datas ? $datas->toArray() : [];
- }
- /**
- * 添加或编辑会员
- * @return array
- */
- public function edit()
- {
- $data = request()->all();
- // 允许更新的字段(对应前端 dialog)
- $allowedFields = [
- 'id',
- 'avatar',
- 'mobile',
- 'nickname',
- 'is_zg_vip',
- 'zg_vip_expired',
- 'is_zsb_vip',
- 'zsb_vip_expired',
- 'is_video_vip',
- 'video_vip_expired',
- 'entry_type',
- 'need_paper'
- ];
- // 只保留允许字段
- $data = array_intersect_key($data, array_flip($allowedFields));
- // 头像与证件图片处理
- foreach (['avatar', 'driving_license', 'drivers_license'] as $field) {
- if (!empty($data[$field])) {
- $data[$field] = get_image_path($data[$field]);
- }
- }
- $id = $data['id'] ?? 0;
- $mobile = trim($data['mobile'] ?? '');
- if ($mobile) {
- $checkId = $this->model->where(['mobile' => $mobile, 'mark' => 1])->value('id');
- if ($checkId && $checkId != $id) {
- return message('手机号已存在', false);
- }
- }
- // 密码加密
- if (!empty($data['password'])) {
- $data['password'] = get_password(trim($data['password']));
- }
- // VIP字段处理(可选)
- $vipFields = ['is_zg_vip', 'is_zsb_vip', 'is_video_vip'];
- $vipMapping = [
- 'is_zg_vip' => 'zg_vip_expired',
- 'is_zsb_vip' => 'zsb_vip_expired',
- 'is_video_vip' => 'video_vip_expired'
- ];
- foreach ($vipFields as $vipField) {
- if (isset($data[$vipField])) {
- $data[$vipField] = intval($data[$vipField]);
- $expireField = $vipMapping[$vipField];
- // 获取当前数据库状态
- $oldStatus = $this->model->where('id', $id)->value($vipField);
- $oldExpire = $this->model->where('id', $id)->value($expireField);
- // 调用公共方法计算新过期时间
- if($data[$expireField]<=date('Y-m-d H:i:s')){
- $data[$expireField] = $this->calcVipExpire($oldStatus, $data[$vipField], $oldExpire);
- }
- }
- }
- // 日志记录
- ActionLogModel::setRecord(
- session('userId'),
- [
- 'type' => 1,
- 'title' => $id ? '修改用户信息' : '新增用户',
- 'content' => json_encode($data, 256),
- 'module' => 'admin'
- ]
- );
- ActionLogModel::record();
- // 清理缓存
- RedisService::keyDel("caches:members:count*");
- try {
- if ($id) {
- DB::table('member')->where('id', $id)->update($data);
- } else {
- DB::table('member')->insert($data);
- }
- return message("操作成功", true, );
- } catch (\Exception $e) {
- return message("操作失败:" . $e->getMessage(), false);
- }
- }
- /**
- * 审核
- * @return array
- * @since 2020/11/11
- * @author laravel开发员
- */
- public function confirm()
- {
- // 请求参数
- $data = request()->all();
- $drivingLicense = isset($data['driving_license']) ? $data['driving_license'] : '';
- if ($drivingLicense) {
- $data['driving_license'] = get_image_path($drivingLicense);
- }
- $driversLicense = isset($data['drivers_license']) ? $data['drivers_license'] : '';
- if ($driversLicense) {
- $data['drivers_license'] = get_image_path($driversLicense);
- }
- // 设置日志
- $mobile = isset($data['mobile']) ? $data['mobile'] : '';
- ActionLogModel::setRecord(session('userId'), ['type' => 1, 'title' => "审核用户[{$mobile}]信息", 'content' => json_encode($data, 256), 'module' => 'admin']);
- ActionLogModel::record();
- RedisService::keyDel("caches:members:count*");
- return parent::edit($data); // TODO: Change the autogenerated stub
- }
- /**
- * 删除
- * @return array
- */
- public function delete()
- {
- // 设置日志标题
- ActionLogModel::setRecord(session('userId'), ['type' => 1, 'title' => "删除用户信息", 'content' => json_encode(request()->post(), 256), 'module' => 'admin']);
- ActionLogModel::record();
- $this->model->where('mark', 0)->where('update_time', '<=', time() - 7 * 86400)->delete();
- RedisService::keyDel("caches:members:count*");
- return parent::delete(); // TODO: Change the autogenerated stub
- }
- /**
- * 批量设置VIP
- * 前端传参示例:
- * {
- * ids: [1,2,3],
- * is_zg_vip: 1 // 可选字段: is_zg_vip / is_zsb_vip / is_video_vip
- * }
- * @return array
- */
- public function batchVip()
- {
- $params = request()->post();
- // 校验会员ID
- if (empty($params['ids']) || !is_array($params['ids'])) {
- return message("请选择要操作的会员", false);
- }
- $ids = $params['ids'];
- $vipMapping = [
- 'is_zg_vip' => 'zg_vip_expired',
- 'is_zsb_vip' => 'zsb_vip_expired',
- 'is_video_vip' => 'video_vip_expired'
- ];
- $updateData = ['update_time' => time()];
- $hasVipField = false;
- foreach ($vipMapping as $vipField => $expireField) {
- if (isset($params[$vipField])) {
- $status = intval($params[$vipField]);
- $updateData[$vipField] = $status;
- // 查出这些会员当前的VIP状态和过期时间
- $members = DB::table('member')
- ->whereIn('id', $ids)
- ->select('id', $vipField, $expireField)
- ->get()
- ->keyBy('id');
- foreach ($members as $memberId => $member) {
- $newExpire = $this->calcVipExpire(intval($member->$vipField), $status, $member->$expireField);
- DB::table('member')
- ->where('id', $memberId)
- ->update([
- $vipField => $status,
- $expireField => $newExpire,
- 'update_time' => time(),
- ]);
- }
- $hasVipField = true;
- }
- }
- if (!$hasVipField) {
- return message("请指定要操作的VIP类型", false);
- }
- return message("批量设置VIP成功", true);
- }
- public function getMemberStats(array $params)
- {
- $dateType = $params['dateType'] ?? 'day';
- $page = max(1, (int) ($params['page'] ?? 1));
- $limit = max(1, (int) ($params['limit'] ?? 10));
- // 分组日期格式
- switch ($dateType) {
- case 'month':
- $format = '%Y-%m';
- $groupRaw = "FROM_UNIXTIME(create_time, '{$format}')";
- break;
- case 'year':
- $format = '%Y';
- $groupRaw = "FROM_UNIXTIME(create_time, '{$format}')";
- break;
- case 'week':
- // 按周统计,格式 YYYY-WW
- $groupRaw = "CONCAT(YEAR(FROM_UNIXTIME(create_time)), '-', LPAD(WEEK(FROM_UNIXTIME(create_time), 1), 2, '0'))";
- break;
- case 'day':
- default:
- $format = '%Y-%m-%d';
- $groupRaw = "FROM_UNIXTIME(create_time, '{$format}')";
- break;
- }
- // 统一转为时间戳
- $startTimestamp = !empty($params['start_time'])
- ? (is_numeric($params['start_time'])
- ? (int) $params['start_time']
- : strtotime($params['start_time']))
- : strtotime(date('2000-01-01 00:00:00')); // 默认起始时间
- $endTimestamp = !empty($params['end_time'])
- ? (is_numeric($params['end_time'])
- ? (int) $params['end_time']
- : strtotime($params['end_time']))
- : time(); // 默认当前时间
- if ($endTimestamp < $startTimestamp) {
- $endTimestamp = $startTimestamp;
- }
- // 基础查询
- $baseQuery = DB::table('member')
- ->selectRaw("{$groupRaw} as stat_date, COUNT(*) as reg_count")
- ->whereBetween('create_time', [$startTimestamp, $endTimestamp])
- ->groupBy('stat_date')
- ->orderBy('stat_date', 'desc');
- // 总数
- $total = DB::table(DB::raw("({$baseQuery->toSql()}) as t"))
- ->mergeBindings($baseQuery)
- ->count();
- // 分页数据
- $list = $baseQuery->forPage($page, $limit)->get();
- return [
- 'list' => $list,
- 'total' => $total,
- 'page' => $page,
- 'limit' => $limit,
- ];
- }
- /**
- * 根据旧状态和新状态计算VIP过期时间
- *
- * @param int|null $oldStatus 当前VIP状态 (0/1/2)
- * @param int $newStatus 新状态 (0/1/2)
- * @param string|null $oldExpire 当前过期时间
- * @return string|null 返回新的过期时间
- */
- public function calcVipExpire(?int $oldStatus, int $newStatus, ?string $oldExpire): ?string
- {
- if ($newStatus === 1) {
- // 从非VIP → VIP,才设置一年过期时间
- if ($oldStatus !== 1) {
- return date('Y-m-d H:i:s', time() + 365 * 24 * 3600);
- }
- // 已是VIP,保持原过期时间
- return $oldExpire;
- } else {
- // 0 或 2 → 清空过期时间
- return null;
- }
- }
- }
|