coupon-admin/app/controller/api/User.php

2164 lines
74 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace app\controller\api;
use app\exception\ApiRedirectException;
use app\exception\RepositoryException;
use app\exception\TraitException;
use app\model\Account;
use app\model\AccountCoinWithdrawal;
use app\model\AccountCoupon;
use app\model\AccountDataLog;
use app\model\AccountRecord;
use app\model\AccountRole;
use app\model\AccountRule;
use app\model\Activity;
use app\model\Appointment;
use app\model\Coupon;
use app\model\CustomerReceive;
use app\model\Message;
use app\model\ScanLog;
use app\model\Staff;
use app\repository\AccountRepository;
use app\repository\ArchivesRepository;
use app\repository\OrderRepository;
use app\service\File;
use app\service\GdTool;
use app\service\Jwt;
use app\service\wx\WechatApplets;
use app\validate\User as UserValidate;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin;
use Endroid\QrCode\Writer\PngWriter;
use Exception;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Config;
use think\facade\Db;
use think\facade\Log;
use think\response\Json;
class User extends Base
{
protected $noNeedLogin = [
'login',
'tempLogin',
'appointmentParameters',
'appointmentPeriodFull',
'feedbackTypes',
'doctorList',
'doctorInfo',
'shareUsers',
'homeCoupon',
'getCoupon',
'signByQr',
];
public function index(): Json
{
$userId = $this->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, '个人海报生成失败');
}
}
}