LoginServices.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\cashier;
  13. use app\Request;
  14. use app\services\BaseServices;
  15. use app\dao\store\SystemStoreStaffDao;
  16. use app\services\system\SystemMenusServices;
  17. use app\services\system\SystemRoleServices;
  18. use app\services\store\SystemStoreServices;
  19. use crmeb\exceptions\AdminException;
  20. use crmeb\exceptions\AuthException;
  21. use crmeb\services\CacheService;
  22. use crmeb\traits\ServicesTrait;
  23. use crmeb\utils\ApiErrorCode;
  24. use crmeb\utils\JwtAuth;
  25. use Firebase\JWT\ExpiredException;
  26. use think\exception\ValidateException;
  27. use think\facade\Cache;
  28. /**
  29. *
  30. * Class LoginServices
  31. * @package app\services\user
  32. * @mixin SystemStoreStaffDao
  33. */
  34. class LoginServices extends BaseServices
  35. {
  36. use ServicesTrait;
  37. /**
  38. * 当前门店权限缓存前缀
  39. */
  40. const STORE_CASHIER_RULES_LEVEL = 'store_cashier_rules_level_';
  41. /**
  42. * LoginServices constructor.
  43. * @param SystemStoreStaffDao $dao
  44. */
  45. public function __construct(SystemStoreStaffDao $dao)
  46. {
  47. $this->dao = $dao;
  48. }
  49. /**
  50. * 获取登陆前的login等信息
  51. * @return array
  52. */
  53. public function getLoginInfo()
  54. {
  55. return [
  56. 'slide' => sys_data('admin_login_slide') ?? [],
  57. 'logo_square' => sys_config('site_logo_square'),//透明
  58. 'logo_rectangle' => sys_config('site_logo'),//方形
  59. 'login_logo' => sys_config('login_logo'),//登陆
  60. 'site_name' => sys_config('site_name'),
  61. 'site_url' => sys_config('site_url'),
  62. ];
  63. }
  64. /**
  65. * H5账号登陆
  66. * @param Request $request
  67. * @return mixed
  68. * @throws \think\db\exception\DataNotFoundException
  69. * @throws \think\db\exception\ModelNotFoundException
  70. * @throws \think\exception\DbException
  71. */
  72. public function login($account, $password, $type)
  73. {
  74. $storeStaffInfo = $this->dao->getOne(['account|phone' => $account, 'is_del' => 0]);
  75. $key = 'cashier_login_captcha_' . $account;
  76. if (!$storeStaffInfo) {
  77. Cache::inc($key);
  78. throw new AdminException('账号不存在!');
  79. }
  80. if ($password) {//平台还可以登录
  81. if (!$storeStaffInfo->status) {
  82. Cache::inc($key);
  83. throw new AdminException('您已被禁止登录!');
  84. }
  85. if (!password_verify($password, $storeStaffInfo->pwd)) {
  86. Cache::inc($key);
  87. throw new AdminException('账号或密码错误,请重新输入');
  88. }
  89. }
  90. return $this->getLoginResult((int)$storeStaffInfo['id'], $type, $storeStaffInfo);
  91. }
  92. /**
  93. * 企业微信扫码登录
  94. * @param array $workUserInfo
  95. * @return array
  96. * @throws \think\db\exception\DataNotFoundException
  97. * @throws \think\db\exception\DbException
  98. * @throws \think\db\exception\ModelNotFoundException
  99. */
  100. public function workScanLogin(array $workUserInfo)
  101. {
  102. if (0 !== $workUserInfo['errcode']) {
  103. throw new ValidateException($workUserInfo['errmsg']);
  104. }
  105. if (empty($workUserInfo['mobile'])) {
  106. throw new ValidateException('改成员请先关联手机号在进行登录');
  107. }
  108. $storeStaffInfo = $this->dao->getOne(['phone' => $workUserInfo['mobile'], 'is_del' => 0]);
  109. if (!$storeStaffInfo) {
  110. throw new AdminException('账号不存在!');
  111. }
  112. return $this->getLoginResult((int)$storeStaffInfo['id'], 'cashier', $storeStaffInfo);
  113. }
  114. /**
  115. * 获取登录店员信息
  116. * @param int $id
  117. * @param string $type
  118. * @param array $storeStaffInfo
  119. * @return array
  120. * @throws \think\db\exception\DataNotFoundException
  121. * @throws \think\db\exception\DbException
  122. * @throws \think\db\exception\ModelNotFoundException
  123. */
  124. public function getLoginResult(int $id, string $type, $storeStaffInfo = [])
  125. {
  126. if (!$storeStaffInfo) {
  127. $storeStaffInfo = $this->dao->get(['id' => $id, 'is_del' => 0]);
  128. }
  129. if (!$storeStaffInfo->is_cashier) {
  130. throw new AdminException('您没有收银员的身份无法登录');
  131. }
  132. if (!$storeStaffInfo) {
  133. throw new AdminException('账号不存在!');
  134. }
  135. $storeStaffInfo->last_time = time();
  136. $storeStaffInfo->last_ip = app('request')->ip();
  137. $storeStaffInfo->login_count++;
  138. $storeStaffInfo->save();
  139. $tokenInfo = $this->createToken($storeStaffInfo->id, $type, $storeStaffInfo->pwd);
  140. /** @var SystemMenusServices $services */
  141. $services = app()->make(SystemMenusServices::class);
  142. [$menus, $uniqueAuth] = $services->getMenusList($storeStaffInfo->roles, (int)($storeStaffInfo['level'] ?? 0), 3);
  143. /** @var SystemStoreServices $storeServices */
  144. $storeServices = app()->make(SystemStoreServices::class);
  145. $store = $storeServices->get((int)$storeStaffInfo['store_id'], ['id', 'image']);
  146. return [
  147. 'token' => $tokenInfo['token'],
  148. 'expires_time' => $tokenInfo['params']['exp'],
  149. 'menus' => $menus,
  150. 'unique_auth' => $uniqueAuth,
  151. 'user_info' => [
  152. 'id' => $storeStaffInfo->getData('id'),
  153. 'account' => $storeStaffInfo->getData('account'),
  154. 'avatar' => $storeStaffInfo->getData('avatar'),
  155. ],
  156. 'logo' => $store && isset($store['image']) && $store['image'] ? $store['image'] : sys_config('site_logo'),
  157. 'logo_square' => $store && isset($store['image']) && $store['image'] ? $store['image'] : sys_config('site_logo_square'),
  158. 'version' => get_crmeb_version(),
  159. 'newOrderAudioLink' => get_file_link(sys_config('new_order_audio_link', '')),
  160. 'prefix' => config('admin.cashier_prefix')
  161. ];
  162. }
  163. /**
  164. * 重置密码
  165. * @param $account
  166. * @param $password
  167. */
  168. public function reset($account, $password)
  169. {
  170. $user = $this->dao->getOne(['account|phone' => $account]);
  171. if (!$user) {
  172. throw new ValidateException('用户不存在');
  173. }
  174. if (!$this->dao->update($user['uid'], ['pwd' => md5((string)$password)], 'uid')) {
  175. throw new ValidateException('修改密码失败');
  176. }
  177. return true;
  178. }
  179. /**
  180. * 获取Admin授权信息
  181. * @param string $token
  182. * @return array
  183. * @throws \Psr\SimpleCache\InvalidArgumentException
  184. */
  185. public function parseToken(string $token): array
  186. {
  187. /** @var CacheService $cacheService */
  188. $cacheService = app()->make(CacheService::class);
  189. if (!$token || $token === 'undefined') {
  190. throw new AuthException(ApiErrorCode::ERR_LOGIN);
  191. }
  192. //检测token是否过期
  193. $md5Token = md5($token);
  194. if (!$cacheService->hasToken($md5Token) || !($cacheToken = $cacheService->getTokenBucket($md5Token))) {
  195. throw new AuthException(ApiErrorCode::ERR_LOGIN);
  196. }
  197. //是否超出有效次数
  198. if (isset($cacheToken['invalidNum']) && $cacheToken['invalidNum'] >= 3) {
  199. if (!request()->isCli()) {
  200. $cacheService->clearToken($md5Token);
  201. }
  202. throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
  203. }
  204. /** @var JwtAuth $jwtAuth */
  205. $jwtAuth = app()->make(JwtAuth::class);
  206. //设置解析token
  207. [$id, $type, $auth] = $jwtAuth->parseToken($token);
  208. //验证token
  209. try {
  210. $jwtAuth->verifyToken();
  211. $cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
  212. } catch (ExpiredException $e) {
  213. $cacheToken['invalidNum'] = isset($cacheToken['invalidNum']) ? $cacheToken['invalidNum']++ : 1;
  214. $cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
  215. } catch (\Throwable $e) {
  216. if (!request()->isCli()) {
  217. $cacheService->clearToken($md5Token);
  218. }
  219. throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
  220. }
  221. //获取管理员信息
  222. $storeStaffInfo = $this->dao->get($id);
  223. if (!$storeStaffInfo || !$storeStaffInfo->id || $storeStaffInfo->is_del) {
  224. if (!request()->isCli()) {
  225. $cacheService->clearToken($md5Token);
  226. }
  227. throw new AuthException(ApiErrorCode::ERR_LOGIN_STATUS);
  228. }
  229. if ($auth !== md5($storeStaffInfo['pwd'])) {
  230. throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
  231. }
  232. $storeStaffInfo->type = $type;
  233. return $storeStaffInfo->hidden(['pwd', 'is_del', 'status'])->toArray();
  234. }
  235. /**
  236. * 后台验证权限
  237. * @param Request $request
  238. */
  239. public function verifiAuth(Request $request)
  240. {
  241. $rule = str_replace('cashierapi/', '', trim(strtolower($request->rule()->getRule())));
  242. if (in_array($rule, ['cashier/logout', 'menuslist'])) {
  243. return true;
  244. }
  245. $method = trim(strtolower($request->method()));
  246. /** @var SystemRoleServices $roleServices */
  247. $roleServices = app()->make(SystemRoleServices::class);
  248. $auth = $roleServices->getAllRoles(2, 3, self::STORE_CASHIER_RULES_LEVEL);
  249. //验证访问接口是否存在
  250. if ($auth && !in_array($method . '@@' . $rule, array_map(function ($item) {
  251. return trim(strtolower($item['methods'])). '@@'. trim(strtolower(str_replace(' ', '', $item['api_url'])));
  252. }, $auth))) {
  253. return true;
  254. }
  255. $auth = $roleServices->getRolesByAuth($request->cashierInfo()['roles'], 2, 3, self::STORE_CASHIER_RULES_LEVEL);
  256. //验证访问接口是否有权限
  257. if ($auth && empty(array_filter($auth, function ($item) use ($rule, $method) {
  258. if (trim(strtolower($item['api_url'])) === $rule && $method === trim(strtolower($item['methods'])))
  259. return true;
  260. }))) {
  261. throw new AuthException(ApiErrorCode::ERR_AUTH);
  262. }
  263. }
  264. }