123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- <?php
- namespace App\Services\User;
- use App\Cache\StatisticCache;
- use App\Cache\UserCache;
- use App\Consts\BaseConst;
- use App\Consts\ErrorConst;
- use App\Dao\Account\AccountDao;
- use App\Dao\User\UserDao;
- use App\Facade\Site;
- use App\Libs\Utils;
- use App\Models\Book\Book;
- use App\Models\User\User;
- use App\Models\User\UserEarningsLogs;
- use App\Models\User\UserPeroidOrderLog;
- use App\Models\User\UserShelfBook;
- use App\Models\User\WithdrawConfigs;
- use App\Services\OpenApi\OpenService;
- use Exception;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Log;
- use Illuminate\Support\Facades\Redis;
- use Vinkla\Hashids\Facades\Hashids;
- use App\Exceptions\ApiException;
- class UserService
- {
- private $userDao;
- private $accountDao;
- private $openService;
- public function __construct(
- UserDao $userDao,
- AccountDao $accountDao,
- OpenService $openService
- )
- {
- $this->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;
- }
- }
|