diff --git a/Server/application/common/controller/PasswordLoginController.php b/Server/application/common/controller/PasswordLoginController.php index a3748a06..fd6da7ec 100644 --- a/Server/application/common/controller/PasswordLoginController.php +++ b/Server/application/common/controller/PasswordLoginController.php @@ -102,15 +102,27 @@ class PasswordLoginController extends BaseController * @param string $account 账号(手机号) * @param string $password 密码(可能是加密后的) * @param string $typeId 登录IP + * @param string $deviceId 本地设备imei * @return array * @throws \Exception */ - protected function doLogin(string $account, string $password, int $typeId): array + protected function doLogin(string $account, string $password, int $typeId, string $deviceId): array { // 获取用户信息 $member = $this->getUser($account, $password, $typeId); $deviceTotal = Db::name('device')->where(['companyId' => $member['companyId'],'deleteTime' => 0])->count(); + //更新设备imei + if ($typeId == 2 && !empty($deviceId)){ + $deviceUser = Db::name('device_user')->where(['companyId' => $member['companyId'],'userId' => $member['id'],'deleteTime' => 0])->find(); + if (!empty($deviceUser)){ + $s2_device = Db::table('s2_device')->where(['companyId' => $member['companyId'],'deleteTime' => 0,'id' => $deviceUser])->find(); + if (!empty($s2_device) && empty($s2_device['deviceImei'])){ + Db::table('s2_device')->where(['id' => $s2_device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); + Db::name('device')->where(['id' => $s2_device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); + } + } + } // 生成JWT令牌 @@ -126,34 +138,16 @@ class PasswordLoginController extends BaseController */ public function index() { - $params = $this->request->only(['account', 'password', 'typeId']); + $params = $this->request->only(['account', 'password', 'typeId','deviceId']); try { $userData = $this->dataValidate($params)->doLogin( $params['account'], $params['password'], - $params['typeId'] + $params['typeId'], + $params['deviceId'] ); - //同时登录客服系统 - /* if (!empty($userData['member']['passwordLocal'])){ - $params = [ - 'grant_type' => 'password', - 'username' => $userData['member']['account'], - 'password' => localDecrypt($userData['member']['passwordLocal']) - ]; - // 调用登录接口获取token - $headerData = ['client:kefu-client']; - $header = setHeader($headerData, '', 'plain'); - $result = requestCurl('https://s2.siyuguanli.com:9991/token', $params, 'POST', $header); - $token = handleApiResponse($result); - $userData['kefuData']['token'] = $token; - if (isset($token['access_token']) && !empty($token['access_token'])) { - $headerData = ['client:kefu-client']; - $header = setHeader($headerData, $token['access_token']); - $result = requestCurl( 'https://s2.siyuguanli.com:9991/api/account/self', [], 'GET', $header,'json'); - $self = handleApiResponse($result); - $userData['kefuData']['self'] = $self; - } - }*/ + + return ResponseHelper::success($userData, '登录成功'); } catch (Exception $e) { return ResponseHelper::error($e->getMessage(), $e->getCode()); diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index d7f4012e..b2c4217b 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -178,6 +178,15 @@ Route::group('v1/', function () { Route::get('detailType', 'app\cunkebao\controller\AiKnowledgeBaseController@detailType'); }); + // 门店端账号管理 + Route::group('store-accounts', function () { + Route::get('', 'app\cunkebao\controller\StoreAccountController@index'); // 获取账号列表 + Route::post('', 'app\cunkebao\controller\StoreAccountController@create'); // 创建账号 + Route::put('', 'app\cunkebao\controller\StoreAccountController@update'); // 编辑账号 + Route::delete('', 'app\cunkebao\controller\StoreAccountController@delete'); // 删除账号 + Route::post('disable', 'app\cunkebao\controller\StoreAccountController@disable'); // 禁用/启用账号 + }); + })->middleware(['jwt']); diff --git a/Server/application/cunkebao/controller/StoreAccountController.php b/Server/application/cunkebao/controller/StoreAccountController.php new file mode 100644 index 00000000..e58d6865 --- /dev/null +++ b/Server/application/cunkebao/controller/StoreAccountController.php @@ -0,0 +1,404 @@ +request->param('account', ''); + $username = $this->request->param('username', ''); + $phone = $this->request->param('phone', ''); + $password = $this->request->param('password', ''); + $deviceId = $this->request->param('deviceId', 0); + + $companyId = $this->getUserInfo('companyId'); + + // 参数验证 + if (empty($account)) { + return ResponseHelper::error('账号不能为空'); + } + if (empty($username)) { + return ResponseHelper::error('昵称不能为空'); + } + if (empty($phone)) { + return ResponseHelper::error('手机号不能为空'); + } + if (!preg_match('/^1[3-9]\d{9}$/', $phone)) { + return ResponseHelper::error('手机号格式不正确'); + } + if (empty($password)) { + return ResponseHelper::error('密码不能为空'); + } + if (strlen($password) < 6 || strlen($password) > 20) { + return ResponseHelper::error('密码长度必须在6-20个字符之间'); + } + if (empty($deviceId)) { + return ResponseHelper::error('请选择设备'); + } + + // 检查账号是否已存在(同一 typeId 和 companyId 下不能重复) + $existUser = Db::name('users')->where(['account' => $account, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->find(); + if ($existUser) { + return ResponseHelper::error('账号已存在'); + } + + // 检查手机号是否已存在(同一 typeId 和 companyId 下不能重复) + $existPhone = Db::name('users')->where(['phone' => $phone, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->find(); + if ($existPhone) { + return ResponseHelper::error('手机号已被使用'); + } + + // 检查设备是否存在且属于当前公司 + $device = Device::where('id', $deviceId) + ->where('companyId', $companyId) + ->find(); + if (!$device) { + return ResponseHelper::error('设备不存在或没有权限'); + } + + // 开始事务 + Db::startTrans(); + try { + // 创建用户 + $userData = [ + 'account' => $account, + 'username' => $username, + 'phone' => $phone, + 'passwordMd5' => md5($password), + 'passwordLocal' => localEncrypt($password), + 'avatar' => 'https://img.icons8.com/color/512/circled-user-male-skin-type-7.png', + 'isAdmin' => 0, + 'companyId' => $companyId, + 'typeId' => 2, // 门店端固定为2 + 'status' => 1, // 默认可用 + 'balance' => 0, + 'tokens' => 0, + 'createTime' => time(), + ]; + + $userId = Db::name('users')->insertGetId($userData); + + // 绑定设备 + Db::name('device_user')->insert([ + 'companyId' => $companyId, + 'userId' => $userId, + 'deviceId' => $deviceId, + 'deleteTime' => 0, + ]); + + // 提交事务 + Db::commit(); + + return ResponseHelper::success('创建账号成功'); + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 编辑账号 + * @return \think\response\Json + */ + public function update() + { + try { + $userId = $this->request->param('userId', 0); + $account = $this->request->param('account', ''); + $username = $this->request->param('username', ''); + $phone = $this->request->param('phone', ''); + $password = $this->request->param('password', ''); + $deviceId = $this->request->param('deviceId', 0); + + $companyId = $this->getUserInfo('companyId'); + + // 参数验证 + if (empty($userId)) { + return ResponseHelper::error('用户ID不能为空'); + } + + // 检查用户是否存在且属于当前公司 + $user = Db::name('users')->where(['id' => $userId, 'companyId' => $companyId, 'typeId' => 2])->find(); + if (!$user) { + return ResponseHelper::error('用户不存在或没有权限'); + } + + $updateData = []; + + // 更新账号 + if (!empty($account)) { + // 检查账号是否已被其他用户使用(同一 typeId 下) + $existUser = Db::name('users')->where(['account' => $account, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->where('id', '<>', $userId) + ->find(); + if ($existUser) { + return ResponseHelper::error('账号已被使用'); + } + $updateData['account'] = $account; + } + + // 更新昵称 + if (!empty($username)) { + $updateData['username'] = $username; + } + + // 更新手机号 + if (!empty($phone)) { + if (!preg_match('/^1[3-9]\d{9}$/', $phone)) { + return ResponseHelper::error('手机号格式不正确'); + } + // 检查手机号是否已被其他用户使用(同一 typeId 下) + $existPhone = Db::name('users')->where(['phone' => $phone, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->where('id', '<>', $userId) + ->find(); + if ($existPhone) { + return ResponseHelper::error('手机号已被使用'); + } + $updateData['phone'] = $phone; + } + + // 更新密码 + if (!empty($password)) { + if (strlen($password) < 6 || strlen($password) > 20) { + return ResponseHelper::error('密码长度必须在6-20个字符之间'); + } + $updateData['passwordMd5'] = md5($password); + $updateData['passwordLocal'] = localEncrypt($password); + } + + // 更新设备绑定 + if (!empty($deviceId)) { + // 检查设备是否存在且属于当前公司 + $device = Device::where('id', $deviceId) + ->where('companyId', $companyId) + ->find(); + if (!$device) { + return ResponseHelper::error('设备不存在或没有权限'); + } + } + + // 开始事务 + Db::startTrans(); + try { + // 更新用户信息 + if (!empty($updateData)) { + $updateData['updateTime'] = time(); + Db::name('users')->where(['id' => $userId])->update($updateData); + } + + // 更新设备绑定 + if (!empty($deviceId)) { + // 删除旧的设备绑定 + Db::name('device_user')->where(['userId' => $userId, 'companyId' => $companyId])->delete(); + + // 添加新的设备绑定 + Db::name('device_user')->insert([ + 'companyId' => $companyId, + 'userId' => $userId, + 'deviceId' => $deviceId, + 'deleteTime' => 0, + ]); + } + + // 提交事务 + Db::commit(); + + return ResponseHelper::success('更新账号成功'); + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 删除账号 + * @return \think\response\Json + */ + public function delete() + { + try { + $userId = $this->request->param('userId', 0); + $companyId = $this->getUserInfo('companyId'); + + if (empty($userId)) { + return ResponseHelper::error('用户ID不能为空'); + } + + // 检查用户是否存在且属于当前公司 + $user = Db::name('users')->where(['id' => $userId, 'companyId' => $companyId, 'typeId' => 2])->find(); + if (!$user) { + return ResponseHelper::error('用户不存在或没有权限'); + } + + // 检查是否是管理账号 + if ($user['isAdmin'] == 1) { + return ResponseHelper::error('管理账号无法删除'); + } + + // 软删除用户 + Db::name('users')->where(['id' => $userId])->update([ + 'deleteTime' => time(), + 'updateTime' => time() + ]); + + // 软删除设备绑定关系 + Db::name('device_user')->where(['userId' => $userId, 'companyId' => $companyId])->update([ + 'deleteTime' => time() + ]); + + return ResponseHelper::success('删除账号成功'); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 禁用/启用账号 + * @return \think\response\Json + */ + public function disable() + { + try { + $userId = $this->request->param('userId', 0); + $status = $this->request->param('status', -1); // 0-禁用 1-启用 + $companyId = $this->getUserInfo('companyId'); + + if (empty($userId)) { + return ResponseHelper::error('用户ID不能为空'); + } + + if ($status != 0 && $status != 1) { + return ResponseHelper::error('状态参数错误'); + } + + // 检查用户是否存在且属于当前公司 + $user = Db::name('users')->where(['id' => $userId, 'companyId' => $companyId, 'typeId' => 2])->find(); + if (!$user) { + return ResponseHelper::error('用户不存在或没有权限'); + } + + // 检查是否是管理账号 + if ($user['isAdmin'] == 1 && $status == 0) { + return ResponseHelper::error('管理账号无法禁用'); + } + + // 更新状态 + Db::name('users')->where(['id' => $userId])->update([ + 'status' => $status, + 'updateTime' => time() + ]); + + $message = $status == 0 ? '禁用账号成功' : '启用账号成功'; + return ResponseHelper::success($message); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 获取账号列表 + * @return \think\response\Json + */ + public function index() + { + try { + $keyword = $this->request->param('keyword', ''); + $status = $this->request->param('status', ''); + $page = $this->request->param('page/d', 1); + $limit = $this->request->param('limit/d', 10); + + $companyId = $this->getUserInfo('companyId'); + + // 构建查询条件 + $where = [ + ['companyId', '=', $companyId], + ['typeId', '=', 2], // 只查询门店端账号 + ['deleteTime', '=', 0] + ]; + + // 关键词搜索(账号、昵称、手机号) + if (!empty($keyword)) { + $where[] = ['account|username|phone', "LIKE", '%'.$keyword.'%']; + } + + // 状态筛选 + if ($status !== '') { + $where[] = ['status', '=', $status]; + } + + // 分页查询 + $query = Db::name('users')->where($where); + $total = $query->count(); + + $list = $query->field('id,account,username,phone,avatar,isAdmin,status,balance,tokens,createTime') + ->order('id desc') + ->page($page, $limit) + ->select(); + + + // 获取每个账号绑定的设备(单个设备) + if (!empty($list)) { + $userIds = array_column($list, 'id'); + $deviceBindings = Db::name('device_user') + ->alias('du') + ->join('device d', 'd.id = du.deviceId', 'left') + ->where([ + ['du.userId', 'in', $userIds], + ['du.companyId', '=', $companyId], + ['du.deleteTime', '=', 0] + ]) + ->field('du.userId,du.deviceId,d.imei,d.memo') + ->order('du.id desc') + ->select(); + + // 组织设备数据(单个设备对象) + $deviceMap = []; + foreach ($deviceBindings as $binding) { + $deviceMap[$binding['userId']] = [ + 'deviceId' => $binding['deviceId'], + 'imei' => $binding['imei'], + 'memo' => $binding['memo'] + ]; + } + + // 将设备信息添加到用户数据中 + foreach ($list as &$item) { + $item['device'] = $deviceMap[$item['id']] ?? null; + } + } + + return ResponseHelper::success([ + 'list' => $list, + 'total' => $total, + 'page' => $page, + 'limit' => $limit + ]); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } +} diff --git a/Server/application/store/controller/LoginController.php b/Server/application/store/controller/LoginController.php new file mode 100644 index 00000000..f507295a --- /dev/null +++ b/Server/application/store/controller/LoginController.php @@ -0,0 +1,25 @@ +request->param('deviceId', ''); + if (empty($deviceId)) { + return errorJson('缺少必要参数'); + } + + $user = Db::name('user')->alias('u') + ->join('device_user du','u.id = du.userId and u.companyId = du.companyId') + ->join('device d','du.deviceId = d.id and u.companyId = du.companyId') + ->where(['d.deviceImei' => $deviceId,'u.deleteTime' => 0,'du.deleteTime' => 0,'d.deleteTime'=> 0 ]) + ->find(); + + exit_data($user); + + } +} \ No newline at end of file diff --git a/Store_vue/api/modules/auth.js b/Store_vue/api/modules/auth.js index b6855228..cd7c7f56 100644 --- a/Store_vue/api/modules/auth.js +++ b/Store_vue/api/modules/auth.js @@ -3,14 +3,18 @@ import { request, requestWithRetry } from '../config' // 认证相关API export const authApi = { // 用户登录 - login: (account, password) => { + // @param {string} account - 账号 + // @param {string} password - 密码 + // @param {string} deviceId - 设备ID(仅APP端传递,H5端为空字符串) + login: (account, password, deviceId) => { return request({ url: '/v1/auth/login', method: 'POST', data: { account: account, password: password, - typeId: 2 // 固定为2 + typeId: 2, // 固定为2 + deviceId: deviceId || '' // 设备ID(仅APP端有值) } }) } diff --git a/Store_vue/pages/login/index.vue b/Store_vue/pages/login/index.vue index 87bcaec9..4c5eb2e9 100644 --- a/Store_vue/pages/login/index.vue +++ b/Store_vue/pages/login/index.vue @@ -8,96 +8,39 @@ - - - - 验证码登录 - - - 密码登录 + + + + + + + - - - 您所在地区仅支持 手机号 + + + + + + + + - - - - - +86 - - - - - - - - {{ codeText }} - - - - - - - - - - +86 - - - - - - - - - - - - - @@ -118,40 +61,6 @@ 登录 - - - - - - - - - - 联系我们 @@ -168,44 +77,63 @@ export default { data() { return { - loginType: 'password', // 默认密码登录 - phone: '', // 手机号 - code: '', // 验证码 + account: '', // 账号 password: '', // 密码 passwordVisible: false, // 密码是否可见 agreement: true, // 是否同意协议 - codeSending: false, // 是否正在发送验证码 - countdown: 60, // 倒计时 - codeText: '发送验证码' // 验证码按钮文本 + deviceId: '' // 设备ID } }, // 页面加载时检查token onLoad() { this.checkTokenStatus(); + this.getDeviceId(); }, // 页面显示时检查token onShow() { this.checkTokenStatus(); }, computed: { - // 验证手机号是否有效 - isPhoneValid() { - return this.phone && this.phone.length === 11; - }, // 验证是否可以登录 canLogin() { - if (!this.phone || !this.agreement) { - return false; - } - - if (this.loginType === 'code') { - return this.isPhoneValid && this.code && this.code.length === 6; - } else { - return this.password && this.password.length >= 6; - } + return this.account && + this.password && + this.password.length >= 6 && + this.agreement; } }, methods: { + // 获取设备ID(仅APP端) + getDeviceId() { + // #ifdef APP-PLUS + try { + // 获取设备信息 + uni.getSystemInfo({ + success: (res) => { + console.log('设备信息:', res); + // 优先使用deviceId,如果没有则使用uuid或其他唯一标识 + this.deviceId = res.deviceId || res.uuid || res.system + '_' + res.model; + console.log('APP设备ID:', this.deviceId); + }, + fail: (err) => { + console.error('获取设备信息失败:', err); + // 如果获取失败,使用一个临时ID + this.deviceId = 'unknown_device'; + } + }); + } catch (err) { + console.error('获取设备ID异常:', err); + this.deviceId = 'unknown_device'; + } + // #endif + + // #ifdef H5 + // H5端不传设备ID + this.deviceId = ''; + console.log('H5端不传设备ID'); + // #endif + }, + // 检查token状态 checkTokenStatus() { // 如果token有效,则跳转到聊天页面 @@ -219,37 +147,7 @@ uni.navigateBack(); }, - // 切换登录类型 - switchLoginType(type) { - this.loginType = type; - }, - - // 发送验证码 - sendCode() { - if (this.codeSending || !this.isPhoneValid) return; - - this.codeSending = true; - this.countdown = 60; - this.codeText = `${this.countdown}秒后重发`; - - // 模拟发送验证码 - uni.showToast({ - title: '验证码已发送', - icon: 'success' - }); - - const timer = setInterval(() => { - this.countdown--; - this.codeText = `${this.countdown}秒后重发`; - - if (this.countdown <= 0) { - clearInterval(timer); - this.codeSending = false; - this.codeText = '发送验证码'; - } - }, 1000); - }, - + // 用户协议复选框变化 checkboxChange(){ this.agreement = !this.agreement }, @@ -258,7 +156,6 @@ // 处理登录 async handleLogin() { // 检查是否同意协议 - console.log(this.agreement) if (!this.agreement) { uni.showToast({ title: '请阅读并同意用户协议和隐私政策', @@ -268,24 +165,28 @@ return; } - if (!this.canLogin) { - // 显示错误原因 - if (!this.isPhoneValid) { - uni.showToast({ - title: '请输入正确的手机号', - icon: 'none' - }); - } else if (this.loginType === 'code' && (!this.code || this.code.length !== 6)) { - uni.showToast({ - title: '请输入6位验证码', - icon: 'none' - }); - } else if (this.loginType === 'password' && (!this.password || this.password.length < 6)) { - uni.showToast({ - title: '密码不能少于6位', - icon: 'none' - }); - } + // 验证表单 + if (!this.account) { + uni.showToast({ + title: '请输入账号', + icon: 'none' + }); + return; + } + + if (!this.password) { + uni.showToast({ + title: '请输入密码', + icon: 'none' + }); + return; + } + + if (this.password.length < 6) { + uni.showToast({ + title: '密码不能少于6位', + icon: 'none' + }); return; } @@ -295,11 +196,16 @@ }); try { - // 调用登录API - const loginPassword = this.loginType === 'password' ? this.password : this.code; - const response = await authApi.login(this.phone, loginPassword); + // 调用登录API,传递账号、密码和设备ID(仅APP端传递) + const response = await authApi.login(this.account, this.password, this.deviceId); - console.log(response); + console.log('登录响应:', response); + // #ifdef APP-PLUS + console.log('APP端登录 - 设备ID:', this.deviceId); + // #endif + // #ifdef H5 + console.log('H5端登录 - 不传设备ID'); + // #endif if (response.code === 200) { // 成功code是200 // 登录成功,缓存token信息 @@ -337,20 +243,12 @@ } }, - // 第三方登录 - handleThirdLogin(platform) { - // uni.showToast({ - // title: `${platform === 'wechat' ? '微信' : 'Apple'}登录功能暂未实现`, - // icon: 'none' - // }); - }, - // 打开协议 openAgreement(type) { - // uni.showToast({ - // title: `打开${type === 'user' ? '用户协议' : '隐私政策'}`, - // icon: 'none' - // }); + uni.showToast({ + title: `打开${type === 'user' ? '用户协议' : '隐私政策'}`, + icon: 'none' + }); }, // 联系我们 @@ -404,42 +302,12 @@ padding: 0 30px; } - .tab-container { - display: flex; - justify-content: space-between; - margin-bottom: 20px; - position: relative; - } - - .tab-item { - position: relative; - text-align: center; - padding: 10px 0; - font-size: 16px; - color: #666; - flex: 1; - } - - .tab-item.active { - color: #4080ff; + .login-title { + font-size: 24px; font-weight: bold; - } - - .tab-item.active::after { - content: ''; - position: absolute; - bottom: -2px; - left: 25%; - width: 50%; - height: 3px; - background-color: #4080ff; - border-radius: 2px; - } - - .tip-text { - font-size: 14px; - color: #666; - margin-bottom: 20px; + color: #333; + margin: 30px 0 40px; + text-align: center; } .input-item { @@ -447,15 +315,8 @@ align-items: center; border-bottom: 1px solid #eee; padding: 12px 0; - height: 24px; - } - - .input-prefix { - color: #333; - margin-right: 10px; - padding-right: 10px; - border-right: 1px solid #eee; - font-size: 14px; + min-height: 50px; + position: relative; } .input-field { @@ -464,25 +325,9 @@ font-size: 15px; } - .code-input-box { - position: relative; - } - - .send-code-btn { + .password-icon { position: absolute; right: 0; - background-color: #4080ff; - color: #fff; - padding: 5px 10px; - border-radius: 4px; - font-size: 12px; - } - - .send-code-btn.disabled { - background-color: #ccc; - } - - .password-icon { padding: 0 5px; height: 100%; display: flex; @@ -526,28 +371,6 @@ background-color: #4080ff; } - .divider { - display: flex; - align-items: center; - margin: 20px 0; - } - - .divider-line { - flex: 1; - height: 1px; - background-color: #eee; - } - - .divider-text { - color: #999; - padding: 0 15px; - font-size: 14px; - } - - .third-party-login { - margin: 20px 0; - } - .contact-us { text-align: center; color: #999;