userDao = $userDao; $this->accountDao = $accountDao; $this->openService = $openService; } /** * 我的书架 * * @param $data * @return mixed */ public function getShelfBooks($data) { $uid = Site::getUid(); $result = Book::leftJoin('user_shelf_books', 'books.id', 'user_shelf_books.bid')->where('user_shelf_books.uid', $uid) ->select('books.id', 'books.name', 'books.cover', 'user_shelf_books.updated_at')->orderBy('user_shelf_books.updated_at', 'desc')->get()->toArray(); foreach ($result as &$item) { $recent_book = UserCache::getRecentBooksByBid($uid, $item['id']); if (!$recent_book) { $item['read_time'] = transDate($item['updated_at']); }else { $item['read_time'] = $recent_book['read_time']; } } $result = sortByField($result, 'read_time', 3); return $result; } /** * 添加书架 * * @param $data * @return mixed */ public function addShelf($data) { $uid = Site::getUid(); $bid = getProp($data, 'bid'); if (mb_strlen($bid) === 32) { $bid = Hashids::decode($bid)[0]; } if (!$bid) { Utils::throwError(ErrorConst::DATA_EXCEPTION); } // 统计(uv|pv) StatisticCache::setPV('add_shelf'); if ($uid) StatisticCache::setUV('add_shelf', $uid); return UserShelfBook::updateOrCreate([ 'uid' => $uid, 'bid' => $bid ], [ 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]); } /** * 删除书架 * * @param $data * @return mixed */ public function batchDeleteShelf($data) { $uid = Site::getUid(); $bids = explode(',', getProp($data, 'bids')); foreach ($bids as &$bid) { if (mb_strlen($bid) === 32) { $bid = Hashids::decode($bid)[0]; } } if (!$bids) { Utils::throwError(ErrorConst::DATA_EXCEPTION); } return UserShelfBook::where(['uid' => $uid])->whereIn('bid', $bids)->delete(); } /** * 最近阅读记录 * * @param $data * @return array|mixed */ public function getRecentBooks($data) { $uid = Site::getUid(); $recent_books = UserCache::getRecentBooksValues($uid); $arr = []; $shelf_bids = UserShelfBook::where('uid', $uid)->whereIn('bid', UserCache::getRecentBooksKeys($uid)) ->select('bid')->get()->pluck('bid')->toArray(); foreach ($recent_books as $item) { if (strtotime($item['read_time']) < time() - BaseConst::ONE_DAY_SECONDS) continue; if (in_array($item['bid'], $shelf_bids)) { $item['in_shelf'] = 1; } else { $item['in_shelf'] = 0; } $item['bid'] = Hashids::encode($item['bid']); $item['cid'] = Hashids::encode($item['cid']); $arr[] = $item; } $arr = sortByField($arr, 'read_time', 3); return $arr; } /** * 用户信息 * * @param $data * @return array */ public function getUserInfo($data) { $uid = Site::getUid(); $channel_type = Site::getChannelType(); $result = User::where('id', $uid)->select('nickname', 'head_img', 'from_uid', 'vip_limit_date', 'invite_code', 'alipay_username', 'alipay_account', 'balance', 'charge_balance', 'reward_balance')->first(); $result = $result ? $result->toArray() : []; $result['vip_limit_date'] = transDate($result['vip_limit_date'], 'Y-m-d'); if (date('Y-m-d') > $result['vip_limit_date']) $result['vip_limit_date']= ''; $result['from_uid'] = $result['from_uid'] ? 1 : 0; $result['channel_type'] = $channel_type; // 对提现账号进行加*处理 if ($result['alipay_account']) { if (strstr($result['alipay_account'], '@')) { $result['alipay_account'] = trans_pass($result['alipay_account'], 3); }else { $result['alipay_account'] = trans_pass($result['alipay_account'], 3, 2); } } return $result; } /** * 绑定用户 * * @param $data * @return mixed * @throws ApiException */ public function bindUser($data) { $code = getProp($data, 'invite_code'); if (!$code) Utils::throwError(ErrorConst::PARAM_ERROR_CODE); $uid = Site::getUid(); $from_uid = User::where('invite_code', $code)->value('id'); if ($from_uid) { $result = $this->bind($from_uid, $uid); if (!is_bool($result)) Utils::throwError('1010:' . $result); } else { Utils::throwError(ErrorConst::USER_IS_NOT_EXIST); } return true; } /** * @param $from_uid int 分享者用户id * @param $uid int 当前登录的用户id * @return bool|string */ public function bind($from_uid, $uid) { if ((int)$from_uid === (int)$uid) return '不得绑定自己'; if (!$uid) return '当前用户未登录'; if (User::where('id', $uid)->value('from_uid')) return '您已经绑定了用户,不可重复绑定'; // 特殊规则(绑定者与被绑定者不可互相成为对方的邀请人) if ((int)User::where('id', $from_uid)->value('from_uid') === (int)$uid) return '该用户已绑定'; try { DB::beginTransaction(); // 更新绑定的用户 $boolen = User::where('id', $uid)->update([ 'from_uid' => $from_uid, 'bind_time' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]); if (!$boolen) { Log::error('更新绑定的用户失败: ' . json_encode([ 'uid' => $uid, 'from_uid' => $from_uid, ], 256)); DB::rollBack(); Utils::throwError(ErrorConst::DB_INVALID); } // 更新邀请人的分享等级 $invite_num = User::where('from_uid', $from_uid)->count('id'); $share_level = DB::table('user_share_configs')->where('need_user_num', '<=', $invite_num)->orderBy('level', 'desc')->value('level'); $boolen2 = User::where('id', $from_uid)->update([ 'share_level' => $share_level, 'updated_at' => date('Y-m-d H:i:s') ]); if (!$boolen2) { Log::error('更新邀请人的分享等级失败: ' . json_encode([ 'uid' => $uid, 'from_uid' => $from_uid, 'share_level' => $share_level, ], 256)); DB::rollBack(); Utils::throwError(ErrorConst::DB_INVALID); } // 分享量达到指定量后送一天会员 $share_num = UserCache::getTodayShareNum($from_uid); $redis_key = 'vip_sended_' . $from_uid; if (!Redis::exists($redis_key) && ($share_num + 1) >= BaseConst::DAILY_SHARE_NUM) { // 今日未赠送会员并且分享数超过目标值 $send_vip_day = 1; $from_uid_info = User::find($from_uid); $origin_end_time = getProp($from_uid_info, 'vip_limit_date'); if (!$origin_end_time) $origin_end_time = date('Y-m-d H:i:s'); $vip_limit_date = date('Y-m-d 23:59:59', strtotime($origin_end_time . ' +'.$send_vip_day.' day')); $boolen3 = User::where('id', $from_uid)->update([ 'vip_limit_date' => $vip_limit_date, 'updated_at' => date('Y-m-d H:i:s') ]); if (!$boolen3) { Log::error('更新邀请人的会员有效期失败: ' . json_encode([ 'uid' => $uid, 'from_uid' => $from_uid, 'vip_limit_date' => $vip_limit_date, ], 256)); DB::rollBack(); Utils::throwError(ErrorConst::DB_INVALID); } $log_data = [ 'uid' => $from_uid, 'distribution_channel_id' => getProp($from_uid_info, 'distribution_channel_id'), 'fee' => 0, 'add_type' => 'share', 'send_order_id' => getProp($from_uid_info, 'send_order_id'), 'add_days' => $send_vip_day, 'trade_no' => '', 'origin_end_time' => $origin_end_time, 'end_time' => $vip_limit_date, 'vip_type' => '', 'desc' => '完成分享任务赠送'.$send_vip_day.'天会员', ]; $boolen4 = UserPeroidOrderLog::firstOrCreate($log_data); if (!$boolen4) { Log::error('会员有效期日志更新失败: ' . json_encode($log_data, 256)); DB::rollBack(); Utils::throwError(ErrorConst::DB_INVALID); } // 每日仅可享受一次赠送 $expire_at = strtotime(date('Y-m-d 23:59:59')) - time(); Redis::setex($redis_key, $expire_at, 1); } // 更新当日分享量 UserCache::setTodayShareNum($from_uid); // 更新redis保存的from_uid $token_data = UserCache::getTokenData(Site::getToken()); $token_data['from_uid'] = $from_uid; UserCache::setTokenData(Site::getToken(), $token_data); DB::commit(); } catch (Exception $e) { DB::rollBack(); return $e->getMessage(); } return true; } /** * 绑定派单链接 * @param $send_order_id * @return int * @throws ApiException */ public function bindSendOrder($send_order_id) { $send_order = DB::table('send_orders')->where('id', $send_order_id)->first(); if (!$send_order) return "该派单链接不存在,请确认后重试~"; $uid = Site::getUid(); $user_info = DB::table('users')->where('id', $uid)->select('distribution_channel_id', 'send_order_id')->first(); if (!$user_info) return '该用户不存在'; if (getProp($user_info, 'send_order_id')) return '该用户已绑定过派单链接'; return DB::table('users')->where('id', $uid)->update([ 'distribution_channel_id' => getProp($send_order, 'distribution_channel_id'), 'send_order_id' => getProp($send_order, 'id'), 'updated_at' => date('Y-m-d H:i:s') ]); } /** * 福利页 * * @param $data * @return array */ public function welfare($data) { $uid = Site::getUid(); $result = User::leftJoin('user_share_configs', 'user_share_configs.id', 'users.share_level')->where('users.id', $uid) ->select('users.total_earnings', 'users.enable_withdraw_earnings', 'users.share_level', 'user_share_configs.share_percent', DB::raw("(select need_user_num from user_share_configs as us2 where us2.level > users.share_level order by level limit 1) as need_user_num"), DB::raw("(select count(id) from users as u2 where u2.from_uid = users.id) as invite_user_num"))->first(); $result = $result ? $result->toArray() : []; $result['next_level'] = $result['share_level'] + 1; // 不得超过最高等级 $max_level = DB::table('user_share_configs')->orderBy('level', 'desc')->value('level'); if ($result['next_level'] >= $max_level) $result['next_level'] = $max_level; // 升级所需人数 $result['upgrade_percent'] = $result['need_user_num'] ? round($result['invite_user_num'] / $result['need_user_num'] * 100) : 0; $result['need_user_num'] = $result['need_user_num'] ? strval($result['need_user_num'] - $result['invite_user_num']) : -1; $result['share_percent'] = round($result['share_percent'] * 100) . '%'; // 今日分享 $result['today_share_num'] = UserCache::getTodayShareNum($uid); // 当前阅读书籍 $current_chapter = UserCache::getCurrentChapter($uid); if ($current_chapter) { $result['bid'] = Hashids::encode($current_chapter['bid']); } // 统计(uv|pv) StatisticCache::setPV('welfare'); if ($uid) StatisticCache::setUV('welfare', $uid); return $result; } /** * 我的收益 * * @param $data * @return array */ public function earnings($data) { $uid = Site::getUid(); $result = []; $result['earnings'] = User::where('id', $uid)->select('total_earnings', 'enable_withdraw_earnings')->first(); $result['earnings'] = $result['earnings'] ? $result['earnings']->toArray() : []; $result['earning_records'] = UserEarningsLogs::leftJoin('users', 'users.id', 'user_earnings_logs.from_uid') ->where('user_earnings_logs.uid', $uid)->orderBy('user_earnings_logs.created_at', 'desc')->select('user_earnings_logs.*', 'users.nickname')->get()->toArray(); $result['invite_records'] = User::where('from_uid', $uid)->orderBy('bind_time', 'desc')->select('nickname', 'bind_time')->get()->toArray(); return $result; } /** * 提现档位 * * @param $data * @return array */ public function withdrawConfigs($data) { $uid = Site::getUid(); $result = WithdrawConfigs::select('withdraw_type', 'price')->orderBy('sort', 'asc')->get()->toArray(); $list = []; $user = User::where('id', $uid)->select('is_new', 'total_earnings', 'enable_withdraw_earnings')->first(); if (!$user) Utils::throwError(ErrorConst::USER_IS_NOT_EXIST); $user = $user->toArray(); $new_user = ['price' => '1.00', 'info' => '新人特权']; $withdraw_type_arr = [ 'WECHAT' => '微信提现', 'ZHIFUBAO' => '支付宝提现' ]; foreach ($result as $item) { if (!isset($list[$item['withdraw_type']])) { $list[$item['withdraw_type']] = [ 'withdraw_type' => $item['withdraw_type'], 'withdraw_info' => isset($withdraw_type_arr[$item['withdraw_type']]) ? $withdraw_type_arr[$item['withdraw_type']] : '', 'withdraw_price' => [ [ 'price' => $item['price'], 'info' => '' ], ] ]; if ($user['is_new']) array_unshift($list[$item['withdraw_type']]['withdraw_price'], $new_user); } else { $list[$item['withdraw_type']]['withdraw_price'][] = ['price' => $item['price'], 'info' => '']; } } $arr['withdraw_data'] = array_values($list); $arr['earnings'] = $user; return $arr; } /** * 提现 * * @param $data * @return mixed * @throws ApiException */ public function withdraw($data) { $amount = getProp($data, 'amount'); $withdraw_type = getProp($data, 'withdraw_type'); $alipay_username = getProp($data, 'alipay_username'); $alipay_account = getProp($data, 'alipay_account'); $uid = Site::getUid(); $user = User::find($uid); // 验证alipay_account if (strstr($alipay_account, '@')) { // 如果传参的邮箱和加*的一致则用户使用的是上次提现账号 if ($alipay_account == trans_pass($user->alipay_account, 3)) { $alipay_account = $user->alipay_account; } // // 验证手机号 // if (!preg_match('/^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8}$/', $alipay_account)) Utils::throwError('1002:手机号格式不正确'); } else { // 如果传参的手机号和加*的一致则用户使用的是上次提现账号 if ($alipay_account == trans_pass($user->alipay_account, 3, 2)) { $alipay_account = $user->alipay_account; } // 验证邮箱 // if (!preg_match('/^[_.0-9a-z-]+@([0-9a-z][0-9a-z-]+.)+[a-z]{2,3}$/', $alipay_account)) Utils::throwError('1002:邮箱格式不正确'); } if ($amount == 1) { if (!$user->is_new) Utils::throwError('1002:不支持的提现额度'); } else { $access_amount = WithdrawConfigs::where('withdraw_type', $withdraw_type)->get()->pluck('price')->toArray(); if (!in_array($amount, $access_amount)) Utils::throwError('1002:不支持的提现额度'); } if ($amount > $user->enable_withdraw_earnings) Utils::throwError('1002:您的余额不足,无法提现'); $distribution_channel_info = DB::table('distribution_channels')->where('id', Site::getChannelId())->first(); if (!$distribution_channel_info) Utils::throwError(ErrorConst::DATA_EXCEPTION); // 写入申请提现表 $withdraw_data = [ 'uid' => $uid, 'distribution_channel_id' => $distribution_channel_info->id, 'distribution_channel_name' => $distribution_channel_info->name, 'amount' => $amount, 'status' => '待审核', 'withdraw_type' => $withdraw_type, 'withdraw_info' => '', 'alipay_username' => $alipay_username, 'alipay_account' => $alipay_account, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]; // 写入收益变动日志 $withdraw_log = [ 'uid' => $uid, 'from_uid' => 0, 'charge_price' => 0, 'share_percent' => 0, 'withdraw_info' => '提现', 'change_type' => 2, 'before_amount' => $user->enable_withdraw_earnings, 'change_amount' => $amount, 'after_amount' => round(($user->enable_withdraw_earnings - $amount), 2), 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]; try { DB::beginTransaction(); $boolen = DB::table('user_withdraw_cashes')->insert($withdraw_data); if (!$boolen) { Log::error('申请提现失败, 错误信息: ' . json_encode($withdraw_data, 256)); DB::rollBack(); Utils::throwError(ErrorConst::DB_INVALID); } $boolen1 = UserEarningsLogs::create($withdraw_log); if (!$boolen1) { Log::error('写入收益变动日志失败, 错误信息: ' . json_encode($withdraw_log, 256)); DB::rollBack(); Utils::throwError(ErrorConst::DB_INVALID); } $user->enable_withdraw_earnings -= $amount; $user->alipay_username = $alipay_username; $user->alipay_account = $alipay_account; $user->is_new = 0; $boolen2 = $user->save(); if (!$boolen2) { Log::error('用户可提现收益更新失败, 错误信息: ' . json_encode($user, 256)); DB::rollBack(); Utils::throwError(ErrorConst::DB_INVALID); } DB::commit(); } catch (Exception $e) { DB::rollBack(); Utils::throwError('1010:' . $e->getMessage()); } return true; } /** * 同步用户数据 * * @param string $iv * @param string $signature * @param string $encryptedData * @param string $rawData * @param $userInfo * @return bool * @throws ApiException */ public function syncUserInfo(string $iv, string $signature, string $encryptedData, string $rawData, $userInfo): bool { $uid = Site::getUid(); $user = $this->userDao->getUserById($uid); $sessionKey = getProp($user, 'session_key'); $openId = getProp($user, 'openid'); if (empty($user)) { Utils::throwError(ErrorConst::USER_IS_NOT_EXIST); } dLog('user')->info('syncUserInfo-params', compact('uid', 'sessionKey', 'openId', 'iv', 'signature', 'encryptedData', 'rawData', 'userInfo')); // 获取用户session_key并初始化 // $instance = $this->openService->getInstance(); /* // 校验用户数据有效性 if ($signature && $rawData) { $isValid = $instance->checkUserDataValid($rawData, $sessionKey, $signature); if (!$isValid) { Utils::throwError(ErrorConst::USER_DATA_IS_VALID); } } // 解密数据 $decryptedData = json_decode($rawData, true); if ($encryptedData && $iv) { $decryptedData = $instance->decryptUserData($encryptedData, $sessionKey, $iv); } dLog('user')->info('syncUserInfo-decryptedData', compact('uid', 'decryptedData')); */ $decryptedData = $userInfo; $updateData = ['updated_at' => date('Y-m-d H:i:s')]; if (isset($decryptedData['avatarUrl'])) $updateData['head_img'] = $decryptedData['avatarUrl']; if (isset($decryptedData['nickName'])) $updateData['nickname'] = $decryptedData['nickName']; if (isset($decryptedData['gender'])) $updateData['sex'] = $this->transGender((int)$decryptedData['gender']); if (isset($decryptedData['city']) && $decryptedData['city']) $updateData['city'] = (string)$decryptedData['city']; if (isset($decryptedData['province']) && $decryptedData['province']) $updateData['province'] = (string)$decryptedData['province']; if (isset($decryptedData['country']) && $decryptedData['province']) $updateData['country'] = (string)$decryptedData['country']; if (count($updateData) < 2) { return false; } /* // 用户openid判断匹配 if ($openId != getProp($decryptedData, 'openid')) { return false; } // appid判断匹配 $waterMark = getProp($decryptedData, 'watermark', []); $appId = getProp($waterMark, 'appid'); $minApp = $this->accountDao->getMiniAppByAppId($appId); if ($appId != getProp($minApp, 'appid')) { return false; } */ return $this->userDao->updateUserById($uid, $updateData); } /** * 转换性别 * * @param int $gender * @return int */ private function transGender(int $gender): int { $sex = 2; if ($gender == 1) { $sex = 0; } if ($gender == 2) { $sex = 1; } return $sex; } }