request->user['user_id'] ?? 0; return json(['code' => 0, 'msg' => 'Index page , I am '.$userId]); } /** * 登录 成功返回token及用户信息 * * @return Json * @throws InvalidConfigException */ public function login(): Json { $params = $this->request->param(); $validate = new UserValidate(); if (!$validate->scene('wx_applets')->check($params)) { return $this->json(4000, $validate->getError()); } $minApp = WechatApplets::getInstance(); $jsCode = $params['code']; $wxUser = $minApp->auth->session($jsCode); if (isset($wxUser['errcode']) && $wxUser['errcode'] != 0) { return $this->json(4001, $wxUser['errcode'].';'.$wxUser['errmsg'] ?? '登录失败'); } // $wxUser success has [session_key, openid, unionid] // 有效期2小时 $wxUser['expire_time'] = time() + 7200; $wxUser['session_key'] = $wxUser['session_key'] ?? ''; $openID = $wxUser['openid']; if (empty($openID)) { return $this->json(4002, '登录失败'); } $isActive = $params['is_active'] ?? 0; $isActive = (is_numeric($isActive) && $isActive > 0) ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE; $phoneActive = $params['phone_active'] ?? 0; $phoneActive = (is_numeric($phoneActive) && $phoneActive > 0) ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE; try { $repo = AccountRepository::getInstance(); $account = $repo->findByOpenID($openID); $inviteCode = $params['invite_code'] ?? ''; $inviteSource = $params['invite_source'] ?? AccountRepository::INVITE_SOURCE_DEF; $channel = $params['channel'] ?? ''; if (!$account) { // 自动注册 $inviterAid = 0; $inviterParentAid = 0; $inviter = null; $customerService = 0;// 所属客服 if (!empty($inviteCode)) { $inviter = $repo->findByInviteCode($inviteCode); if ($inviter) { $inviterAid = $inviter['id']; $inviterParentAid = $inviter['inviter_account_id']; $channel = (empty($channel) && $inviter['is_staff'] > 0) ? AccountRepository::CHANNEL_MEMBER : AccountRepository::CHANNEL_CUSTOMER; if ($inviter['is_staff'] > 0) { //若分享人是员工 自动绑定为客服 $customerService = $inviter['id']; } } } // 活码进入 绑定客服 if (isset($params['source_code']) && !empty($params['source_code'])) { if ($sourceUser = Activity::findOne(['code' => $params['source_code']])) { $customerService = $sourceUser['account_id'] ?? 0; } } $inviteSource = in_array($inviteSource, array_keys(AccountRepository::inviteSourceList())) ? $inviteSource : AccountRepository::INVITE_SOURCE_DEF; $channelList = $repo->channelList(); $channel = (empty($channel) || !in_array($channel, array_keys($channelList))) ? AccountRepository::CHANNEL_NORMAL : $channel; $account = $repo->create([ 'unionid' => $wxUser['unionid'] ?? '', 'openid' => $openID, 'last_login' => date('Y-m-d H:i:s'), 'login_ip' => $this->request->ip(), 'created_at' => date('Y-m-d H:i:s'), 'created_year' => date('Y'), 'created_month' => date('n'), 'created_day' => date('j'), 'nickname' => $params['nickname'] ?? '', 'headimgurl' => $params['headimgurl'] ?? '', 'country' => $params['country'] ?? '', 'province' => $params['province'] ?? '', 'city' => $params['city'] ?? '', 'county' => $params['county'] ?? '', 'gender' => $params['gender'] ?? 0, 'language' => $params['language'] ?? 'zh_CN', 'mobile' => $params['mobile'] ?? '', 'status' => AccountRepository::STATUS_NORMAL, 'invite_source' => $inviteSource, 'inviter_account_id' => $inviterAid, 'inviter_parent_id' => $inviterParentAid, 'channel' => $channel, 'is_staff' => AccountRepository::BOOL_FALSE, 'is_active' => $isActive, 'phone_active' => $phoneActive, 'customer_service' => $customerService, 'source_code' => $params['source_code'] ?? '', 'session_key' => $wxUser['session_key'] ?? '', ]); // 存在所属客服 添加绑定记录 if ($customerService > 0) { CustomerReceive::firstBoundService($account['id'], $customerService); } $regAccountId = $account->id ?? 0; $accountActive = $phoneActive > 0; if ($inviter) { $repo->addShareRegLog($regAccountId, $inviter['id'], AccountRepository::SHARE_GRADE_FIRST, $accountActive); if ($inviterParentAid > 0) { $repo->addShareRegLog($regAccountId, $inviterParentAid, AccountRepository::SHARE_GRADE_SECOND, $accountActive); } // 邀请人绑定的客服可获得积分, 有效用户才关联 if ($accountActive) { $boundServiceAid = $repo->getBoundServiceAId($inviter['id']); if ($boundServiceAid > 0) { $repo->addShareRegLog($regAccountId, $boundServiceAid, AccountRepository::SHARE_GRADE_SERVICE, true); } } } AccountRecord::record($regAccountId, AccountRecord::TYPE_OTHER, AccountRecord::ACTION_REGISTER); } else { $updateData = [ 'last_login' => date('Y-m-d H:i:s'), 'login_ip' => $this->request->ip(), 'session_key' => $wxUser['session_key'] ?? '', 'language' => $params['language'] ?? 'zh_CN', ]; $phoneActiveOld = $account['phone_active']; // 更新资料 $modifyStringList = ['headimgurl', 'nickname', 'mobile', 'country', 'province', 'city', 'county']; foreach ($modifyStringList as $modifyKey) { if (isset($account[$modifyKey]) && empty($account[$modifyKey])) { $updateData[$modifyKey] = $params[$modifyKey] ?? ''; } } if (empty($account['gender'])) { $updateData['gender'] = $params['gender'] ?? 0; } if (isset($account['is_active']) && $account['is_active'] == AccountRepository::BOOL_FALSE) { $updateData['is_active'] = $isActive; } if (isset($account['phone_active']) && $account['phone_active'] == AccountRepository::BOOL_FALSE) { $updateData['phone_active'] = $phoneActive; } $repo->update($updateData, ['id' => $account['id']]); $account = $repo->findById($account['id']); // 授权手机号后才能算有效激活用户,并更新相关业务数据 if ($phoneActiveOld == AccountRepository::BOOL_FALSE && $account['phone_active'] == AccountRepository::BOOL_TRUE) { if ($account['inviter_account_id'] > 0) { $repo->addShareRegLog($account['id'], $account['inviter_account_id'], AccountRepository::SHARE_GRADE_FIRST, true); // 邀请人绑定的客服可获得积分 $boundServiceAid = $repo->getBoundServiceAId($account['inviter_account_id']); if ($boundServiceAid > 0) { $repo->addShareRegLog($account['id'], $boundServiceAid, AccountRepository::SHARE_GRADE_SERVICE, true); } } if ($account['inviter_parent_id'] > 0) { $repo->addShareRegLog($account['id'], $account['inviter_parent_id'], AccountRepository::SHARE_GRADE_SECOND, true); } } } } catch (RepositoryException | Exception $e) { return $this->json(4003, '登录失败!'.$e->getMessage()); } $account = $account->toArray(); $jwtData = [ 'user_id' => $account['id'], 'open_id' => $openID, 'session_key' => $wxUser['session_key'], 'expire_time' => $wxUser['expire_time'], ]; $account['headimgurl'] = File::convertCompleteFileUrl($account['headimgurl']); $fields = [ 'coding', 'real_name', 'nickname', 'headimgurl', 'gender', 'mobile', 'province', 'city', 'county', 'country', 'birthday', 'score', 'status', 'position', 'invite_code', 'channel', 'is_staff', 'is_active', 'phone_active' ]; $accountData = arrayKeysFilter($account, $fields); $account['is_active'] = ($account['is_active'] == Account::COMMON_ON && $account['phone_active'] == Account::COMMON_ON); $data = [ 'account_id' => $account['id'], 'token' => Jwt::generate($jwtData), 'expire' => $wxUser['expire_time'], 'openid' => $openID, ]; $data = array_merge($data, $accountData); return $this->json(0, 'success', $data); } /** * 获取用户信息 * * @return Json */ public function info(): Json { try { $accountId = $this->request->user['user_id'] ?? 0; $fields = [ 'id', 'coding', 'real_name', 'nickname', 'headimgurl', 'gender', 'mobile', 'province', 'city', 'county', 'country', 'birthday', 'score', 'status', 'position', 'invite_code', 'channel', 'is_staff', 'is_active', "continuity_sign", "last_sign_online", 'coin_total', 'coin', 'phone_active' ]; $repo = AccountRepository::getInstance(); $user = $repo->infoWithRelation($accountId, $fields); //更新连续签到次数 AccountRepository::getInstance()->checkContinuitySign($user); if (empty($user)) { return $this->json(4004, '没有相关的用户记录'); } $user->headimgurl = File::convertCompleteFileUrl($user->headimgurl ?? ''); // 职工身份,校验纠正当前用户是否为职工 $isWorker = (empty($user->worker) || $user->worker['status'] == Staff::STATUS_DISABLE) ? self::BOOL_FALSE : self::BOOL_TRUE; $user['is_staff'] = $isWorker ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE; /** * 补充信息: * 文章收藏量、分享注册用户量(包含2级) */ $user->collects = $repo->countRecordByAction(AccountRecord::TYPE_CONTENT, AccountRecord::ACTION_COLLECT, $accountId); $user->share_users = $repo->shareAccountCount($accountId); $user->unread_messages = $repo->countUnReadMessage($accountId); $user = $user->toArray(); $user = arrayNullToString($user); $user['rules'] = $repo->getAccountRules($user['id']); $user['order_count'] = OrderRepository::getInstance()->orderCount($accountId); $user["level"] = $repo->getUserLevel($user['coin_total']); unset($user['worker']); return $this->json(0, 'success', $user); } catch (Exception $e) { return $this->json(4000, '没有相关的用户记录'.$e->getMessage()); } } /** * 首页优惠券 * * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function homeCoupon(): Json { $accountId = $this->request->user['user_id'] ?? 0; //是否有可领取的推荐优惠券(进入首页时弹出) $homeCoupon = AccountCoupon::homeCoupon($accountId); $data['has_coupon'] = (int) !empty($homeCoupon); $data['home_coupon'] = $homeCoupon; return $this->json(0, 'success', $data); } /** * 修改用户资料 * 修改内容范围:姓名、昵称、联系电话、性别、地址(省、市、区/县) * array $params 键值对形式 */ public function editInfo() { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $params = $this->request->param(); $validate = new UserValidate(); if (!$validate->scene('edit')->check($params)) { return $this->json(4001, $validate->getError()); } $accountId = $this->request->user['user_id'] ?? 0; try { AccountRepository::getInstance()->accountEditInfo($accountId, $params); } catch (RepositoryException $e) { return $this->json(4002, $e->getMessage()); } catch (Exception $e) { return $this->json(5000, '资料修改失败'); } return $this->json(); } /** * 临时登录 通过openid登录 仅用于接口测试阶段 * * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function tempLogin(): Json { $params = $this->request->param(); if (!isset($params['openid'])) { return $this->json(4001, '参数错误'); } if (!$user = AccountRepository::getInstance()->findByOpenID($params['openid'])) { return $this->json(4004, '账号不存在'); } $data = [ 'token' => Jwt::generate(['user_id' => $user['id'], 'nickname' => $user['nickname']]), 'expire' => Jwt::expire() ]; return $this->json(0, 'success', $data); } /** * 获取自主预约的参数信息 * 支持7天内预约 * * @return Json */ public function appointmentParameters(): Json { $resp = [ 'types' => [], 'days' => [], 'times' => [], 'types_appointment' => [], ]; try { $repo = AccountRepository::getInstance(); $timesResp = $repo->appointmentPeriodNormalList(); $typesResp = $repo->appointmentTypes(['id', 'title', 'max']); $days = $repo->appointmentAllowDays(); $times = $timesResp['list']->toArray(); $types = $typesResp->toArray(); $nowTime = time(); $typesAppointment = []; $hadCountList = $repo->appointmentPeriodGroupCount(reset($days), end($days)); foreach ($types as $type) { $allowMax = $type['max']; $daysTimes = []; foreach ($days as $day) { $dayTimes = []; foreach ($times as $time) { $hadCount = $hadCountList[$type['id']][$day][$time['id']] ?? 0; $isFull = false; $disable = false; $timeList = $repo->parsePeriodToTime($time['name']); $endTime = 0; if (!empty($timeList)) { $endTime = strtotime($day.' '.$timeList['end']); } if ($hadCount >= $allowMax) { $isFull = true; $disable = true; } if ($endTime <= $nowTime) { $disable = true; } $time['is_full'] = $isFull ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE; $time['disable'] = $disable ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE; $dayTimes[] = $time; } $daysTimes[] = [ 'day' => $day, 'times' => $dayTimes ]; } $typesAppointment[] = [ 'type' => $type, 'days_times' => $daysTimes ]; } // 现阶段暂不做工作日排班安排,默认7天内均可预约 $daysList = []; $weekList = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; $weekStatus = AccountRepository::BOOL_TRUE; foreach ($days as $day) { $daysList[] = [ 'week' => $weekList[date('w', strtotime($day))], 'day' => $day, 'status' => $weekStatus ]; } $resp = [ 'types' => $types, 'days' => $daysList, 'times' => $times, 'types_appointment' => $typesAppointment, ]; } catch (TraitException $e) { } return $this->json(0, 'success', $resp); } /** * 查询指定预约类型下某天禁选的时间段 * 已约满、时间段已过(超时) */ public function appointmentPeriodFull(): Json { $typeId = $this->request->param('type_id/d', 0); $appointmentDay = $this->request->param('day/s', ''); if ($typeId <= 0 || empty($appointmentDay) || !strtotime($appointmentDay)) { return $this->json(); } $appointmentDay = date('Y-m-d', strtotime($appointmentDay)); try { $repo = AccountRepository::getInstance(); $type = $repo->appointmentTypeInfo($typeId); if (empty($type)) { return $this->json(); } $hadCountList = $repo->appointmentPeriodGroupCount($appointmentDay, $appointmentDay, [$typeId]); $typeDayCounts = $hadCountList[$typeId][$appointmentDay] ?? []; $timesResp = $repo->appointmentPeriodNormalList(); $times = $timesResp['list']->toArray(); $nowTime = time(); $disablePeriods = []; foreach ($times as $time) { $hadCount = $typeDayCounts[$time['id']] ?? 0; $timeList = $repo->parsePeriodToTime($time['name']); $endTime = 0; $disable = false; $isFull = false; if (!empty($timeList)) { $endTime = strtotime($appointmentDay.' '.$timeList['end']); } if ($endTime <= $nowTime || $hadCount >= $type['max']) { $disable = true; $isFull = ($hadCount >= $type['max']); } if ($disable) { $time['is_full'] = $isFull ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE; $time['disable'] = AccountRepository::BOOL_TRUE; $disablePeriods[] = $time; } } return $this->json(0, 'success', $disablePeriods); } catch (TraitException $e) { return $this->json(); } } /** * 提交预约申请 */ public function appointmentApply(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; $params = $this->request->param(); try { AccountRepository::getInstance()->addAppointment($accountId, $params); } catch (TraitException $e) { return $this->json(4001, $e->getMessage()); } return $this->json(); } /** * 用户预约记录 * * @return Json */ public function appointmentList(): Json { $accountId = $this->request->user['user_id'] ?? 0; $page = $this->request->param('page/d', 1); $size = $this->request->param('size/d', 10); $page = $page <= 0 ? 1 : $page; $size = $size <= 0 ? 10 : $size; try { $repo = AccountRepository::getInstance(); $resp = $repo->appointmentList($accountId, $page, $size); $statusTextList = $repo->appointmentStatusTextList(); foreach ($resp['list'] as $item) { $item['status_text'] = ''; // 现阶段预约完成,小程序端不显示状态描述 if ($item['status'] != Appointment::STATUS_OVER) { $item['status_text'] = $statusTextList[$item['status']] ?? ''; } } } catch (TraitException $e) { $resp = [ 'total' => 0, 'current' => $page, 'size' => $size, 'list' => new Collection(), ]; } return $this->json(0, 'success', $resp); } /** * 取消预约 */ public function appointmentCancel(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; $appointId = $this->request->param('appointment_id/d', 0); try { AccountRepository::getInstance()->cancelAppointment($accountId, $appointId); } catch (TraitException $e) { return $this->json(4001, $e->getMessage()); } return $this->json(); } /** * 搜索历史 * * @return Json */ public function searchHistory(): Json { $page = input('page/d', 1); $size = input('size/d', 10); $accountId = $this->request->user['user_id'] ?? 0; try { $data = AccountRecord::findSearch($accountId, $page, $size); return $this->json(0, 'success', $data); } catch (RepositoryException $e) { return $this->json(4001, $e->getMessage()); } catch (Exception $e) { ArchivesRepository::log($e->getMessage(), $e); return $this->json(5000, '操作失败'); } } /** * 搜索历史 * * @return Json */ public function clearSearch(): Json { $accountId = $this->request->user['user_id'] ?? 0; try { AccountRecord::clearSearch($accountId); return $this->json(0, 'success'); } catch (Exception $e) { ArchivesRepository::log($e->getMessage(), $e); return $this->json(5000, '操作失败'); } } /** * 个人分享中心 * 统计:累计积分、累计绑定的客户数量(包含2级分销) */ public function shareCount(): Json { $accountId = $this->request->user['user_id'] ?? 0; $res = [ 'score' => 0, 'user_count' => [ 'first' => 0, 'second' => 0, 'total' => 0, ], 'order_count' => 0, ]; try { $repo = AccountRepository::getInstance(); $account = $repo->findById($accountId); if (!empty($account)) { $res['score'] = $account['score'] ?? 0; $res['user_count'] = $repo->shareAccountCount($accountId); } } catch (Exception $e) { } return $this->json(0, 'success', $res); } /** * 查看分享绑定用户的信息 */ public function shareUsers(): Json { $accountId = $this->request->user['user_id'] ?? 2; $page = $this->request->param('page/d', 1); $size = $this->request->param('size/d', 10); $grade = $this->request->param('grade/s', 'first'); // 层级[first|second] if (!in_array($grade, [AccountRepository::SHARE_GRADE_FIRST, AccountRepository::SHARE_GRADE_SECOND])) { return $this->json(4001, '层级参数错误'); } $page = $page <= 0 ? 1 : $page; $size = $size <= 0 ? 10 : $size; $list = AccountRepository::getInstance()->shareUsers($accountId, $grade, $page, $size); return $this->json(0, 'success', $list); } /** * 生成用户个人海报 */ public function personalPoster(): Json { $accountId = $this->request->user['user_id'] ?? 0; $bgImg = $this->request->param('bg_src/s', ''); $bgImg = empty($bgImg) ? '' : File::convertCompleteFileUrl($bgImg); try { $account = AccountRepository::getInstance()->findById($accountId); if (empty($account)) { return $this->json(4000, '无效请求'); } $domain = $this->request->domain(); $inviteCode = $account['invite_code'] ?: md5($account['id']); $inviteUrl = $domain.'/share?invite_code='.$inviteCode; $logoImg = app()->getRootPath().'public/static/images/icon-logo.jpg'; $result = Builder::create() ->writer(new PngWriter()) ->writerOptions([]) ->data($inviteUrl) ->encoding(new Encoding('UTF-8')) ->errorCorrectionLevel(new ErrorCorrectionLevelHigh()) ->size(300) ->margin(10) ->roundBlockSizeMode(new RoundBlockSizeModeMargin()) ->logoPath($logoImg) ->logoResizeToHeight(100) ->logoResizeToWidth(100) ->logoPunchoutBackground(true) ->build(); $dataUri = $result->getDataUri(); $posterData = GdTool::generatePoster($dataUri, $bgImg); return $this->json(0, 'success', ['poster' => $posterData]); } catch (Exception $e) { AccountRepository::log('个人海报生成失败', $e); return $this->json(5000, '个人海报生成失败'); } } /** * 用户操作记录 分享、咨询 * * @return Json * @throws Exception */ public function record(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; $params = input('post.'); $rules = [ 'type|操作类型' => 'require|in:content,other', 'action|操作' => 'require', ]; if (isset($params['type']) && $params['type'] == AccountRecord::TYPE_CONTENT) { $rules['id|ID'] = 'require'; } $validate = $this->validateByApi($params, $rules, ['type.in' => '类型错误', 'id.require' => '此类型 ID必传']); if ($validate !== true) { return $validate; } try { $relationId = $params['id'] ?? 0; $relationId = is_numeric($relationId) ? $relationId : 0; AccountRecord::record($accountId, $params['type'], $params['action'], $relationId); } catch (Exception $e) { AccountRepository::log('记录用户操作失败', $e); return $this->json(5001, '操作失败'); } return $this->json(); } /** * 个人二维码 */ public function personalQr(): Json { $accountId = $this->request->user['user_id'] ?? 0; try { $account = AccountRepository::getInstance()->findById($accountId); if (empty($account)) { return $this->json(4000, '无效请求'); } $nonceStr = uniqid(); $qrDataStr = json_encode([ 'user_coding' => $account['coding'], 'nonce_str' => $nonceStr, ]); $logoImg = File::convertCompleteFileUrl($account['headimgurl']); if (empty($logoImg)) { $logoImg = app()->getRootPath().'public/static/images/icon-logo.jpg'; } $result = Builder::create() ->writer(new PngWriter()) ->writerOptions([]) ->data($qrDataStr) ->encoding(new Encoding('UTF-8')) ->errorCorrectionLevel(new ErrorCorrectionLevelHigh()) ->size(300) ->margin(10) ->roundBlockSizeMode(new RoundBlockSizeModeMargin()) ->logoPath($logoImg) ->logoResizeToHeight(100) ->logoResizeToWidth(100) ->logoPunchoutBackground(true) ->build(); $dataUri = $result->getDataUri(); return $this->json(0, 'success', ['qr' => $dataUri, 'nonce_str' => $nonceStr]); } catch (Exception $e) { AccountRepository::log('个人二维码生成失败', $e); return $this->json(5000, '个人二维码生成失败'); } } /** * 用户签到 * 线下客服扫码:绑定客户关系 */ public function signIn(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } Log::info('用户签到绑定:'); Log::info(json_encode($this->request->param(), JSON_UNESCAPED_UNICODE)); $accountId = $this->request->user['user_id'] ?? 0; $userCoding = $this->request->param('user_coding/s', ''); $nonceStr = $this->request->param('nonce_str/s', ''); if (empty($userCoding)) { return $this->json(4000, '无效请求'); } try { $repo = AccountRepository::getInstance(); $signAccount = $repo->findByUserCoding($userCoding); if (empty($signAccount)) { return $this->json(4001, '签到失败,没有相关的用户记录'); } // 记录扫码日志 ScanLog::log($signAccount['id'], $accountId, $nonceStr); $boundSign = $repo->getBoundService($signAccount['id'], CustomerReceive::ACTION_OFFLINE_SIGN); if ($boundSign) { $signAccount->save(['is_sign' => Account::COMMON_ON]); return $this->json(0, '签到成功'); } $signData = [ 'customer_aid' => $signAccount['id'], 'worker_aid' => $accountId, 'created_at' => date('Y-m-d H:i:s'), 'action' => CustomerReceive::ACTION_OFFLINE_SIGN, 'pid' => 0, 'path' => ',0,' ]; $boundService = $repo->getBoundService($signAccount['id'], CustomerReceive::ACTION_ONLINE_SERVICE); if ($boundService) { $signData['pid'] = $boundService['id']; $signData['path'] = $boundService['path'].$boundService['id'].','; } CustomerReceive::create($signData); $signAccount->save(['is_sign' => Account::COMMON_ON, 'customer_service' => $accountId]); } catch (Exception $e) { return $this->json(5000, '签到失败'); } return $this->json(); } /** * 二维码打卡 * 线下二维码扫码器扫码打卡 */ public function signByQr() { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } Log::info('扫码器提交内容:'); Log::info(file_get_contents("php://input")); try { $input = file_get_contents("php://input"); // $input = 'vgdecoderesult={"user_coding":"210000000013","nonce_str":"618e1ad5bdded"}&&devicenumber=111&&otherparams='; $info = []; parse_str($input, $info); $parseInfo = json_decode($info['vgdecoderesult'], true); $userCoding = $parseInfo['user_coding'] ?? ''; $nonceStr = $parseInfo['nonce_str'] ?? ''; $deviceNumber = $info['devicenumber'] ?? ''; $repo = AccountRepository::getInstance(); $signAccount = $repo->findByUserCoding($userCoding); if (empty($signAccount)) { return 'code=4000'; } // 记录扫码日志 ScanLog::log($signAccount['id'], 0, $nonceStr, ScanLog::TYPE_SIGN, ScanLog::ACTION_SCAN_BY_MACHINE, $deviceNumber); return 'code=0000'; } catch (Exception $e) { return 'code=4000'; } } /** * 检测扫码是否成功 * * @return Json */ public function checkScan(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; if (empty($accountId)) { return $this->json(4002, '请先登录'); } $nonceStr = $this->request->param('nonce_str/s', ''); if (empty($nonceStr)) { return $this->json(4001, '随机字符串必填'); } return $this->json(0, 'success', ['result' => (int) ScanLog::isScan($accountId, $nonceStr)]); } /** * 投诉与建议类型选择列表 * (问题类型列表) */ public function feedbackTypes(): Json { $list = AccountRepository::getInstance()->feedbackTypes(); return $this->json(0, 'success', $list); } /** * 提交反馈意见 */ public function addFeedback(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; $params = [ 'type_id' => $this->request->param('type_id/d', 0), 'content' => postFilter($this->request->param('content/s', '')), 'user_name' => postFilter($this->request->param('user_name/s', '')), 'user_phone' => postFilter($this->request->param('user_phone/s', '')), ]; try { AccountRepository::getInstance()->addFeedback($accountId, $params); } catch (TraitException $e) { return $this->json(4001, $e->getMessage()); } return $this->json(0, '您已提交成功,感谢您的反馈!'); } /** * 用户消息 */ public function messages(): Json { $accountId = $this->request->user['user_id'] ?? 0; $page = $this->request->param('page/d', 1); $size = $this->request->param('size/d', 10); $type = $this->request->param('type/s', ''); $page = $page <= 0 ? 1 : $page; $size = $size <= 0 ? 10 : $size; $repo = AccountRepository::getInstance(); $whereMap = []; $fields = [ 'id', 'type', 'target', 'title', 'summary', 'content', 'status', 'sort', 'created_at', 'send_at' ]; $orders = ['sort' => 'desc', 'id' => 'desc']; $regTime = ''; $account = $repo->findById($accountId); if ($account) { $regTime = $account['created_at'] ?? ''; } if ($type == 'reminders') { $whereMap[] = ['type', '=', $type]; } $resp = $repo->messageList($whereMap, $fields, $page, $size, function ($q) use ($accountId, $regTime) { return $q->whereTime('send_at', '<=', date('Y-m-d H:i:s')) ->when(!empty($regTime), function ($q3) use ($regTime) { $q3->whereTime('send_at', '>=', $regTime); }) ->where(function ($q3) use ($accountId) { $q3->whereRaw('(target = "part" and find_in_set("'.$accountId.'", target_list)) or target <> "part"'); }); }, $orders); $msgIds = $resp['list']->column('id'); $hadReadMsgIds = $repo->getHadReadMessageIds($accountId, $msgIds); $msgTypeTextList = $repo->messageTypeTextList(); foreach ($resp['list'] as $item) { $item['type_text'] = $msgTypeTextList[$item['type']] ?? ''; $item['is_read'] = in_array($item['id'], $hadReadMsgIds) ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE; } // todo 获取列表后更新阅览日志记录 $repo->addReadLogs($accountId, $msgIds); return $this->json(0, 'success', $resp); } /** * 查看消息详情 * 添加阅览日志记录 */ public function messageInfo(): Json { $accountId = $this->request->user['user_id'] ?? 0; $msgId = $this->request->param('message_id/d', 0); $repo = AccountRepository::getInstance(); $fields = [ 'id', 'type', 'target', 'title', 'summary', 'content', 'target_list', 'status', 'sort', 'created_at', 'send_at' ]; $message = $repo->messageInfo($msgId, $fields); if (empty($message)) { return $this->json(4004, '没有相关的消息记录'); } if ($message['target'] == Message::TARGET_PART) { $targetList = empty($message['target_list']) ? [] : explode(',', $message['target_list']); if (!in_array($accountId, $targetList)) { return $this->json(4001, '没有查看权限'); } } elseif (!empty($message['send_at']) && strtotime($message['send_at']) > time()) { return $this->json(4001, '没有查看权限'); } $repo->addReadLogs($accountId, [$msgId]); $msgTypeTextList = $repo->messageTypeTextList(); $message['type_text'] = $msgTypeTextList[$message['type']] ?? ''; $message['is_read'] = AccountRepository::BOOL_TRUE; unset($message['target_list']); return $this->json(0, 'success', $message); } /** * 医生列表 */ public function doctorList(): Json { $page = $this->request->param('page/d', 1); $size = $this->request->param('size/d', 10); $keyword = $this->request->param('keyword/s', ''); $page = $page <= 0 ? 1 : $page; $size = $size <= 0 ? 10 : $size; $repo = AccountRepository::getInstance(); $whereMap = []; $fields = []; $searchName = ''; if (!empty($keyword)) { /** * 关键词查询范围:问题、病种、医生姓名 */ $doctorIds = $repo->searchRelationDoctorIds($keyword); if (count($doctorIds)) { $whereMap[] = ['id', 'in', $doctorIds]; } else { $searchName = $keyword; } } $resp = $repo->findDoctorListByWhere($whereMap, $fields, $page, $size, [], function ($q) use ($searchName) { if (empty($searchName)) { return $q; } else { // 二维数组JSON查询 return $q->whereRaw('(JSON_CONTAINS(`extra`, JSON_OBJECT("name", "'.$searchName.'")) or (`name` like "'.$searchName.'%") )'); } }); $list = $resp['list']->toArray(); $diseaseFields = ['disease_id', 'disease_name', 'disease_pid']; $accountFields = [ 'id', 'coding', 'real_name', 'nickname', 'headimgurl', 'gender', 'mobile', 'province', 'city', 'county', 'country', 'birthday', 'status', 'invite_code', ]; foreach ($list as &$item) { $diseases = []; foreach ($item['diseases'] as $disease) { $diseases[$disease['disease_id']] = arrayKeysFilter($disease, $diseaseFields); } $item['diseases'] = array_values($diseases); $account = arrayKeysFilter($item['account'], $accountFields); $account['headimgurl'] = File::convertCompleteFileUrl($account['headimgurl'] ?? ''); $item['account'] = $account; } unset($item); $resp['list'] = arrayNullToString($list); return $this->json(0, 'success', $resp); } /** * 医生详情 */ public function doctorInfo(): Json { $doctorId = $this->request->param('doctor_id/d', 0); $repo = AccountRepository::getInstance(); $whereMap = ['id' => $doctorId]; $doctor = $repo->findDoctorByWhere($whereMap); if (empty($doctor)) { return $this->json(4004, '没有相关的记录'); } $doctor = $doctor->toArray(); // 过滤负责的病种,避免重复 $diseases = []; $diseaseFields = ['disease_id', 'disease_name', 'disease_pid']; foreach ($doctor['diseases'] as $item) { $diseases[$item['disease_id']] = arrayKeysFilter($item, $diseaseFields); } $doctor['diseases'] = array_values($diseases); // 管理的前端用户信息过滤 $accountFields = [ 'id', 'coding', 'real_name', 'nickname', 'headimgurl', 'gender', 'mobile', 'province', 'city', 'county', 'country', 'birthday', 'status', 'invite_code', ]; $account = arrayKeysFilter($doctor['account'], $accountFields); $account['headimgurl'] = File::convertCompleteFileUrl($account['headimgurl'] ?? ''); $doctor['account'] = $account; // null空值转换 $doctor = arrayNullToString($doctor); return $this->json(0, 'success', $doctor); } /** * 客户足迹 * * @return Json */ public function footmarks(): Json { $page = input('page/d', 1); $size = input('size/d', 20); $customerId = input('customer_id/d', 0); //指定查询的客户 默认全部 $keyword = input('keyword/s', ''); //用户姓名或电话 $accountId = $this->request->user['user_id'] ?? 0; try { $data = AccountRepository::getInstance()->footmarks($accountId, $customerId, $keyword, $page, $size); return $this->json(0, 'success', $data); } catch (RepositoryException $e) { return $this->json(4001, $e->getMessage()); } catch (Exception $e) { ArchivesRepository::log($e->getMessage(), $e); return $this->json(5000, '操作失败'); } } /** * 前台权限列表 * * @return Json */ public function ruleList(): Json { try { $data = AccountRule::field('id,title,name,sort') ->order('sort', 'desc') ->order('id', 'asc') ->select(); return $this->json(0, 'success', $data); } catch (Exception $e) { ArchivesRepository::log($e->getMessage(), $e); return $this->json(5000, '操作失败'); } } /** * 获取已绑定线上客服的客户列表 */ public function getBindServiceList(): Json { $accountId = $this->request->user['user_id'] ?? 0; $keyword = input('keyword/s', ''); $page = input('page/d', 1); $size = input('size/d', 20); $type = input('type/s', '');//mine个人绑定的客户 其他获取全部 try { $where = []; $where[] = ['customer_service', '>', 0]; if ($type == 'mine') { $where[] = ['customer_service', '=', $accountId]; } if (!empty($keyword)) { $where[] = ['nickname|mobile', 'like', '%'.$keyword.'%']; } $list = Account::findList($where, ['id', 'nickname', 'mobile', 'customer_service'], $page, $size, function ($q) { return $q->with([ 'serviceList' => function ($query) { $query->field('id,account_id,name,phone'); } ])->order('id', 'desc'); }); $list['list'] = $list['list']->each(function ($item) { if (!$item->serviceList) { $item->serviceList = new Staff(); $item->serviceList->id = 0; $item->serviceList->account_id = 0; $item->serviceList->name = '所属员工已失效'; $item->serviceList->phone = ''; } if (isset($item->serviceList->phone)) { $item->serviceList->phone = !empty($item->serviceList->phone) ? stringDesensitization($item->serviceList->phone) : ''; } $item->service = $item->serviceList; unset($item->serviceList); if (isset($item->mobile)) { $item->mobile = !empty($item->mobile) ? stringDesensitization($item->mobile) : ''; } }); return $this->json(0, 'success', $list); } catch (Exception $e) { Log::error('获取客户绑定列表失败'.$e->getMessage()); return $this->json(5000, '获取客户绑定列表失败!'); } } /** * 随机绑定客服(线上客服) */ public function randBindService(): Json { // 目前关掉随机绑定客服功能 return $this->json(); if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; try { $repo = AccountRepository::getInstance(); if (!$account = $repo->findById($accountId)) { return $this->json(4001, '请先登录'); } $boundService = $repo->getBoundService($accountId, CustomerReceive::ACTION_ONLINE_SERVICE); if ($boundService) { return $this->json(0, '绑定成功'); } // 客服列表 $servicerList = Staff::getStaff(Staff::ROLE_CUSTOMER_ONLINE); if ($servicerList['total'] <= 0) { return $this->json(4002, '还没有客服!'); } $accountIds = $servicerList['list']->where('status', Staff::COMMON_ON)->column('account_id'); $servicerId = $accountIds[array_rand($accountIds)];//随机获取一个客服ID $boundData = [ 'customer_aid' => $accountId, 'worker_aid' => $servicerId, 'action' => CustomerReceive::ACTION_ONLINE_SERVICE, 'pid' => 0, 'path' => ',0,', 'created_at' => date('Y-m-d H:i:s') ]; CustomerReceive::create($boundData); $account->save(['customer_service' => $servicerId]); } catch (Exception $e) { Log::error('随机绑定客服失败'.$e->getMessage()); return $this->json(5000, '绑定失败!'); } return $this->json(); } /** * 用户绑定客服人员(线上客服) */ public function bindService(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } Log::info('线上客服绑定:'); Log::info(json_encode($this->request->param(), JSON_UNESCAPED_UNICODE)); $accountId = $this->request->user['user_id'] ?? 0; $serviceCoding = $this->request->param('service_coding/s', ''); if (empty($serviceCoding)) { return $this->json(4000, '无效请求'); } try { $repo = AccountRepository::getInstance(); $serviceAccount = $repo->findByUserCoding($serviceCoding); if (empty($serviceAccount)) { return $this->json(4001, '绑定客服失败!没有相关的客服记录'); } $boundService = $repo->getBoundService($accountId, CustomerReceive::ACTION_ONLINE_SERVICE); if ($boundService) { return $this->json(0, '绑定成功'); } $service = $repo->findServiceByWhere(['account_id' => $serviceAccount['id']]); if (empty($service)) { return $this->json(4002, '绑定客服失败!没有相关的客服记录'); } elseif ($service['status'] == staff::STATUS_DISABLE) { return $this->json(4003, '绑定客服失败!该客服账号已被冻结'); } $boundData = [ 'customer_aid' => $accountId, 'worker_aid' => $serviceAccount['id'], 'action' => CustomerReceive::ACTION_ONLINE_SERVICE, 'pid' => 0, 'path' => ',0,' ]; CustomerReceive::create($boundData); } catch (Exception $e) { return $this->json(5000, '绑定失败!'); } return $this->json(); } /** * 客服二维码 * @return Json */ public function serviceQr(): Json { $accountId = $this->request->user['user_id'] ?? 0; try { $account = AccountRepository::getInstance()->findById($accountId); if (empty($account)) { return $this->json(4000, '无效请求'); } $qrDataStr = $this->request->domain().'/bind-servicer?coding='.$account['coding']; $logoImg = app()->getRootPath().'public/static/images/icon-logo.jpg'; $result = Builder::create() ->writer(new PngWriter()) ->writerOptions([]) ->data($qrDataStr) ->encoding(new Encoding('UTF-8')) ->errorCorrectionLevel(new ErrorCorrectionLevelHigh()) ->size(300) ->margin(10) ->roundBlockSizeMode(new RoundBlockSizeModeMargin()) ->logoPath($logoImg) ->logoResizeToHeight(100) ->logoResizeToWidth(100) ->logoPunchoutBackground(true) ->build(); $dataUri = $result->getDataUri(); return $this->json(0, 'success', ['qr' => $dataUri]); } catch (Exception $e) { AccountRepository::log('客服二维码生成失败', $e); return $this->json(5000, '客服二维码生成失败'); } } /** * 客户列表 * * @return Json * @throws Exception */ public function customer(): Json { $accountId = $this->request->user['user_id'] ?? 0; $type = input('type/s', 'all'); $page = input('page/d', 1); $size = input('size/d', 20); try { if ($accountId == 0) { return $this->json(6001, '请先登录'); } $where = []; if ($type !== 'all') { $where[] = ['channel', '=', $type]; } $field = ['id', 'nickname', 'real_name', 'headimgurl', 'mobile', 'channel', 'created_at', 'customer_service']; $data = AccountRepository::getInstance()->customerList($where, $field, $accountId, $page, $size); $data['list'] = $data['list']->each(function ($item) { $item->real_name = $item->real_name ?? ''; unset($item->tags); }); return $this->json(0, 'success', $data); } catch (RepositoryException $e) { return $this->json(4001, $e->getMessage()); } catch (Exception $e) { ArchivesRepository::log($e->getMessage(), $e); return $this->json(5000, '操作失败'); } } /** * 来源渠道列表 * * @return Json * @throws Exception */ public function channel(): Json { return $this->json(0, 'success', Account::channelTextList()); } /** * 客户分配(分配给在线客服) * * @return Json * @throws Exception */ public function customerAllot(): Json { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; $params = input('post.'); $rules = [ 'customer_id|客户' => 'require|number', 'staff_id|员工' => 'require|number', ]; $validate = $this->validateByApi($params, $rules); if ($validate !== true) { return $validate; } try { if (!$staff = Staff::findOne(['account_id' => $accountId])) { return $this->json(4001, '你不是员工'); } $staffRules = explode(',', $staff['rules']); if (!in_array('customer-allot', $staffRules)) { return $this->json(4002, '无此权限'); } CustomerReceive::allotService($params['customer_id'], $params['staff_id']); } catch (RepositoryException $e) { return $this->json(4001, $e->getMessage()); } catch (Exception $e) { AccountRepository::log('分配客户失败', $e); return $this->json(5001, '操作失败'); } return $this->json(); } /** * 客服列表 * * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function servicerList(): Json { $roleName = 'customer-online';//在线客服的角色 $roleId = AccountRole::where('name', $roleName)->value('id'); $serviceList = Staff::whereRaw('FIND_IN_SET('.$roleId.', account_roles)') ->field('name,account_id as id') ->order('id', 'asc') ->select(); return $this->json(0, 'success', $serviceList); } /** * 绑定手机 * * @return bool|Json * @throws Exception */ public function bindPhone() { if (!$this->request->isPost()) { return $this->json(4000, '无效请求'); } $accountId = $this->request->user['user_id'] ?? 0; $params = input('post.'); $rules = [ 'encryptedData|加密数据' => 'require', 'iv|IV' => 'require', ]; $validate = $this->validateByApi($params, $rules); if ($validate !== true) { return $validate; } try { if (!$account = Account::findById($accountId)) { return $this->json(4000, '用户不存在'); } // 解密手机相关数据 若存在手机则覆盖 $minApp = WechatApplets::getInstance(); $sessionKey = $this->request->user['session_key'] ?? ''; $decryptData = $minApp->encryptor->decryptData($sessionKey, $params['iv'], $params['encryptedData']); $phone = $decryptData['phoneNumber'] ?? ''; // 通过iv和加密数据 解密出手机号 if ($phone) { $account->save(['mobile' => $phone, 'phone_active' => Account::COMMON_ON]); } return $this->json(0, 'success', ['phone' => $phone]); } catch (Exception $e) { AccountRepository::log('手机绑定失败', $e); return $this->json(5001, '手机绑定失败'); } } /** * 领取优惠券 * * @return Json * @throws Exception */ public function getCoupon(): Json { $accountId = $this->request->user['user_id'] ?? 0; $couponID = input('coupon_id/d', 0); try { AccountRepository::getInstance()->getCoupon($accountId, $couponID); return $this->json(); } catch (ApiRedirectException $e) { return $this->json(302, $e->getMessage()); } catch (RepositoryException $e) { return $this->json(4000, $e->getMessage()); } catch (Exception $e) { AccountRepository::log('领取优惠券失败', $e); return $this->json(5001, '领取优惠券失败'); } } /** * 展示体验券 生成token * * @return Json * @throws Exception */ public function exhibitionExperienceCoupon(): Json { $accountId = $this->request->user['user_id'] ?? 0; $couponID = input('coupon_id/d', 0); try { $coupon = AccountRepository::getInstance()->userCouponInfo($accountId, $couponID); $time = time(); //不存在 if (empty($coupon) || !isset($coupon['coupon'])) { return $this->json(4002, "优惠券不存在"); } //状态异常 if ($coupon['status'] != AccountCoupon::STATUS_NORMAL) { return $this->json(4002, "优惠券状态已经不能使用"); } //未开始 if (strtotime($coupon['coupon']["begin_at"]) > $time) { return $this->json(4002, "优惠券还未到开始使用时间"); } //过期 if (strtotime($coupon['coupon']["end_at"]) < $time) { return $this->json(4002, "优惠券已过期"); } if ($coupon['is_taste'] != AccountCoupon::COMMON_ON) { return $this->json(4002, "优惠券不是体验券"); } $secret = AccountRepository::getInstance()->createExperienceCouponQR($accountId, $couponID); $url = ($this->request->domain()."/api/staff/coupon/write-off-experience-coupon?account_id=$accountId&&coupon_id=$couponID&&secret=$secret"); return $this->json(0, "success", ["url" => $url, "secret" => $secret]); } catch (ApiRedirectException $e) { return $this->json(302, $e->getMessage()); } catch (RepositoryException $e) { return $this->json(4000, $e->getMessage()); } catch (Exception $e) { AccountRepository::log('获取优惠券失败', $e); return $this->json(5001, '获取优惠券失败'); } } /** * 获取优惠券列表 * * @return Json * @throws Exception */ public function getCouponList(): Json { $accountId = $this->request->user['user_id'] ?? 0; $status = input('status/s', AccountCoupon::STATUS_NORMAL); $page = input('page/d', 1); $size = input('size/d', 0); try { $data = AccountRepository::getInstance()->couponList($status, $accountId, $page, $size); return $this->json(0, 'success', $data); } catch (Exception $e) { AccountRepository::log('获取优惠券列表失败', $e); return $this->json(5001, '获取优惠券列表失败'); } } /** * 获取地址列表 * * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function address(): Json { $userId = $this->request->user['user_id'] ?? 0; $list = AccountRepository::getInstance()->findAddressByUid($userId); return $this->json(0, 'success', $list); } /** * 保存地址 * * @return Json */ public function addressSave(): Json { $userId = $this->request->user['user_id'] ?? 0; $params = $this->request->param(); $validate = new UserValidate(); $params['user_id'] = $userId; if (!$validate->scene('address')->check($params)) { return $this->json(4000, $validate->getError()); } AccountRepository::getInstance()->saveAddress($params); return $this->json(); } /** * 地址详情 * * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function addressInfo(): Json { if (!$id = input('id/d', 0)) { return $this->json(4000, '参数错误'); } $info = AccountRepository::getInstance()->findAddressById($id); return $this->json(0, 'success', $info); } /** * 地址删除 * * @return Json */ public function addressDel(): Json { if (!$id = input('id/d', 0)) { return $this->json(4000, '参数错误'); } AccountRepository::getInstance()->delAddress($id); return $this->json(); } /** * 孔雀币管理页面 * * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function coinLoad() { $accountId = $this->request->user['user_id']; $account = Account::findById($accountId); if (empty($account)) { return $this->json(6001, '未登录'); } Config::load('extra/coin_withdrawal', 'coin_withdrawal'); $coinWithdrawal = config('coin_withdrawal'); //审核中的提现金额 $withdrawalIng = AccountRepository::getInstance()->withdrawalIng($accountId); //已经提现金额 $withdrawald = AccountRepository::getInstance()->withdrawald($accountId); $key = $account['is_staff'] ? "withdrawal_proportion_staff" : "withdrawal_proportion_account"; return $this->json(0, "ok", [ "coin" => $account["coin"], "withdrawal_ing" => $withdrawalIng, "withdrawald" => $withdrawald, "withdrawal_proportion" => $coinWithdrawal[$key] ?? ["coin" => 0, "money" => 0], ]); } /** * 提现和获取孔雀币记录列表 * * @param $type string coin / withdrawal * @return Json */ public function withdrawalAndGetCoinList() { $page = input('page/d', 1); $limit = input('size/d', 10); $type = input("type/s", "coin"); $accountId = $this->request->user['user_id']; //如果是获取列表 if ($type == AccountDataLog::TYPE_COIN) { $items = AccountDataLog::findList([ ["account_id", "=", $accountId,], ["type", "=", AccountDataLog::TYPE_COIN], ["action", "not in", [AccountDataLog::ACTION_WITHDRAWAL, AccountDataLog::ACTION_WITHDRAWAL_RETURN]], ], [], $page, $limit, null, ["id" => "desc"]); } else { $items = AccountDataLog::findList([ ["account_id", "=", $accountId,], ["type", "=", AccountDataLog::TYPE_COIN], ["action", "in", [AccountDataLog::ACTION_WITHDRAWAL, AccountDataLog::ACTION_WITHDRAWAL_RETURN]], ], [], $page, $limit, null, ["id" => "desc"]); } return $this->json(0, '操作成功', $items); } /** * 孔雀币收支记录 * * @return Json * @throws Exception */ public function coinLog(): Json { $page = input('page/d', 1); $limit = input('size/d', 10); $type = input("type/s", "in"); //in=收入 out=支出 默认in $accountId = $this->request->user['user_id'] ?? 0; $where = []; $where[] = ["account_id", "=", $accountId]; $where[] = ["type", "=", AccountDataLog::TYPE_COIN]; if ($type == 'in') { $where[] = ["num", ">", 0]; } if ($type == 'out') { $where[] = ["num", "<", 0]; } $items = AccountDataLog::findList($where, [], $page, $limit, null, ['id' => 'desc']); return $this->json(0, '操作成功', $items); } /** * 积分收支记录 * * @return Json * @throws Exception */ public function scoreLog(): Json { $page = input('page/d', 1); $limit = input('size/d', 10); $type = input("type/s", "in"); //in=收入 out=支出 默认in $accountId = $this->request->user['user_id'] ?? 0; $where = []; $where[] = ["account_id", "=", $accountId]; $where[] = ["type", "=", AccountDataLog::TYPE_SCORE]; if ($type == 'in') { $where[] = ["num", ">", 0]; } if ($type == 'out') { $where[] = ["num", "<", 0]; } $items = AccountDataLog::findList($where, [], $page, $limit, null, ['id' => 'desc']); return $this->json(0, '操作成功', $items); } /** * 孔雀币管理页面 * * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function scoreLoad(): Json { $accountId = $this->request->user['user_id']; $account = Account::findById($accountId); if (empty($account)) { return $this->json(6001, '未登录'); } return $this->json(0, "ok", [ "score" => $account["score"], "share" => AccountRepository::getInstance()->shareAccountCount($accountId), ]); } /** * 发起提现孔雀币 * * @param $type string coin / withdrawal * @return Json */ public function withdrawalCoin() { $coin = input("coin/d", 0); $accountId = $this->request->user['user_id']; $account = Account::findOne(["id" => $accountId], [], function ($q) { return $q->lock(true); }); if (empty($account)) { return $this->json(6001, '未登录'); } if ($coin <= 0) { return $this->json(4000, '提现孔雀币不能小于0'); } //换算出 1元 = 多少孔雀币 try { //本次提现共计金额 $totalMoney = AccountRepository::getInstance()->transformationCoinOrMoney($coin, "money", $account['is_staff']); $totalMoney = $totalMoney / 100;//分转元 if ($account['coin'] < $coin) { return $this->json(4004, '孔雀币不足'); } $key = ($account['is_staff'] ? AccountDataLog::withdrawal_proportion_staff : AccountDataLog::withdrawal_proportion_account); $coinWithdrawal = config('coin_withdrawal'); $withdrawalProportion = $coinWithdrawal[$key]; } catch (Exception $e) { return $this->json(5004, '提现失败'); } catch (RepositoryException $e) { return $this->json(5005, $e->getMessage()); } //条件满足 开始写入提现 Db::startTrans(); try { //写入--积分孔雀币日志 AccountDataLog::log($account['id'], "提现-扣除孔雀币", $coin * -1, AccountDataLog::TYPE_COIN, AccountDataLog::ACTION_WITHDRAWAL, $account[AccountDataLog::TYPE_COIN] - $coin ); //写入提现记录 $accountCoinWithdrawalData = [ "account_id" => $account['id'], "coin_number" => $coin, "role" => ($account["is_staff"] == 0) ? AccountCoinWithdrawal::$role_account : AccountCoinWithdrawal::$role_staff, "money" => $totalMoney, "proportion" => $withdrawalProportion["coin"].":".$withdrawalProportion["money"], "status" => AccountCoinWithdrawal::$status_default, "created_at" => date("Y-m-d H:i:s"), ]; AccountCoinWithdrawal::create($accountCoinWithdrawalData); //扣除孔雀币 $account->save(["coin" => ($account["coin"] - $coin)]); Db::commit(); return $this->json(); } catch (Exception $e) { Db::rollback(); return $this->json(5000, "提现失败-2"); } catch (RepositoryException $e) { Db::rollback(); return $this->json(5000, "提现失败-3"); } } /** * 获取孔雀币抵扣金额 * * @return Json * @throws Exception */ public function getCoinPrice(): Json { $accountId = $this->request->user['user_id'] ?? 0; $amount = input('amount/f', 0); $type = input('get_type/s', 'money');//获取类型 money=根据孔雀币获取金额 coin=根据金额获取孔雀币 try { if (!$account = AccountRepository::getInstance()->findById($accountId)) { return $this->json(4001, '用户不存在'); } if ($type == 'money') { $amount = AccountRepository::getInstance() ->transformationCoinOrMoney($amount, 'money', $account['is_staff'] == 1); } else { $amount = AccountRepository::getInstance() ->transformationCoinOrMoney($amount, 'coin', $account['is_staff'] == 1); } return $this->json(0, 'success', ['amount' => $amount]); } catch (RepositoryException $e) { return $this->json(4000, $e->getMessage()); } catch (Exception $e) { AccountRepository::log('获取孔雀币抵扣金额失败', $e); return $this->json(5001, '获取孔雀币抵扣金额失败'); } } /** * 我的订单 * * @return Json * @throws RepositoryException */ public function order(): Json { $userId = $this->request->user['user_id'] ?? 0; $page = $this->request->param('page/d', 1); $size = $this->request->param('size/d', 20); $tag = $this->request->param('tag/s', 'all'); $res = OrderRepository::getInstance()->mine($userId, $tag, $page, $size); return $this->json(0, 'success', $res); } /** * 立即免拼 需要单人开团允许 * * @return Json */ public function openOne(): Json { $userId = $this->request->user['user_id'] ?? 0; $coding = input('order_coding/s'); try { if (empty($coding)) { return $this->json(4001, '参数错误'); } OrderRepository::getInstance()->openOne($userId, $coding); return $this->json(); } catch (RepositoryException $e) { return $this->json(4000, $e->getMessage()); } catch (Exception $e) { AccountRepository::log('立即免拼失败', $e); return $this->json(5001, '立即免拼失败'); } } /** * 订单详情 * * @return Json * @throws RepositoryException */ public function orderDetail(): Json { $id = input('id/d', 0); $userId = $this->request->user['user_id'] ?? 0; $res = OrderRepository::getInstance()->detail($id); if (!$res) { return $this->json(4000, '订单不存在'); } if ($res['account_id'] !== $userId) { return $this->json(4000, '不是您的订单'); } return $this->json(0, 'success', $res); } /** * 我的海报列表 * * @return Json */ public function poster(): Json { $page = input('page/d', 1); $size = input('size/d', 10); $userId = $this->request->user['user_id'] ?? 0; try { $list = AccountRepository::getInstance()->poster($userId, $page, $size); return $this->json(0, 'success', $list); } catch (RepositoryException $e) { return $this->json(4000, $e->getMessage()); } catch (Exception $e) { AccountRepository::log('获取个人海报列表失败', $e); return $this->json(5001, '海报列表获取失败'); } } /** * 生成用户指定海报 */ public function posterInfo(): Json { $accountId = $this->request->user['user_id'] ?? 0; $src = $this->request->param('poster_src/s', ''); $domain = $this->request->domain(); try { $posterData = AccountRepository::getInstance()->posterInfo($accountId, $src, $domain); return $this->json(0, 'success', ['poster' => $posterData]); } catch (RepositoryException $e) { return $this->json(4000, $e->getMessage()); } catch (Exception $e) { AccountRepository::log('个人海报生成失败', $e); return $this->json(5000, '个人海报生成失败'); } } }