From 3ed4bd7ecaad47700dba10f0070293d412010ac6 Mon Sep 17 00:00:00 2001 From: Ghost <106998207@qq.com> Date: Mon, 17 Mar 2025 10:09:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/.env | 15 + .../api/controller/BaseController.php | 60 +-- .../api/controller/BaseLoginController.php | 10 - .../api/controller/StatsController.php | 75 +++ .../api/controller/UserController.php | 374 ++++++++++++++ .../api/controller/WechatController.php | 102 ++++ .../api/controller/WechatFriendController.php | 127 +++++ Server/application/common.php | 457 ++++++++++++++++++ .../common/model/CompanyAccountModel.php | 11 + .../common/model/DeviceGroupModel.php | 11 + .../common/model/WechatAccountModel.php | 19 + .../common/model/WechatFriendModel.php | 11 + .../frontend/controller/IndexController.php | 16 - Server/config/app.php | 6 +- Server/config/database.php | 15 +- 15 files changed, 1224 insertions(+), 85 deletions(-) create mode 100644 Server/.env delete mode 100644 Server/application/api/controller/BaseLoginController.php create mode 100644 Server/application/api/controller/StatsController.php create mode 100644 Server/application/api/controller/UserController.php create mode 100644 Server/application/api/controller/WechatController.php create mode 100644 Server/application/api/controller/WechatFriendController.php create mode 100644 Server/application/common/model/CompanyAccountModel.php create mode 100644 Server/application/common/model/DeviceGroupModel.php create mode 100644 Server/application/common/model/WechatAccountModel.php create mode 100644 Server/application/common/model/WechatFriendModel.php delete mode 100755 Server/application/frontend/controller/IndexController.php diff --git a/Server/.env b/Server/.env new file mode 100644 index 00000000..17cc44b4 --- /dev/null +++ b/Server/.env @@ -0,0 +1,15 @@ +[app] +debug = true +trace = true + +[database] +type = mysql +hostname = 127.0.0.1 +database = yi_54iis_com +username = yi_54iis_com +password = c1RbMwrZCCyxF1bC +hostport = 3306 +prefix = tk_ + +[api] +wechat_url = https://s2.siyuguanli.com:9991/ \ No newline at end of file diff --git a/Server/application/api/controller/BaseController.php b/Server/application/api/controller/BaseController.php index cc2fdb30..f3aac3bd 100644 --- a/Server/application/api/controller/BaseController.php +++ b/Server/application/api/controller/BaseController.php @@ -2,71 +2,33 @@ namespace app\api\controller; +use app\common\model\UserModel; +use app\common\model\UserTokenModel; use think\Controller; +use think\facade\Env; class BaseController extends Controller { + /** * 令牌 * - * @var null + * @var string */ - protected $token = NULL; + protected $token = ''; - protected function initialize() { - parent::initialize(); + protected $baseUrl; + public function __construct() { + parent::__construct(); + $this->baseUrl = Env::get('api.wechat_url'); // 允许跨域 header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: *'); header('Access-Control-Allow-Headers: *'); } - /** - * 接口调用成功 JSON - * - * @param null $data - * @return \think\response\Json - */ - protected function jsonSucc($data = NULL) { - return json([ - 'code' => 0, - 'msg' => '操作成功', - 'data' => $data, - ]); - } - /** - * 接口调用错误 JSON - * - * @param $error - * @param int $code - * @return \think\response\Json - */ - protected function jsonFail($error, $code = 500) { - return json([ - 'code' => $code, - 'msg' => $error, - ]); - } - /** - * 获取URL - * - * @param $url - * @return string - */ - protected function absoluteUrl($url) { - return (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . ($url{0} === '/' ? $url : '/' . $url); - } - /** - * 小数格式化 - * - * @param $float - * @return float - */ - protected function floatFormat($float) { - return floatval($float); - } -} \ No newline at end of file +} diff --git a/Server/application/api/controller/BaseLoginController.php b/Server/application/api/controller/BaseLoginController.php deleted file mode 100644 index fe8597bb..00000000 --- a/Server/application/api/controller/BaseLoginController.php +++ /dev/null @@ -1,10 +0,0 @@ -request->header('authorization', '')); + if (empty($authorization)) { + return errorJson('缺少授权信息'); + } + + $headerData = ['client:' . self::CLIENT_TYPE]; + $header = setHeader($headerData, $authorization, 'plain'); + + try { + $result = requestCurl($this->baseUrl . '/api/DashBoard/ListHomePageStatistics', ['refresh' => 10000], 'GET', $header); + return successJson($result); + } catch (\Exception $e) { + return errorJson('获取基础数据失败:' . $e->getMessage()); + } + } + + /** + * 好友统计 + * @return \think\response\Json + */ + public function FansStatistics(){ + /* 参数说明 + lidu 数据搜索类型 0 小时 1 天 2月 + from to 时间 当lidu为 0时(2025-03-12 09:54:42) 当lidu为 1时(2025-03-12) 当lidu为 2时(2025-03) + */ + + $authorization = trim($this->request->header('authorization', '')); + $lidu = trim($this->request->param('lidu', '')); + $from = trim($this->request->param('from', '')); + $to = trim($this->request->param('to', '')); + + if (empty($authorization)) { + return errorJson('缺少授权信息'); + } + + $params = [ + 'lidu' => $lidu, + 'from' => $from, + 'to' => $to, + ]; + + $headerData = ['client:' . self::CLIENT_TYPE]; + $header = setHeader($headerData, $authorization, 'plain'); + + try { + $result = requestCurl($this->baseUrl . 'api/DashBoard/listStatisticsCountDTOByCreateTimeAsync', $params, 'GET', $header); + return successJson($result); + } catch (\Exception $e) { + return errorJson('获取粉丝统计数据失败:' . $e->getMessage()); + } + } + +} \ No newline at end of file diff --git a/Server/application/api/controller/UserController.php b/Server/application/api/controller/UserController.php new file mode 100644 index 00000000..550d5850 --- /dev/null +++ b/Server/application/api/controller/UserController.php @@ -0,0 +1,374 @@ +validateLoginParams(); + if (!is_array($params)) { + return $params; + } + + // 验证账号是否存在 + $existingAccount = CompanyAccountModel::where('userName', $params['username'])->find(); + if (empty($existingAccount)) { + // 记录登录失败日志 + recordUserLog(0, $params['username'], 'LOGIN', '账号不存在', $params, 500, '账号不存在'); + return errorJson('账号不存在'); + } + + // 获取验证码会话ID和用户输入的验证码 + $verifySessionId = $this->request->param('verifySessionId', ''); + $verifyCode = $this->request->param('verifyCode', ''); + + // 设置请求头 + $headerData = ['client:' . self::CLIENT_TYPE]; + + // 如果存在验证码信息,添加到请求头 + if (!empty($verifySessionId) && !empty($verifyCode)) { + $headerData[] = 'verifysessionid:' . $verifySessionId; + $headerData[] = 'verifycode:' . $verifyCode; + } + + $header = setHeader($headerData, '', 'plain'); + + try { + // 请求登录接口 + $result = requestCurl($this->baseUrl . 'token', $params, 'POST', $header); + $result_array = handleApiResponse($result); + + if (is_array($result_array) && isset($result_array['error'])) { + // 记录登录失败日志 + recordUserLog(0, $params['username'], 'LOGIN', '登录失败', $params, 500, $result_array['error_description']); + return errorJson($result_array['error_description']); + } + + // 获取客户端IP地址 + $ip = $this->request->ip(); + + // 登录成功,更新密码信息和登录信息 + $updateData = [ + 'passwordMd5' => md5($params['password']), + 'passwordLocal' => localEncrypt($params['password']), + 'lastLoginIp' => $ip, + 'lastLoginTime' => time() + ]; + + // 更新密码信息 + CompanyAccountModel::where('userName', $params['username'])->update($updateData); + + // 记录登录成功日志 + recordUserLog($existingAccount['id'], $params['username'], 'LOGIN', '登录成功', [], 200, '登录成功'); + + return successJson($result_array); + } catch (\Exception $e) { + // 记录登录异常日志 + recordUserLog(0, $params['username'], 'LOGIN', '登录请求失败', $params, 500, $e->getMessage()); + return errorJson('登录请求失败:' . $e->getMessage()); + } + } + + /** + * 获取新的token + * @return \think\response\Json + */ + public function getNewToken() + { + $grant_type = $this->request->param('grant_type', 'refresh_token'); + $refresh_token = $this->request->param('refresh_token', ''); + $authorization = $this->request->header('authorization', ''); + + if (empty($grant_type) || empty($authorization)) { + return errorJson('参数错误'); + } + + $params = [ + 'grant_type' => $grant_type, + 'refresh_token' => $refresh_token, + ]; + + + + $headerData = ['client:' . self::CLIENT_TYPE]; + $header = setHeader($headerData, $authorization, 'system'); + + try { + $result = requestCurl($this->baseUrl . 'token', $params, 'POST', $header); + $result_array = handleApiResponse($result); + + if (is_array($result_array) && isset($result_array['error'])) { + recordUserLog(0, '', 'REFRESH_TOKEN', '刷新token失败', $params, 500, $result_array['error_description']); + return errorJson($result_array['error_description']); + } + + recordUserLog(0, '', 'REFRESH_TOKEN', '刷新token成功', $params, 200, '刷新成功'); + return successJson($result_array); + } catch (\Exception $e) { + recordUserLog(0, '', 'REFRESH_TOKEN', '刷新token异常', $params, 500, $e->getMessage()); + return errorJson('获取新token失败:' . $e->getMessage()); + } + } + + /** + * 获取商户基本信息 + * @return \think\response\Json + */ + public function getAccountInfo() + { + $authorization = trim($this->request->header('authorization', '')); + if (empty($authorization)) { + return errorJson('缺少授权信息'); + } + + $headerData = ['client:' . self::CLIENT_TYPE]; + $header = setHeader($headerData, $authorization, 'plain'); + + try { + $result = requestCurl($this->baseUrl . 'api/Account/self', [], 'GET', $header); + $response = handleApiResponse($result); + + if (!empty($response['account'])) { + $accountData = $response['account']; + + // 准备数据库字段映射,保持驼峰命名 + $dbData = [ + 'accountId' => $accountData['id'], + 'realName' => $accountData['realName'], + 'nickname' => $accountData['nickname'], + 'memo' => $accountData['memo'], + 'avatar' => $accountData['avatar'], + 'userName' => $accountData['userName'], + 'secret' => $accountData['secret'], + 'accountType' => $accountData['accountType'], + 'departmentId' => $accountData['departmentId'], + 'useGoogleSecretKey' => $accountData['useGoogleSecretKey'], + 'hasVerifyGoogleSecret' => $accountData['hasVerifyGoogleSecret'], + 'updateTime' => time() + ]; + + + // 查找是否存在该账户 + $existingAccount = CompanyAccountModel::where('userName', $accountData['userName'])->find(); + if ($existingAccount) { + // 更新现有记录 + CompanyAccountModel::where('userName', $accountData['userName'])->update($dbData); + } else { + // 创建新记录 + $dbData['createTime'] = time(); + CompanyAccountModel::create($dbData); + } + return successJson($response['account']); + }else{ + return successJson($response); + } + + + } catch (\Exception $e) { + recordUserLog(0, '', 'GET_ACCOUNT_INFO', '获取账户信息异常', [], 500, $e->getMessage()); + return errorJson('获取账户信息失败:' . $e->getMessage()); + } + } + + /** + * 修改密码 + * @return \think\response\Json + */ + public function modifyPwd() + { + // 获取并验证参数 + $params = $this->validateModifyPwdParams(); + if (!is_array($params)) { + return $params; + } + + $authorization = trim($this->request->header('authorization', '')); + if (empty($authorization)) { + return errorJson('缺少授权信息'); + } + + $headerData = ['client:' . self::CLIENT_TYPE]; + $header = setHeader($headerData, $authorization, 'plain'); + + try { + $result = requestCurl($this->baseUrl . 'api/Account/self', $params, 'PUT', $header); + $response = handleApiResponse($result); + + if (empty($response)) { + // 获取当前用户信息 + $currentUser = CompanyAccountModel::where('token', $authorization)->find(); + if ($currentUser) { + recordUserLog($currentUser['id'], $currentUser['userName'], 'MODIFY_PASSWORD', '修改密码成功', [], 200, '修改成功'); + } + return successJson(['message' => '修改成功']); + } + + recordUserLog(0, '', 'MODIFY_PASSWORD', '修改密码失败', $params, 500, $response); + return errorJson($response); + } catch (\Exception $e) { + recordUserLog(0, '', 'MODIFY_PASSWORD', '修改密码异常', $params, 500, $e->getMessage()); + return errorJson('修改密码失败:' . $e->getMessage()); + } + } + + /** + * 登出 + * @return \think\response\Json + */ + public function logout() + { + $authorization = trim($this->request->header('authorization', '')); + if (empty($authorization)) { + return errorJson('缺少授权信息'); + } + + $headerData = ['client:' . self::CLIENT_TYPE]; + $header = setHeader($headerData, $authorization, 'plain'); + + try { + // 获取当前用户信息 + $currentUser = CompanyAccountModel::where('token', $authorization)->find(); + + // 调用外部退出登录接口 + $result = requestCurl($this->baseUrl . 'api/Account/SignOut', [], 'POST', $header); + + if ($currentUser) { + recordUserLog($currentUser['id'], $currentUser['userName'], 'LOGOUT', '退出登录成功', [], 200, '退出成功'); + } + + return successJson([] , '退出成功'); + } catch (\Exception $e) { + recordUserLog(0, '', 'LOGOUT', '退出登录异常', [], 500, $e->getMessage()); + return errorJson('退出登录失败:' . $e->getMessage()); + } + } + + /** + * 获取验证码 + * @return \think\response\Json + */ + public function getVerifyCode() + { + $headerData = ['client:' . self::CLIENT_TYPE]; + $header = setHeader($headerData, '', 'plain'); + + try { + $result = requestCurl($this->baseUrl . 'api/Account/getVerifyCode', [], 'GET', $header); + $response = handleApiResponse($result); + + // 检查返回的数据格式 + if (is_array($response)) { + // 如果verifyCodeImage和verifySessionId都不为null,返回它们 + if (!empty($response['verifyCodeImage']) && !empty($response['verifySessionId'])) { + return successJson([ + 'verifyCodeImage' => $response['verifyCodeImage'], + 'verifySessionId' => $response['verifySessionId'] + ]); + } + } + + // 如果不是预期的格式,返回原始数据 + return successJson($response); + } catch (\Exception $e) { + return errorJson('获取验证码失败:' . $e->getMessage()); + } + } + + /** + * 验证登录参数 + * @return array|\think\response\Json + */ + private function validateLoginParams() + { + $username = trim($this->request->param('username', '')); + $password = trim($this->request->param('password', '')); + $verifyCode = trim($this->request->param('verifyCode', '')); + $verifySessionId = trim($this->request->param('verifySessionId', '')); + + if (empty($username) || empty($password)) { + return errorJson('用户名和密码不能为空'); + } + + // 验证密码格式 + $passwordValidation = validateString($password, 'password',['max_length' => 20]); + if (!$passwordValidation['status']) { + return errorJson($passwordValidation['message']); + } + + // 如果提供了验证码,验证格式 + if (!empty($verifyCode)) { + if (empty($verifySessionId)) { + return errorJson('验证码会话ID不能为空'); + } + // 验证码格式验证(假设是4位数字) + if (!preg_match('/^\d{4}$/', $verifyCode)) { + return errorJson('验证码格式不正确'); + } + } + + return [ + 'grant_type' => 'password', + 'username' => $username, + 'password' => $password, + ]; + } + + /** + * 验证修改密码参数 + * @return array|\think\response\Json + */ + private function validateModifyPwdParams() + { + $cPw = trim($this->request->param('cPw', '')); + $newPw = trim($this->request->param('newPw', '')); + $oldPw = trim($this->request->param('oldPw', '')); + + if (empty($cPw) || empty($newPw) || empty($oldPw)) { + return errorJson('密码参数不完整'); + } + + if ($newPw !== $cPw) { + return errorJson('两次输入的新密码不一致'); + } + + // 验证新密码格式 + $passwordValidation = validateString($newPw, 'password'); + if (!$passwordValidation['status']) { + return errorJson($passwordValidation['message']); + } + + return [ + 'cPw' => $cPw, + 'newPw' => $newPw, + 'oldPw' => $oldPw, + ]; + } +} diff --git a/Server/application/api/controller/WechatController.php b/Server/application/api/controller/WechatController.php new file mode 100644 index 00000000..baaef0f6 --- /dev/null +++ b/Server/application/api/controller/WechatController.php @@ -0,0 +1,102 @@ + $item['wechatId'], + 'deviceAccountId' => $item['deviceAccountId'], + 'imei' => $item['imei'], + 'deviceMemo' => $item['deviceMemo'], + 'accountUserName' => $item['accountUserName'], + 'accountRealName' => $item['accountRealName'], + 'accountNickname' => $item['accountNickname'], + 'keFuAlive' => $item['keFuAlive'], + 'deviceAlive' => $item['deviceAlive'], + 'wechatAlive' => $item['wechatAlive'], + 'yesterdayMsgCount' => $item['yesterdayMsgCount'], + 'sevenDayMsgCount' => $item['sevenDayMsgCount'], + 'thirtyDayMsgCount' => $item['thirtyDayMsgCount'], + 'totalFriend' => $item['totalFriend'], + 'maleFriend' => $item['maleFriend'], + 'femaleFriend' => $item['femaleFriend'], + 'wechatGroupName' => $item['wechatGroupName'], + 'tenantId' => $item['tenantId'], + 'nickname' => $item['nickname'], + 'alias' => $item['alias'], + 'avatar' => $item['avatar'], + 'gender' => $item['gender'], + 'region' => $item['region'], + 'signature' => $item['signature'], + 'bindQQ' => $item['bindQQ'], + 'bindEmail' => $item['bindEmail'], + 'bindMobile' => $item['bindMobile'], + 'currentDeviceId' => $item['currentDeviceId'], + 'isDeleted' => $item['isDeleted'], + 'deleteTime' => $item['deleteTime'], + 'groupId' => $item['groupId'], + 'memo' => $item['memo'], + 'wechatVersion' => $item['wechatVersion'], + 'labels' => $item['labels'] + ]; + + $account = WechatAccountModel::where('wechatId', $item['wechatId'])->find(); + if ($account) { + $account->save($data); + } else { + WechatAccountModel::create($data); + } + } + + public function getWechatAccountList() + { + // 获取授权token + $authorization = trim($this->request->header('authorization', '')); + if (empty($authorization)) { + return errorJson('缺少授权信息'); + } + + try { + // 构建请求参数 + $params = [ + 'wechatAlive' => input('wechatAlive', ''), + 'keyword' => input('keyword', ''), + 'groupId' => input('groupId', ''), + 'departmentId' => input('departmentId', ''), + 'hasDevice' => input('hasDevice', ''), + 'deviceGroupId' => input('deviceGroupId', ''), + 'containSubDepartment' => input('containSubDepartment', 'false'), + 'pageIndex' => input('pageIndex', 0), + 'pageSize' => input('pageSize', 10) + ]; + + // 设置请求头 + $headerData = ['client:system']; + $header = setHeader($headerData, $authorization, 'plain'); + + // 发送请求 + $result = requestCurl($this->baseUrl . 'api/WechatAccount/list', $params, 'GET', $header); + $response = handleApiResponse($result); + + // 保存数据到数据库 + if (!empty($response['results'])) { + foreach ($response['results'] as $item) { + $this->saveWechatAccount($item); + } + } + + return successJson($response); + } catch (\Exception $e) { + return errorJson('获取微信账号列表失败:' . $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/Server/application/api/controller/WechatFriendController.php b/Server/application/api/controller/WechatFriendController.php new file mode 100644 index 00000000..f8e845d9 --- /dev/null +++ b/Server/application/api/controller/WechatFriendController.php @@ -0,0 +1,127 @@ +request->header('authorization', '')); + if (empty($authorization)) { + return errorJson('缺少授权信息'); + } + + try { + // 构建请求参数 + $params = [ + 'accountKeyword' => input('accountKeyword', ''), + 'addFrom' => input('addFrom', []), + 'allotAccountId' => input('allotAccountId', ''), + 'containAllLabel' => input('containAllLabel', false), + 'containSubDepartment' => input('containSubDepartment', false), + 'departmentId' => input('departmentId', ''), + 'extendFields' => input('extendFields', []), + 'friendKeyword' => input('friendKeyword', ''), + 'friendPhoneKeyword' => input('friendPhoneKeyword', ''), + 'friendPinYinKeyword' => input('friendPinYinKeyword', ''), + 'friendRegionKeyword' => input('friendRegionKeyword', ''), + 'friendRemarkKeyword' => input('friendRemarkKeyword', ''), + 'gender' => input('gender', ''), + 'groupId' => input('groupId', null), + 'isDeleted' => input('isDeleted', false), + 'isPass' => input('isPass', true), + 'keyword' => input('keyword', ''), + 'labels' => input('labels', []), + 'pageIndex' => input('pageIndex', 0), + 'pageSize' => input('pageSize', 20), + 'preFriendId' => input('preFriendId', ''), + 'wechatAccountKeyword' => input('wechatAccountKeyword', '') + ]; + + // 设置请求头 + $headerData = ['client:system']; + $header = setHeader($headerData, $authorization, 'plain'); + + // 发送请求获取好友列表 + $result = requestCurl($this->baseUrl . 'api/WechatFriend/friendlistData', $params, 'GET', $header); + $response = handleApiResponse($result); + + // 保存数据到数据库 + if (!empty($response['results'])) { + foreach ($response['results'] as $item) { + $this->saveFriend($item); + } + } + + return successJson($response); + } catch (\Exception $e) { + return errorJson('获取微信好友列表失败:' . $e->getMessage()); + } + } + + /** + * 保存微信好友数据到数据库 + * @param array $item 微信好友数据 + */ + private function saveFriend($item) + { + $data = [ + 'wechatAccountId' => $item['wechatAccountId'], + 'alias' => $item['alias'], + 'wechatId' => $item['wechatId'], + 'conRemark' => $item['conRemark'], + 'nickname' => $item['nickname'], + 'pyInitial' => $item['pyInitial'], + 'quanPin' => $item['quanPin'], + 'avatar' => $item['avatar'], + 'gender' => $item['gender'], + 'region' => $item['region'], + 'addFrom' => $item['addFrom'], + 'labels' => is_array($item['labels']) ? json_encode($item['labels']) : json_encode([]), + 'signature' => $item['signature'], + 'isDeleted' => $item['isDeleted'], + 'isPassed' => $item['isPassed'], + 'deleteTime' => $item['deleteTime'], + 'accountId' => $item['accountId'], + 'extendFields' => is_array($item['extendFields']) ? json_encode($item['extendFields']) : json_encode([]), + 'accountUserName' => $item['accountUserName'], + 'accountRealName' => $item['accountRealName'], + 'accountNickname' => $item['accountNickname'], + 'ownerAlias' => $item['ownerAlias'], + 'ownerWechatId' => $item['ownerWechatId'], + 'ownerNickname' => $item['ownerNickname'], + 'ownerAvatar' => $item['ownerAvatar'], + 'phone' => $item['phone'], + 'thirdParty' => is_array($item['thirdParty']) ? json_encode($item['thirdParty']) : json_encode([]), + 'groupId' => $item['groupId'], + 'passTime' => $item['passTime'], + 'additionalPicture' => $item['additionalPicture'], + 'desc' => $item['desc'], + 'country' => $item['country'], + 'province' => $item['province'], + 'city' => $item['city'], + 'createTime' => $item['createTime'] + ]; + + // 使用三个字段的组合作为唯一性判断 + $friend = WechatFriendModel::where([ + ['ownerWechatId', '=', $item['ownerWechatId']], + ['wechatId', '=', $item['wechatId']], + ['wechatAccountId', '=', $item['wechatAccountId']] + ])->find(); + + if ($friend) { + $friend->save($data); + } else { + WechatFriendModel::create($data); + } + } +} \ No newline at end of file diff --git a/Server/application/common.php b/Server/application/common.php index 55d22f26..d9fec0d5 100755 --- a/Server/application/common.php +++ b/Server/application/common.php @@ -10,3 +10,460 @@ // +---------------------------------------------------------------------- // 应用公共文件 + + +if (!function_exists('requestCurl')) { + /** + * @param string $url 请求的链接 + * @param array $params 请求附带的参数 + * @param string $method 请求的方式默认 GTE + * @param array $header 头部 + * @return bool|string + */ + function requestCurl($url, $params = [], $method = 'GET', $header = [], $type = 'dataBuild') + { + $str = ''; + if (!empty($url)) { + try { + $ch = curl_init(); + if (strtoupper($method) == 'GET' && !empty($params)) { + $url = $url . '?' . dataBuild($params); + curl_setopt($ch, CURLOPT_URL, $url); + } else { + curl_setopt($ch, CURLOPT_URL, $url); + } + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); //30秒超时 + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + if (strtoupper($method) == 'POST') { + curl_setopt($ch, CURLOPT_POST, 1); + if ($type == 'dataBuild') { + $params = dataBuild($params); + } elseif ($type == 'json') { + $params = json_encode($params); + } else { + $params = dataBuild($params); + } + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + } + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //是否验证对等证书,1则验证,0则不验证 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + $str = curl_exec($ch); + curl_close($ch); + } catch (Exception $e) { + $str = ''; + } + } + return $str; + } +} + + +if (!function_exists('dataBuild')) { + function dataBuild($array) + { + return is_array($array) ? http_build_query($array) : $array; + } +} + + +if (!function_exists('setHeader')) { + /** + * 设置头部 + * + * @param array $headerData 头部数组 + * @param string $authorization + * @param string $type 类型 默认json (json,plain) + * @return array + */ + function setHeader($headerData = [], $authorization = '', $type = '') + { + $header = $headerData; + + switch ($type) { + case 'json': + $header[] = 'Content-Type:application/json'; + break; + case 'html' : + $header[] = 'Content-Type:text/html'; + break; + case 'plain' : + $header[] = 'Content-Type:text/plain'; + break; + default: + $header[] = 'Content-Type:application/json'; + } +// $header[] = $type == 'plain' ? 'Content-Type:text/plain' : 'Content-Type: application/json'; + if ($authorization !== "") $header[] = 'authorization:bearer ' . $authorization; + return $header; + } +} + +if (!function_exists('errorJson')) { + function errorJson($error = '', $code = 500) + { + return json([ + 'code' => $code, + 'msg' => $error, + ]); + } +} + + +if (!function_exists('successJson')) { + function successJson($data = [] ,$error = '操作成功', $code = 200) + { + return json([ + 'data' => $data, + 'code' => $code, + 'msg' => $error, + ]); + } +} + +if (!function_exists('validateString')) { + /** + * 通用字符串验证 + * @param string $string 待验证的字符串 + * @param string $type 验证类型 (password|email|mobile|nickname|url|idcard|bankcard|ip|date|username|chinese|english|number|zip|qq) + * @param array $options 额外的验证选项 + * @return array ['status' => bool, 'message' => string] + */ + function validateString($string, $type, $options = []) + { + // 默认配置 + $config = [ + 'password' => [ + 'min_length' => 6, + 'pattern' => '/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]+$/', + 'error' => '密码必须包含英文和数字,不能包含特殊符号' + ], + 'email' => [ + 'pattern' => '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', + 'error' => '邮箱格式不正确' + ], + 'mobile' => [ + 'pattern' => '/^1[3456789]\d{9}$/', + 'error' => '手机号格式不正确' + ], + 'nickname' => [ + 'min_length' => 2, + 'max_length' => 20, + 'pattern' => '/^[a-zA-Z0-9\x{4e00}-\x{9fa5}]+$/u', + 'error' => '昵称只能包含中文、英文、数字,长度2-20位' + ], + 'url' => [ + 'pattern' => '/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/', + 'error' => '网址格式不正确' + ], + 'idcard' => [ + 'pattern' => '/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/', + 'error' => '身份证号码格式不正确' + ], + 'bankcard' => [ + 'pattern' => '/^[1-9]\d{9,29}$/', + 'error' => '银行卡号格式不正确' + ], + 'ip' => [ + 'pattern' => '/^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/', + 'error' => 'IP地址格式不正确' + ], + 'date' => [ + 'pattern' => '/^\d{4}(-)(0[1-9]|1[0-2])(-)([0-2][1-9]|3[0-1])$/', + 'error' => '日期格式不正确(YYYY-MM-DD)' + ], + 'username' => [ + 'min_length' => 4, + 'max_length' => 20, + 'pattern' => '/^[a-zA-Z][a-zA-Z0-9_]{3,19}$/', + 'error' => '用户名必须以字母开头,只能包含字母、数字和下划线,长度4-20位' + ], + 'chinese' => [ + 'pattern' => '/^[\x{4e00}-\x{9fa5}]+$/u', + 'error' => '只能输入中文汉字' + ], + 'english' => [ + 'pattern' => '/^[a-zA-Z]+$/', + 'error' => '只能输入英文字母' + ], + 'number' => [ + 'pattern' => '/^[0-9]+$/', + 'error' => '只能输入数字' + ], + 'zip' => [ + 'pattern' => '/^\d{6}$/', + 'error' => '邮政编码格式不正确' + ], + 'qq' => [ + 'pattern' => '/^[1-9][0-9]{4,}$/', + 'error' => 'QQ号格式不正确' + ], + 'age' => [ + 'pattern' => '/^(?:[1-9][0-9]?|1[01][0-9]|120)$/', + 'error' => '年龄必须在1-120之间' + ], + 'phone' => [ + 'pattern' => '/^([0-9]{3,4}-)?[0-9]{7,8}$/', + 'error' => '固定电话格式不正确' + ], + 'money' => [ + 'pattern' => '/^[0-9]+\.?[0-9]{0,2}$/', + 'error' => '金额格式不正确(最多保留两位小数)' + ], + 'color' => [ + 'pattern' => '/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/', + 'error' => '颜色格式不正确(#RGB或#RRGGBB)' + ] + ]; + + // 合并自定义配置 + if (!empty($options)) { + $config[$type] = array_merge($config[$type], $options); + } + + // 空值检查 + if (empty($string)) { + return ['status' => false, 'message' => '不能为空']; + } + + switch ($type) { + case 'password': + case 'username': + // 长度检查 + if (strlen($string) < $config[$type]['min_length']) { + return [ + 'status' => false, + 'message' => '长度不能小于' . $config[$type]['min_length'] . '位' + ]; + } + if (strlen($string) > $config[$type]['max_length']) { + return [ + 'status' => false, + 'message' => '长度不能大于' . $config[$type]['max_length'] . '位' + ]; + } + // 格式检查 + if (!preg_match($config[$type]['pattern'], $string)) { + return [ + 'status' => false, + 'message' => $config[$type]['error'] + ]; + } + break; + + case 'nickname': + // 长度检查 + $length = mb_strlen($string, 'UTF-8'); + if ($length < $config['nickname']['min_length'] || $length > $config['nickname']['max_length']) { + return [ + 'status' => false, + 'message' => '昵称长度必须在' . $config['nickname']['min_length'] . '-' . $config['nickname']['max_length'] . '位之间' + ]; + } + // 格式检查 + if (!preg_match($config['nickname']['pattern'], $string)) { + return [ + 'status' => false, + 'message' => $config['nickname']['error'] + ]; + } + break; + + case 'idcard': + // 身份证号验证 + if (!preg_match($config['idcard']['pattern'], $string)) { + return [ + 'status' => false, + 'message' => $config['idcard']['error'] + ]; + } + // 进一步验证18位身份证的最后一位校验码 + if (strlen($string) == 18) { + $idCardBase = substr($string, 0, 17); + $verify = substr($string, 17, 1); + if (!checkIdCardVerify($idCardBase, $verify)) { + return [ + 'status' => false, + 'message' => '身份证校验码错误' + ]; + } + } + break; + + default: + if (!isset($config[$type])) { + return [ + 'status' => false, + 'message' => '不支持的验证类型' + ]; + } + if (!preg_match($config[$type]['pattern'], $string)) { + return [ + 'status' => false, + 'message' => $config[$type]['error'] + ]; + } + break; + } + + return ['status' => true, 'message' => '验证通过']; + } +} + +if (!function_exists('checkIdCardVerify')) { + /** + * 验证身份证校验码 + * @param string $idCardBase 身份证前17位 + * @param string $verify 校验码 + * @return bool + */ + function checkIdCardVerify($idCardBase, $verify) + { + if (strlen($idCardBase) != 17) { + return false; + } + + // 加权因子 + $factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; + + // 校验码对应值 + $verifyNumberList = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']; + + // 根据前17位计算校验码 + $sum = 0; + for ($i = 0; $i < 17; $i++) { + $sum += substr($idCardBase, $i, 1) * $factor[$i]; + } + + $mod = $sum % 11; + $verifyNumber = $verifyNumberList[$mod]; + + return strtoupper($verify) == $verifyNumber; + } +} + +if (!function_exists('handleApiResponse')) { + /** + * 处理API响应 + * @param string $response 响应内容 + * @param bool $returnRaw 是否返回原始数据 + * @return mixed + */ + function handleApiResponse($response, $returnRaw = false) + { + if (empty($response)) { + return $response; + } + + // 检查是否为JSON格式 + $decoded = json_decode($response, true); + if (json_last_error() === JSON_ERROR_NONE) { + return $decoded; + } + + // 不是JSON格式,直接返回原始数据 + return $response; + } +} + +if (!function_exists('localEncrypt')) { + /** + * 本地密码加密 + * @param string $password 待加密的密码 + * @param string $key 加密密钥 + * @return string + */ + function localEncrypt($password, $key = 'karuo') + { + return openssl_encrypt($password, 'AES-256-CBC', $key, 0, substr(md5($key), 0, 16)); + } +} + +if (!function_exists('localDecrypt')) { + /** + * 本地密码解密 + * @param string $encrypted 已加密的密码 + * @param string $key 加密密钥 + * @return string + */ + function localDecrypt($encrypted, $key = 'karuo') + { + return openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, substr(md5($key), 0, 16)); + } +} + +if (!function_exists('recordUserLog')) { + /** + * 记录用户操作日志 + * @param int $userId 用户ID + * @param string $userName 用户名 + * @param string $action 操作类型 + * @param string $description 操作描述 + * @param array $requestData 请求数据 + * @param int $responseCode 响应状态码 + * @param string $responseMsg 响应消息 + * @return bool + */ + function recordUserLog($userId, $userName, $action, $description = '', $requestData = [], $responseCode = 200, $responseMsg = '操作成功') + { + try { + $request = request(); + + // 获取用户代理信息 + $userAgent = $request->header('user-agent'); + + // 准备日志数据 + $logData = [ + 'userId' => $userId, + 'userName' => $userName, + 'action' => $action, + 'description' => $description, + 'ip' => $request->ip(), + 'userAgent' => $userAgent, + 'requestMethod' => $request->method(), + 'requestUrl' => $request->url(true), + 'requestData' => !empty($requestData) ? json_encode($requestData, JSON_UNESCAPED_UNICODE) : '', + 'responseCode' => $responseCode, + 'responseMsg' => $responseMsg, + 'createTime' => time() + ]; + + // 写入日志 + \think\Db::name('user_log')->insert($logData); + return true; + } catch (\Exception $e) { + // 记录日志失败不影响主业务 + \think\facade\Log::error('记录用户日志失败:' . $e->getMessage()); + return false; + } + } +} + +if (!function_exists('getUserAction')) { + /** + * 获取用户操作类型常量 + * @return array + */ + function getUserAction() + { + return [ + 'LOGIN' => '登录', + 'LOGOUT' => '退出登录', + 'MODIFY_PASSWORD' => '修改密码', + 'GET_VERIFY_CODE' => '获取验证码', + 'UPDATE_PROFILE' => '更新个人信息', + 'REFRESH_TOKEN' => '刷新令牌', + 'API_REQUEST' => 'API请求', + 'FILE_UPLOAD' => '文件上传', + 'FILE_DOWNLOAD' => '文件下载', + 'DATA_EXPORT' => '数据导出', + 'DATA_IMPORT' => '数据导入', + 'CREATE' => '创建', + 'UPDATE' => '更新', + 'DELETE' => '删除', + 'QUERY' => '查询' + ]; + } +} \ No newline at end of file diff --git a/Server/application/common/model/CompanyAccountModel.php b/Server/application/common/model/CompanyAccountModel.php new file mode 100644 index 00000000..5f8395c0 --- /dev/null +++ b/Server/application/common/model/CompanyAccountModel.php @@ -0,0 +1,11 @@ +redirect('/admin/'); - } -} \ No newline at end of file diff --git a/Server/config/app.php b/Server/config/app.php index d8009b70..9478e75a 100755 --- a/Server/config/app.php +++ b/Server/config/app.php @@ -12,16 +12,16 @@ // +---------------------------------------------------------------------- // | 应用设置 // +---------------------------------------------------------------------- - +use think\facade\Env; return [ // 应用名称 'app_name' => '', // 应用地址 'app_host' => '', // 应用调试模式 - 'app_debug' => true, + 'app_debug' => Env::get('app.debug', false), // 应用Trace - 'app_trace' => false, + 'app_trace' => Env::get('app.trace', false), // 是否支持多模块 'app_multi_module' => true, // 入口自动绑定模块 diff --git a/Server/config/database.php b/Server/config/database.php index 971ddc3c..3cf0b2a4 100755 --- a/Server/config/database.php +++ b/Server/config/database.php @@ -9,19 +9,20 @@ // | Author: liu21st // +---------------------------------------------------------------------- +use think\facade\Env; return [ // 数据库类型 - 'type' => 'mysql', + 'type' => Env::get('database.type', 'mysql'), // 服务器地址 - 'hostname' => '127.0.0.1', + 'hostname' => Env::get('database.hostname', '127.0.0.1'), // 数据库名 - 'database' => 'yishi', + 'database' => Env::get('database.database', 'database'), // 用户名 - 'username' => 'yishi', + 'username' => Env::get('database.username', 'root'), // 密码 - 'password' => 'KcankSjjdZ5CsTC7', + 'password' => Env::get('database.password', 'root'), // 端口 - 'hostport' => '', + 'hostport' => Env::get('database.hostport', '3306'), // 连接dsn 'dsn' => '', // 数据库连接参数 @@ -29,7 +30,7 @@ return [ // 数据库编码默认采用utf8 'charset' => 'utf8mb4', // 数据库表前缀 - 'prefix' => 'tk_', + 'prefix' => Env::get('database.prefix', 'tk_'), // 数据库调试模式 'debug' => true, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)