28 changed files with 3421 additions and 0 deletions
@ -0,0 +1 @@ |
|||
deny from all |
|||
@ -0,0 +1,22 @@ |
|||
<?php |
|||
declare (strict_types = 1); |
|||
|
|||
namespace app; |
|||
|
|||
use think\Service; |
|||
|
|||
/** |
|||
* 应用服务类 |
|||
*/ |
|||
class AppService extends Service |
|||
{ |
|||
public function register() |
|||
{ |
|||
// 服务注册 |
|||
} |
|||
|
|||
public function boot() |
|||
{ |
|||
// 服务启动 |
|||
} |
|||
} |
|||
@ -0,0 +1,141 @@ |
|||
<?php |
|||
declare (strict_types = 1); |
|||
|
|||
namespace app; |
|||
|
|||
use think\App; |
|||
use think\exception\ValidateException; |
|||
use think\Validate; |
|||
use app\manage\model\{Config}; |
|||
use think\facade\Cache; |
|||
use thans\jwt\facade\JWTAuth; |
|||
/** |
|||
* 控制器基础类 |
|||
*/ |
|||
abstract class BaseController |
|||
{ |
|||
/** |
|||
* Request实例 |
|||
* @var \think\Request |
|||
*/ |
|||
protected $request; |
|||
|
|||
/** |
|||
* 应用实例 |
|||
* @var \think\App |
|||
*/ |
|||
protected $app; |
|||
|
|||
/** |
|||
* 是否批量验证 |
|||
* @var bool |
|||
*/ |
|||
protected $batchValidate = false; |
|||
|
|||
/** |
|||
* 控制器中间件 |
|||
* @var array |
|||
*/ |
|||
protected $middleware = []; |
|||
|
|||
/** |
|||
* 是否批量验证 |
|||
* @var bool |
|||
*/ |
|||
protected $userInfo = []; |
|||
|
|||
/** |
|||
* 接收的post数据 |
|||
* @var bool |
|||
*/ |
|||
protected $postData = []; |
|||
|
|||
protected $uid = 0; |
|||
|
|||
protected $globalConfig = []; |
|||
|
|||
protected $chatSetting = []; |
|||
|
|||
/** |
|||
* 构造方法 |
|||
* @access public |
|||
* @param App $app 应用对象 |
|||
*/ |
|||
public function __construct(App $app) |
|||
{ |
|||
$this->app = $app; |
|||
$this->request = $this->app->request; |
|||
// 控制器初始化 |
|||
$this->initialize(); |
|||
} |
|||
|
|||
// 初始化 |
|||
protected function initialize() |
|||
{ |
|||
$this->userInfo=$this->request->userInfo; |
|||
$this->uid=$this->userInfo['user_id'] ?? 0; |
|||
$config=Config::getSystemInfo(); |
|||
if($config){ |
|||
$this->globalConfig = $config; |
|||
$this->chatSetting = $config['chatInfo'] ?? []; |
|||
} |
|||
// 验证版本,如果不一致,就需要退出重新登陆 |
|||
$version =config('app.app_version'); |
|||
$oldVersion=Cache::get('app_version'); |
|||
if($version!=$oldVersion){ |
|||
Cache::set('app_version',$version); |
|||
JWTAuth::refresh(); |
|||
Cache::delete('systemInfo'); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证数据 |
|||
* @access protected |
|||
* @param array $data 数据 |
|||
* @param string|array $validate 验证器名或者验证规则数组 |
|||
* @param array $message 提示信息 |
|||
* @param bool $batch 是否批量验证 |
|||
* @return array|string|true |
|||
* @throws ValidateException |
|||
*/ |
|||
protected function validate(array $data, $validate, array $message = [], bool $batch = false) |
|||
{ |
|||
if (is_array($validate)) { |
|||
$v = new Validate(); |
|||
$v->rule($validate); |
|||
} else { |
|||
if (strpos($validate, '.')) { |
|||
// 支持场景 |
|||
[$validate, $scene] = explode('.', $validate); |
|||
} |
|||
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate); |
|||
$v = new $class(); |
|||
if (!empty($scene)) { |
|||
$v->scene($scene); |
|||
} |
|||
} |
|||
|
|||
$v->message($message); |
|||
|
|||
// 是否批量验证 |
|||
if ($batch || $this->batchValidate) { |
|||
$v->batch(true); |
|||
} |
|||
|
|||
return $v->failException(true)->check($data); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 自动获取前端传递的分页数量 |
|||
* @param \think\Model|\think\model\relation\HasMany $model |
|||
* @return \think\Paginator |
|||
*/ |
|||
protected function paginate($model) |
|||
{ |
|||
$limit = $this->request->param('limit', 20); |
|||
return $model->paginate($limit); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
<?php |
|||
/** |
|||
* Created by PhpStorm |
|||
* User xiekunyu@kaishanlaw.com |
|||
* Date 2021/7/9 16:15 |
|||
*/ |
|||
|
|||
namespace app; |
|||
|
|||
use think\facade\Db; |
|||
use think\Model; |
|||
|
|||
class BaseModel extends Model |
|||
{ |
|||
protected $defaultSoftDelete = 0; |
|||
protected $error = ''; |
|||
protected static $db_prefix = 'yu_'; |
|||
protected static $userInfo = null; |
|||
protected static $uid = null; |
|||
|
|||
|
|||
protected static function init() |
|||
{ |
|||
self::$db_prefix = config('database.connections.mysql.prefix') ?: "yu_"; |
|||
self::initModel(); |
|||
} |
|||
|
|||
// 加载模型自动处理 |
|||
public static function initModel() |
|||
{ |
|||
self::$userInfo=request()->userInfo ?? null; |
|||
self::$uid=request()->userInfo['user_id'] ?? null; |
|||
} |
|||
|
|||
/** |
|||
* 获取树状信息 |
|||
* @param array $config |
|||
*/ |
|||
public static function getCheckNode($arr, $pid, $field = "parent_id", $table = '') |
|||
{ |
|||
if (!$table) { |
|||
$res = self::find($pid); |
|||
} else { |
|||
$res = Db::name($table)->find($pid); |
|||
} |
|||
if ($res) { |
|||
if ($res[$field] > 0) { |
|||
array_unshift($arr, $res[$field]); |
|||
return self::getCheckNode($arr, $res[$field], $field, $table); |
|||
} |
|||
} |
|||
return $arr; |
|||
} |
|||
|
|||
// 获取错误信息 |
|||
public function getError() |
|||
{ |
|||
return $this->error; |
|||
} |
|||
|
|||
/** |
|||
* 获取模型的json字段数组 |
|||
* @return array |
|||
*/ |
|||
public function getJsonFieldName(): array |
|||
{ |
|||
return $this->json; |
|||
} |
|||
|
|||
// 匹配列表信息 |
|||
public static function filterIdr($data, $many, $field) |
|||
{ |
|||
if ($many) { |
|||
$idr = \utils\Arr::arrayToString($data, $field, false); |
|||
} else { |
|||
$idr = []; |
|||
if (is_array($field)) { |
|||
foreach ($field as $v) { |
|||
$idr[] = $data[$v]; |
|||
} |
|||
} else { |
|||
$idr = [$data[$field]]; |
|||
} |
|||
} |
|||
$key = array_search(0, $idr); |
|||
if ($key) { |
|||
array_splice($idr, $key, 1); |
|||
} |
|||
$idr = array_unique($idr); |
|||
|
|||
return $idr ? : []; |
|||
} |
|||
|
|||
// 获取某一项数据的统计 |
|||
public static function getTotal($map,$where=[],$field,$group){ |
|||
return self::field($field) |
|||
->where($map) |
|||
->where($where) |
|||
->group($group) |
|||
->select()->toArray(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
<?php |
|||
namespace app; |
|||
|
|||
use think\db\exception\DataNotFoundException; |
|||
use think\db\exception\ModelNotFoundException; |
|||
use think\exception\Handle; |
|||
use think\exception\HttpException; |
|||
use think\exception\HttpResponseException; |
|||
use think\exception\ValidateException; |
|||
use think\Response; |
|||
use Throwable; |
|||
|
|||
/** |
|||
* 应用异常处理类 |
|||
*/ |
|||
class ExceptionHandle extends Handle |
|||
{ |
|||
/** |
|||
* 不需要记录信息(日志)的异常类列表 |
|||
* @var array |
|||
*/ |
|||
protected $ignoreReport = [ |
|||
HttpException::class, |
|||
HttpResponseException::class, |
|||
ModelNotFoundException::class, |
|||
DataNotFoundException::class, |
|||
ValidateException::class, |
|||
]; |
|||
|
|||
/** |
|||
* 记录异常信息(包括日志或者其它方式记录) |
|||
* |
|||
* @access public |
|||
* @param Throwable $exception |
|||
* @return void |
|||
*/ |
|||
public function report(Throwable $exception): void |
|||
{ |
|||
// 使用内置的方式记录异常日志 |
|||
parent::report($exception); |
|||
} |
|||
|
|||
/** |
|||
* Render an exception into an HTTP response. |
|||
* |
|||
* @access public |
|||
* @param \think\Request $request |
|||
* @param Throwable $e |
|||
* @return Response |
|||
*/ |
|||
public function render($request, Throwable $e): Response |
|||
{ |
|||
// 添加自定义异常处理机制 |
|||
|
|||
// 其他错误交给系统处理 |
|||
return parent::render($request, $e); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
<?php |
|||
namespace app; |
|||
|
|||
// 应用请求对象类 |
|||
class Request extends \think\Request |
|||
{ |
|||
|
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,20 @@ |
|||
<?php |
|||
// 事件定义文件 |
|||
return [ |
|||
'bind' => [ |
|||
], |
|||
|
|||
'listen' => [ |
|||
'AppInit' => [], |
|||
'HttpRun' => [], |
|||
'HttpEnd' => [], |
|||
'LogLevel' => [], |
|||
'LogWrite' => [], |
|||
'UserRegister'=>['app\common\listener\UserRegister'], |
|||
'GroupChange'=>['app\enterprise\listener\GroupChange'], |
|||
'GreenText'=>['app\common\listener\GreenText'], |
|||
], |
|||
|
|||
'subscribe' => [ |
|||
], |
|||
]; |
|||
@ -0,0 +1,62 @@ |
|||
<?php |
|||
return [ |
|||
// Friend |
|||
'cannot_add_self_as_friend' => 'Cannot add yourself as a friend', |
|||
'you_are_already_friends' => 'You are already friends', |
|||
'you_have_already_applied_please_wait' => 'You have already applied, please wait for their approval', |
|||
'new_friend' => 'New Friend', |
|||
'has_added_you_as_a_friend' => 'has added you as a friend', |
|||
'added_successfully' => 'Added successfully', |
|||
'request_does_not_exist' => 'Request does not exist', |
|||
'operation_successful' => 'Operation successful', |
|||
'friend_does_not_exist' => 'Friend does not exist', |
|||
'deleted_successfully' => 'Deleted successfully', |
|||
'note_cannot_be_empty' => 'Note cannot be empty', |
|||
'settings_updated_successfully' => 'Settings updated successfully', |
|||
|
|||
// Group |
|||
'you_do_not_have_permission_only_owners_and_admins_can_modify' => 'You do not have permission, only group owners and administrators can modify!', |
|||
'modified_successfully' => 'Modified successfully', |
|||
'member_limit_exceeded' => 'Member limit exceeded', |
|||
'you_do_not_have_permission' => 'You do not have permission!', |
|||
'settings_failed' => 'Settings failed!', |
|||
'you_do_not_have_permission_to_create_group' => 'You do not have permission to create a group!', |
|||
'please_select_at_least_two_members' => 'Please select at least two members!', |
|||
'group_created' => 'Group created', |
|||
'your_permission_is_not_sufficient' => 'Your permission is not sufficient!', |
|||
'deleted_successfully_group' => 'Deleted successfully', |
|||
'please_enter_content' => 'Please enter content!', |
|||
'you_are_already_in_this_group' => 'You are already in this group!', |
|||
'joined_successfully' => 'Joined successfully', |
|||
'group_does_not_exist' => 'Group does not exist', |
|||
'user_does_not_exist' => 'User does not exist', |
|||
'transferred_successfully' => 'Transferred successfully', |
|||
'change_failed' => 'Change failed', |
|||
|
|||
// IM |
|||
'private_chat_is_currently_disabled' => 'Private chat is currently disabled!', |
|||
'you_are_not_on_their_friend_list_cannot_send_message' => 'You are not on their friend list, cannot send a message!', |
|||
'they_are_not_your_friend_cannot_send_message' => 'They are not your friend, cannot send a message!', |
|||
'send_failed' => 'Send failed', |
|||
'please_select_users_or_quantity_not_exceeding_10' => 'Please select users or quantity not exceeding 10!', |
|||
'message_does_not_exist' => 'Message does not exist', |
|||
'forwarding_failed_due_to_rules' => 'Forwarding failed due to rules', |
|||
'forwarded_successfully' => 'Forwarded successfully', |
|||
'you' => 'You', |
|||
'other' => 'Other', |
|||
'cannot_withdraw_after_2_minutes' => 'Cannot withdraw after 2 minutes!', |
|||
'withdrawn_a_message' => 'Withdrawn a message', |
|||
'you_do_not_have_permission_to_withdraw_this_message' => 'You do not have permission to withdraw this message', |
|||
'a_message_was_withdrawn_by_an_admin' => 'A message was withdrawn by an admin', |
|||
'deleted_successfully_im' => 'Deleted successfully', |
|||
'call_cancelled' => 'Call cancelled', |
|||
'declined' => 'Declined', |
|||
'not_connected' => 'Not connected', |
|||
'call_duration' => 'Call duration', |
|||
'busy' => 'Busy', |
|||
'operation_performed_on_another_device' => 'Operation performed on another device', |
|||
'video_call' => 'Video call', |
|||
'audio_call' => 'Audio call', |
|||
'answer_call_request' => 'Answer call request', |
|||
'data_exchange_in_progress' => 'Data exchange in progress' |
|||
]; |
|||
@ -0,0 +1,156 @@ |
|||
<?php |
|||
return [ |
|||
'system' => [ |
|||
'success' => 'Operation successful', |
|||
'fail' => 'Operation failed', |
|||
'error' => 'System error', |
|||
'forbidden' => 'Access forbidden', |
|||
'exist' => 'The record does not exist', |
|||
'sendOK' => 'Sent successfully', |
|||
'sendFail' => 'Sending failed', |
|||
'delOk' => 'Deletion successful', |
|||
'settingOk' => 'Settings updated successfully', |
|||
'notNull' => 'Cannot be empty', |
|||
'editOk' => 'Edit successful', |
|||
'editFail' => 'Edit failed', |
|||
'addOk' => 'Addition successful', |
|||
// The original 'addFail' seems to be a typo, assuming it should be '添加失败' which translates to 'Addition failed' |
|||
'addFail' => 'Addition failed', |
|||
'joinOk' => 'Joining successful', |
|||
'notAuth' => 'You do not have the permission to perform this operation!', |
|||
'demoMode' => 'Modifications are not supported in demo mode', |
|||
'parameterError' => 'Parameter error', |
|||
'longTime' => 'Request timeout', |
|||
'apiClose' => 'API is closed', |
|||
'appIdError' => 'appId error', |
|||
'signError' => 'Signature error', |
|||
'tooFast'=>"You visited too fast!" |
|||
], |
|||
'messageType' => [ |
|||
'other' => "[Unsupported message type]", |
|||
'image' => "[Image]", |
|||
'emoji' => "[DIYemoji]", |
|||
'voice' => "[Voice]", |
|||
'video' => "[Video]", |
|||
'file' => "[File]", |
|||
'webrtcAudio' => "[Audio call request with you]", |
|||
'webrtcVideo' => "[Video call request with you]", |
|||
], |
|||
'friend' => [ |
|||
'notAddOwn' => "You cannot add yourself as a friend", |
|||
'already' => "You are already friends", |
|||
'repeatApply' => "You have already sent a request, please wait for the other person to accept", |
|||
'new' => "New friend", |
|||
'apply' => "Has added you as a friend", |
|||
'notApply' => "The request does not exist", |
|||
'not' => "Friend does not exist", |
|||
'newChat' => "You have been successfully added as friends, let's start chatting now! ", |
|||
'limit' => "Your friends have reached the limit! ", |
|||
], |
|||
'group' => [ |
|||
'name' => "Group chat", |
|||
'notAuth' => "You do not have permission to perform this action. Only the group owner and administrators can make changes!", |
|||
'userLimit' => "The number of members cannot exceed {:userMax} people!", |
|||
'inviteLimit'=>'The number of people invited at a single time cannot exceed {:limit}! ', |
|||
'invite' => "{:username} has invited you to join the group chat", |
|||
'removeUser'=>"You have been removed from Group chats! ", |
|||
'notCustom'=>"You are not a member of this group and have no right to send messages!", |
|||
'add' => "{:username} has created a group chat", |
|||
'join'=>"{:username} join the group chat", |
|||
'atLeast' => "Please select at least two people!", |
|||
'alreadyJoin' => "You are already in this group!", |
|||
'exist' => "The group chat does not exist", |
|||
'notice'=>"Announcement", |
|||
'all'=>"All", |
|||
'noSpeak'=>"You have been banned from speaking, recovery time is {:time}", |
|||
'notSpeak'=>"Group chat has been banned!", |
|||
'limit'=>"You have been restricted from creating Group chats! ", |
|||
], |
|||
'user' => [ |
|||
'exist' => "User does not exist", |
|||
'codeErr' => "Verification code is incorrect!", |
|||
'newCodeErr' => "New verification code is incorrect!", |
|||
'passErr' => "Original password is incorrect!", |
|||
'already' => "Account already exists", |
|||
'registerOk' => "Registration successful", |
|||
'loginOk' => "Login successful", |
|||
'tokenFailure' => "TOKEN has expired!", |
|||
'forbid' => "Your account has been disabled", |
|||
'passError' => "Password is incorrect", |
|||
'logoutOk' => "Logout successful!", |
|||
'closeRegister' => "The system has disabled registration!", |
|||
'inviteCode' => "Invite code has expired!", |
|||
'accountVerify' => "Account must be a phone number or email", |
|||
'waitMinute' => "Please try again after one minute!", |
|||
"loginAccount" => "Login account", |
|||
"registerAccount" => "Register account", |
|||
"editPass" => "Change password", |
|||
"editAccount" => "Edit account", |
|||
'loginError' => 'Login information is incorrect. Please log in again.', |
|||
'mustToken' => 'Please log in to the system first', |
|||
'blacklist' => 'Login has expired. Please log in again', |
|||
'expired' => 'Login has expired. Please log in again', |
|||
'notOwn' =>"Customer service can't be for him", |
|||
'loginLimit' =>"Your password has been wrong too many times. Please try again later! ", |
|||
'registerLimit'=>"Please register again in {:time} minutes" |
|||
], |
|||
'im' => [ |
|||
'forbidChat' => "Private chatting is currently prohibited!", |
|||
'notFriend' => "You are not on their friend list, cannot send messages!", |
|||
'friendNot' => "They are not your friend, cannot send messages!", |
|||
'forwardLimit' => "Please select fewer than {:count} recipients for forwarding!", |
|||
'exist' => "Message does not exist", |
|||
'forwardRule' => "Forwarding failed for {:count} messages due to rule restrictions!", |
|||
'forwardOk' => 'Message forwarded successfully', |
|||
'you' => 'You', |
|||
'other' => 'Recipient', |
|||
'redoLimitTime' => "Cannot recall messages after {:time} minutes!", |
|||
'redo' => "A message has been recalled", |
|||
'manageRedo' => "A message has been recalled by (an admin)", |
|||
'msgContentLimit' => "Message content too long", |
|||
'sendTimeLimit'=>'The message sending interval is {:time} seconds!', |
|||
'forbidMsg'=>'[The message is suspected of violation and has been blocked]', |
|||
], |
|||
'webRtc' => [ |
|||
'cancel' => 'Call has been canceled', |
|||
'refuse' => 'Call has been rejected', |
|||
'notConnected' => 'Call not connected', |
|||
'duration' => 'Call duration: {:time}', |
|||
'busy' => 'Busy', |
|||
'other' => 'Operation performed on another device', |
|||
'video' => 'Video call', |
|||
'audio' => 'Audio call', |
|||
'answer' => 'Answer call request', |
|||
'exchange' => 'Data exchange in progress', |
|||
'fail' => 'Call failed', |
|||
], |
|||
'email' => [ |
|||
'input' => 'Please enter a valid email address', |
|||
'testTitle' => "Test Email", |
|||
'testContent' => "This is a test email. If you receive it, it means all your configurations are correct!", |
|||
], |
|||
'task' => [ |
|||
'schedule' => 'Scheduled Task', |
|||
'queue' => 'Message Queue', |
|||
'worker' => 'Message Push', |
|||
'clearStd' => 'Clear Logs', |
|||
'null' => "Unknown task", |
|||
'winRun' => "To start on Windows, please run the 'start_for_win.bat' file in the root directory", |
|||
'alreadyRun' => "Process is already running", |
|||
'startOk' => "Started successfully", |
|||
'startFail' => "Failed to start", |
|||
'notRun' => "Process is not running", |
|||
'logExist' => "Log does not exist", |
|||
], |
|||
'file' => [ |
|||
'preview' => "Preview file", |
|||
'browserDown' => "Please use the browser to download", |
|||
'exist' => "The file does not exist", // Note: This might be a duplicate of 'preview' and could be replaced with a more specific message |
|||
'uploadLimit' => "File size cannot exceed {:size}MB", |
|||
'typeNotSupport' => "File format is not supported", |
|||
'uploadOk' => "Upload successful", |
|||
], |
|||
'scan' => [ |
|||
'failure' => 'QR code has expired' |
|||
] |
|||
]; |
|||
@ -0,0 +1,155 @@ |
|||
<?php |
|||
return [ |
|||
'system'=>[ |
|||
'success'=>'操作成功', |
|||
'fail'=>'操作失败', |
|||
'error'=>'系统错误', |
|||
'forbidden'=>"禁止访问", |
|||
'exist'=>"记录不存在", |
|||
'sendOK'=>"发送成功", |
|||
'sendFail'=>"发送失败", |
|||
'delOk'=>"删除成功", |
|||
'settingOk'=>"设置成功", |
|||
'notNull'=>"不能为空", |
|||
'editOk'=>'修改成功', |
|||
'editFail'=>'修改失败', |
|||
'addOk'=>'添加成功', |
|||
'addFail'=>'添加成功', |
|||
'joinOk'=>'加入成功', |
|||
'notAuth'=>"您没有操作权限!", |
|||
'demoMode'=>"演示模式不支持修改", |
|||
'parameterError'=>"参数错误", |
|||
'longTime'=>'请求超时', |
|||
'apiClose'=>"接口已关闭", |
|||
'appIdError'=>'appId错误', |
|||
'signError'=>'签名错误', |
|||
'toofast'=>"您访问的太快了!" |
|||
], |
|||
'messageType'=>[ |
|||
'other'=>"[暂不支持的消息类型]", |
|||
'image'=>'[图片]', |
|||
'voice'=>'[语音]', |
|||
'emoji'=>'[自定义表情]', |
|||
'video'=>'[视频]', |
|||
'file'=>'[文件]', |
|||
'webrtcAudio'=>'[正在请求与您语音通话]', |
|||
'webrtcVideo'=>'[正在请求与您视频通话]', |
|||
], |
|||
'friend'=>[ |
|||
'notAddOwn'=>"不能添加自己为好友", |
|||
'already'=>"你们已经是好友了", |
|||
'repeatApply'=>"你已经申请过了,请等待对方同意", |
|||
'new'=>"新朋友", |
|||
"apply"=>"添加您为好友", |
|||
'notApply'=>"申请不存在", |
|||
'not'=>"好友不存在", |
|||
'newChat'=>"你们已经成功添加为好友,现在开始聊天吧!", |
|||
'limit'=>"您的好友已达上限!", |
|||
], |
|||
'group'=>[ |
|||
'name'=>"群聊", |
|||
'notAuth'=>'你没有操作权限,只有群主和群管理员才可以修改!', |
|||
'userLimit'=>'人数不能超过{:userMax}人!', |
|||
'inviteLimit'=>'单次邀请人数不能超过{:limit}人!', |
|||
'invite'=>"{:username}邀请你加入群聊", |
|||
'removeUser'=>"您已被移出群聊!", |
|||
'notCustom'=>"您不是本群成员,无权发送消息!", |
|||
'add'=>"{:username}创建了群聊", |
|||
'join'=>"{:username}加入了群聊", |
|||
'atLeast'=>"请至少选择两人!", |
|||
'alreadyJoin'=>'您已经加入该群!', |
|||
'exist'=>"群聊不存在", |
|||
'notice'=>"群公告", |
|||
'all'=>"所有人", |
|||
'noSpeak'=>"您已被禁言,恢复时间为:{:time}", |
|||
'notSpeak'=>"群聊已禁言!", |
|||
'limit'=>"您已被限制创建群聊!", |
|||
], |
|||
'user'=>[ |
|||
'exist'=>"用户不存在", |
|||
'codeErr'=>'验证码不正确!', |
|||
'newCodeErr'=>'新验证码不正确!', |
|||
'passErr'=>"原密码不正确!", |
|||
'already'=>"账户已存在", |
|||
'registerOk'=>"注册成功", |
|||
'loginOk'=>"登陆成功", |
|||
'tokenFailure'=>"TOKEN已失效!", |
|||
'forbid'=>'您的账号已被禁用', |
|||
'passError'=>'密码错误', |
|||
'logoutOk'=>'退出成功!', |
|||
'closeRegister'=>'当前系统已关闭注册功能!', |
|||
'inviteCode'=>'邀请码已失效!', |
|||
'accountVerify'=>'账户必须为手机号或者邮箱', |
|||
'waitMinute'=>"请一分钟后再试!", |
|||
"loginAccount"=>"登录账户", |
|||
"registerAccount"=>"注册账户", |
|||
"editPass"=>"修改密码", |
|||
"editAccount"=>"修改账户", |
|||
'loginError' => '登陆信息有误 请重新登录', |
|||
'mustToken' => '请先登陆系统', |
|||
'blacklist' => '登陆已失效 请重新登陆', |
|||
'expired' => '登陆已过期 请重新登陆', |
|||
'notOwn' =>"客服不能为他本人", |
|||
'loginLimit' =>"您的密码错误次数过多,请稍后再试!", |
|||
'registerLimit'=>"请{:time}分钟后再注册!", |
|||
], |
|||
'im'=>[ |
|||
'forbidChat'=>"目前禁止用户私聊!", |
|||
'notFriend'=>"您不在TA的好友列表,不能发消息!", |
|||
'friendNot'=>"TA还不是您的好友,不能发消息!", |
|||
'forwardLimit'=>"请选择转发的用户或者数量不操作{:count}个!", |
|||
'exist'=>"消息不存在", |
|||
'forwardRule'=>"由于规则限制,转发失败{:count}条!", |
|||
'forwardOk'=>'转发成功', |
|||
'you'=>'你', |
|||
'other'=>'对方', |
|||
'redoLimitTime'=>"超过{:time}分钟不能撤回!", |
|||
'redo'=>"撤回了一条消息", |
|||
'manageRedo'=>'被(管理员)撤回了一条消息', |
|||
'msgContentLimit'=>'你发送的消息长度太长了!', |
|||
'sendTimeLimit'=>'消息发送时间间隔为 {:time} 秒!', |
|||
'forbidMsg'=>'[该消息涉嫌违规,已被屏蔽]', |
|||
], |
|||
'webRtc'=>[ |
|||
'cancel'=>'已取消通话', |
|||
'refuse'=>'已拒绝', |
|||
'notConnected'=>'未接通', |
|||
'duration'=>'通话时长:{:time}', |
|||
'busy'=>'忙线中', |
|||
'other'=>'其他端已操作', |
|||
'video'=>'视频通话', |
|||
'audio'=>'语音通话', |
|||
'answer'=>'接听通话请求', |
|||
'exchange'=>'数据交换中', |
|||
'fail'=>'通话失败', |
|||
], |
|||
'email'=>[ |
|||
'input'=>'请输入正确的邮箱', |
|||
'testTitle'=>"测试邮件", |
|||
'testContent'=>'这是一封测试邮件,当您收到之后表明您的所有配置都是正确的!', |
|||
], |
|||
'task'=>[ |
|||
'schedule' => '计划任务', |
|||
'queue' => '消息队列', |
|||
'worker' => '消息推送', |
|||
'clearStd' => '清理日志', |
|||
'null'=>"未知任务", |
|||
'winRun'=>"windows启动请运行根目录下的:start_for_win.bat", |
|||
'alreadyRun'=>"进程已启动", |
|||
'startOk'=>"启动成功", |
|||
'startFail'=>"启动失败", |
|||
'notRun'=>"进程未启动", |
|||
'logExist'=>"日志不存在", |
|||
], |
|||
'file'=>[ |
|||
'preview'=>"预览文件", |
|||
'browserDown'=>"请使用浏览器下载", |
|||
'exist'=>"文件不存在", |
|||
'uploadLimit'=>"文件大小不能超过{:size}MB", |
|||
'typeNotSupport'=>"文件格式不支持", |
|||
'uploadOk'=>"上传成功" |
|||
], |
|||
'scan'=>[ |
|||
'failure'=>'二维码已失效' |
|||
] |
|||
]; |
|||
@ -0,0 +1,104 @@ |
|||
<?php |
|||
/** |
|||
* Created by PhpStorm |
|||
* User Julyssn |
|||
* Date 2022/12/14 17:24 |
|||
*/ |
|||
|
|||
|
|||
namespace app\manage\controller; |
|||
|
|||
|
|||
use app\BaseController; |
|||
use app\manage\model\{Config as Conf}; |
|||
use think\facade\Cache; |
|||
class Config extends BaseController |
|||
{ |
|||
/** |
|||
* 获取单个配置 |
|||
* @return \think\response\Json |
|||
*/ |
|||
public function getInfo() |
|||
{ |
|||
$name=$this->request->param('name'); |
|||
$data = Conf::where(['name'=>$name])->value('value'); |
|||
return success('', $data); |
|||
} |
|||
|
|||
/** |
|||
* 获取配置 |
|||
* @return \think\response\Json |
|||
*/ |
|||
public function getAllConfig() |
|||
{ |
|||
$name=['sysInfo','chatInfo','smtp','fileUpload','compass']; |
|||
$list = Conf::where(['name'=>$name])->select(); |
|||
return success('', $list); |
|||
} |
|||
|
|||
/** |
|||
* 修改配置 |
|||
* @return \think\response\Json |
|||
*/ |
|||
public function setConfig() |
|||
{ |
|||
$name = $this->request->param('name'); |
|||
$value = $this->request->param('value'); |
|||
if(Conf::where(['name'=>$name])->find()){ |
|||
Conf::where(['name'=>$name])->update(['value'=>$value]); |
|||
}else{ |
|||
Conf::create(['name'=>$name,'value'=>$value]); |
|||
} |
|||
if($name=='fileUpload'){ |
|||
updateEnv('driver',$value['disk']); |
|||
updateEnv('own',$value['preview']); |
|||
foreach ($value['aliyun'] as $k=>$v){ |
|||
if($v){ |
|||
updateEnv('aliyun_'.$k,$v); |
|||
} |
|||
} |
|||
foreach ($value['qiniu'] as $k=>$v){ |
|||
if($v){ |
|||
updateEnv('qiniu_'.$k,$v); |
|||
} |
|||
} |
|||
foreach ($value['qcloud'] as $k=>$v){ |
|||
if($v){ |
|||
updateEnv('qcloud_'.$k,$v); |
|||
} |
|||
} |
|||
}else{ |
|||
// 更新系统缓存 |
|||
$systemInfo=Conf::getSystemInfo(true); |
|||
// 向所有人推送新的设置 |
|||
wsSendMsg(0,'updateConfig',$systemInfo); |
|||
} |
|||
return success(lang('system.editOk')); |
|||
} |
|||
|
|||
/** |
|||
* 获取邀请链接 |
|||
* @return \think\response\Json |
|||
*/ |
|||
public function getInviteLink(){ |
|||
$uid=$this->userInfo['user_id']; |
|||
// 邀请码仅两天有效 |
|||
$code=\utils\Str::random(8); |
|||
Cache::set($code,$uid,172800); |
|||
$url=getMainHost().'/index.html/#/register?inviteCode='.$code; |
|||
return success('',$url); |
|||
} |
|||
|
|||
// 发送测试邮件 |
|||
public function sendTestEmail(){ |
|||
$email=$this->request->param('email'); |
|||
if(!$email || !(\utils\Regular::is_email($email))){ |
|||
return warning(lang('email.input')); |
|||
} |
|||
$conf=Conf::where(['name'=>'smtp'])->value('value'); |
|||
$mail=new \mail\Mail($conf); |
|||
$mail->sendEmail([$email],lang('email.testTitle'),lang('email.testContent')); |
|||
return success(lang('system.sendOk')); |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,166 @@ |
|||
<?php |
|||
/** |
|||
* Created by PhpStorm |
|||
* User raingad@foxmail.com |
|||
* Date 2022/12/14 17:24 |
|||
*/ |
|||
namespace app\manage\controller; |
|||
use app\BaseController; |
|||
use app\enterprise\model\{User as UserModel,GroupUser,Group as GroupModel}; |
|||
use think\facade\Db; |
|||
|
|||
class Group extends BaseController |
|||
{ |
|||
// 获取群聊列表 |
|||
public function index() |
|||
{ |
|||
$map = []; |
|||
$model=new GroupModel(); |
|||
$param = $this->request->param(); |
|||
//搜索关键词 |
|||
if ($keyword = $this->request->param('keywords')) { |
|||
$model = $model->whereLike('name|name_py', '%' . $keyword . '%'); |
|||
} |
|||
// 排序 |
|||
$order='group_id DESC'; |
|||
if ($param['order_field'] ?? '') { |
|||
$order = orderBy($param['order_field'],$param['order_type'] ?? 1); |
|||
} |
|||
$list = $this->paginate($model->where($map)->order($order)); |
|||
if ($list) { |
|||
$data = $list->toArray()['data']; |
|||
$userList=UserModel::matchUser($data,true,'owner_id',120); |
|||
foreach($data as $k=>$v){ |
|||
$data[$k]['avatar']=avatarUrl($v['avatar'],$v['name'],$v['group_id'],120); |
|||
$data[$k]['owner_id_info']=$userList[$v['owner_id']] ?? []; |
|||
} |
|||
} |
|||
return success('', $data, $list->total(), $list->currentPage()); |
|||
} |
|||
|
|||
// 更换群主 |
|||
public function changeOwner() |
|||
{ |
|||
$group_id = $this->request->param('group_id'); |
|||
$user_id = $this->request->param('user_id'); |
|||
$group=GroupModel::where('group_id',$group_id)->find(); |
|||
if(!$group){ |
|||
return warning(lang('group.exist')); |
|||
} |
|||
$user=UserModel::where('user_id',$user_id)->find(); |
|||
if(!$user){ |
|||
return warning(lang('user.exist')); |
|||
} |
|||
Db::startTrans(); |
|||
try{ |
|||
GroupUser::where('group_id',$group_id)->where('user_id',$user_id)->update(['role'=>1]); |
|||
GroupUser::where('group_id',$group_id)->where('user_id',$group->owner_id)->update(['role'=>3]); |
|||
$group->owner_id=$user_id; |
|||
$group->save(); |
|||
wsSendMsg($group_id,"changeOwner",['group_id'=>'group-'.$group_id,'user_id'=>$user_id],1); |
|||
Db::commit(); |
|||
return success(''); |
|||
}catch (\Exception $e){ |
|||
Db::rollback(); |
|||
return warning(''); |
|||
} |
|||
} |
|||
|
|||
// 解散群聊 |
|||
public function del() |
|||
{ |
|||
$group_id = $this->request->param('group_id'); |
|||
$group=GroupModel::where('group_id',$group_id)->find(); |
|||
if(!$group){ |
|||
return warning(lang('group.exist')); |
|||
} |
|||
Db::startTrans(); |
|||
try{ |
|||
// 删除团队成员 |
|||
GroupUser::where('group_id',$group_id)->delete(); |
|||
// 删除团队 |
|||
GroupModel::destroy($group_id); |
|||
wsSendMsg($group_id,"removeGroup",['group_id'=>'group-'.$group_id],1); |
|||
Db::commit(); |
|||
return success(''); |
|||
}catch (\Exception $e){ |
|||
Db::rollback(); |
|||
return warning(''); |
|||
} |
|||
} |
|||
|
|||
// 添加群成员 |
|||
public function addGroupUser(){ |
|||
$param = $this->request->param(); |
|||
$uid=$this->userInfo['user_id']; |
|||
$group_id = $param['group_id']; |
|||
$group=GroupModel::where('group_id',$group_id)->find(); |
|||
if(!$group){ |
|||
return warning(lang('group.exist')); |
|||
} |
|||
$user_ids=$param['user_ids']; |
|||
$data=[]; |
|||
try{ |
|||
foreach($user_ids as $k=>$v){ |
|||
$data[]=[ |
|||
'group_id'=>$group_id, |
|||
'user_id'=>$v, |
|||
'role'=>3, |
|||
'invite_id'=>$uid |
|||
]; |
|||
} |
|||
$groupUser=new GroupUser; |
|||
$groupUser->saveAll($data); |
|||
$url=GroupModel::setGroupAvatar($group_id); |
|||
wsSendMsg($group_id,"addGroupUser",['group_id'=>"group-".$group_id,'avatar'=>$url],1); |
|||
return success(lang('system.addOk')); |
|||
}catch(\Exception $e){ |
|||
return error($e->getMessage()); |
|||
} |
|||
|
|||
} |
|||
|
|||
// 删除群成员 |
|||
public function delGroupUser(){ |
|||
$param = $this->request->param(); |
|||
$group_id = $param['group_id']; |
|||
$group=GroupModel::where('group_id',$group_id)->find(); |
|||
if(!$group){ |
|||
return warning(lang('group.exist')); |
|||
} |
|||
$user_id=$param['user_id']; |
|||
$groupUser=GroupUser::where(['group_id'=>$group_id,'user_id'=>$user_id])->find(); |
|||
if($groupUser){ |
|||
$groupUser->delete(); |
|||
wsSendMsg($group_id,"removeUser",['group_id'=>'group-'.$group_id],1); |
|||
return success(''); |
|||
}else{ |
|||
return warning(''); |
|||
} |
|||
|
|||
} |
|||
|
|||
// 设置管理员 |
|||
public function setManager(){ |
|||
$param = $this->request->param(); |
|||
$group_id = $param['group_id']; |
|||
$group=GroupModel::where('group_id',$group_id)->find(); |
|||
if(!$group){ |
|||
return warning(lang('group.exist')); |
|||
} |
|||
$user_id=$param['user_id']; |
|||
$role=$param['role']; |
|||
$groupUser=GroupUser::where(['group_id'=>$group_id,'user_id'=>$user_id])->find(); |
|||
if($groupUser){ |
|||
$groupUser->role=$role; |
|||
$groupUser->save(); |
|||
wsSendMsg($group_id,"setManager",['group_id'=>'group-'.$group_id],1); |
|||
return success(''); |
|||
}else{ |
|||
return warning(''); |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,107 @@ |
|||
<?php |
|||
/** |
|||
* Created by PhpStorm |
|||
* User Julyssn |
|||
* Date 2022/12/14 17:24 |
|||
*/ |
|||
|
|||
|
|||
namespace app\manage\controller; |
|||
|
|||
|
|||
use app\BaseController; |
|||
use app\enterprise\model\{Message}; |
|||
use think\facade\Cache; |
|||
class Index extends BaseController |
|||
{ |
|||
// 超级管理员专属功能 |
|||
|
|||
// 清理消息 |
|||
public function clearMessage(){ |
|||
if($this->userInfo['user_id']!=1){ |
|||
return warning('system.noAuth'); |
|||
} |
|||
Message::where(['status'=>1])->delete(); |
|||
return success('system.clearOk'); |
|||
} |
|||
|
|||
// 公告列表 |
|||
public function noticeList(){ |
|||
$model=new Message(); |
|||
// 排序 |
|||
$order='msg_id DESC'; |
|||
$map=['chat_identify'=>"admin_notice"]; |
|||
$list = $this->paginate($model->where($map)->order($order)); |
|||
if ($list) { |
|||
$data = $list->toArray()['data']; |
|||
foreach($data as $k=>$v){ |
|||
$data[$k]['title']=$v['extends']['title']; |
|||
} |
|||
} |
|||
return success('', $data, $list->total(), $list->currentPage()); |
|||
} |
|||
|
|||
// 删除公告 |
|||
public function delNotice(){ |
|||
$param=$this->request->param(); |
|||
$msgId=$param['id'] ?:0; |
|||
$map=['msg_id'=>$msgId]; |
|||
$message=Message::where($map)->find(); |
|||
if($message){ |
|||
Message::where($map)->delete(); |
|||
} |
|||
return success(''); |
|||
} |
|||
|
|||
// 发布公告 |
|||
public function publishNotice(){ |
|||
$userInfo=$this->userInfo; |
|||
if($userInfo['user_id']!=1){ |
|||
return warning('system.noAuth'); |
|||
} |
|||
$param=$this->request->param(); |
|||
$msgId=$param['msgId'] ?? 0; |
|||
$content="<h4>".$param['title']."</h4><br><p>".$param['content']."</p>"; |
|||
$data=[ |
|||
'from_user'=>$userInfo['user_id'], |
|||
'to_user'=>0, |
|||
'content'=>str_encipher($content,true), |
|||
'chat_identify'=>'admin_notice', |
|||
'create_time'=>time(), |
|||
'type'=>'text', |
|||
'is_group'=>2, |
|||
'is_read'=>1, |
|||
'is_top'=>0, |
|||
'is_notice'=>1, |
|||
'at'=>[], |
|||
'pid'=>0, |
|||
'extends'=>['title'=>$param['title'],'notice'=>$param['content']], |
|||
]; |
|||
if($msgId){ |
|||
Message::where(['msg_id'=>$msgId])->update([ |
|||
'content'=>$data['content'], |
|||
'extends'=>$data['extends'], |
|||
]); |
|||
}else{ |
|||
$data['id']=\utils\Str::getUuid(); |
|||
$message=new Message(); |
|||
$message->save($data); |
|||
$msgId=$message->msg_id; |
|||
} |
|||
$msgInfo=$data; |
|||
$msgInfo['status']='successd'; |
|||
$msgInfo['msg_id']=$msgId; |
|||
$msgInfo['user_id']=$userInfo['user_id']; |
|||
$msgInfo['sendTime']=time()*1000; |
|||
$msgInfo['toContactId']='admin_notice'; |
|||
$msgInfo['to_user']='admin_notice'; |
|||
$msgInfo['content']=$param['title']; |
|||
$msgInfo['fromUser']=[ |
|||
'id'=>$userInfo['user_id'], |
|||
'avatar'=>avatarUrl($userInfo['avatar'],$userInfo['realname'],$userInfo['user_id'],120), |
|||
'displayName'=>$userInfo['realname'] |
|||
]; |
|||
wsSendMsg(0,'simple',$msgInfo,1); |
|||
return success(''); |
|||
} |
|||
} |
|||
@ -0,0 +1,209 @@ |
|||
<?php |
|||
/** |
|||
* User raingad |
|||
* Date 2024/11/17 17:24 |
|||
*/ |
|||
|
|||
|
|||
namespace app\manage\controller; |
|||
|
|||
|
|||
use app\BaseController; |
|||
use app\enterprise\model\{Message as MessageModel,User,Friend,Group}; |
|||
use think\facade\Db; |
|||
class Message extends BaseController |
|||
{ |
|||
protected $fileType = ['file', 'image','video','voice']; |
|||
// 获取聊天记录 |
|||
public function index() |
|||
{ |
|||
$param = $this->request->param(); |
|||
$user_id=$param['user_id'] ?? 0; |
|||
$toContactId=$param['toContactId'] ?? 0; |
|||
$is_group=($param['is_group'] ?? 0) ? $param['is_group']-1 : -1; |
|||
$map = [ 'status' => 1]; |
|||
if($user_id){ |
|||
if(!$toContactId){ |
|||
return warning(lang('system.parameterError')); |
|||
} |
|||
$chat_identify=chat_identify($param['user_id'],$param['toContactId']); |
|||
$map['chat_identify'] = $chat_identify; |
|||
} |
|||
if($is_group>=0){ |
|||
$map['is_group']=$is_group; |
|||
} |
|||
$type = isset($param['type']) ? $param['type'] : ''; |
|||
$where = []; |
|||
if ($type && $type != "all") { |
|||
$map['type'] = $type; |
|||
} else { |
|||
$where[] = ['type', 'not in', ['event','admin_notice','webrtc']]; |
|||
} |
|||
$keywords = isset($param['keywords']) ? $param['keywords'] : ''; |
|||
if ($keywords && in_array($type, ['text', 'all'])) { |
|||
$where[] = ['content', 'like', '%' . $keywords . '%']; |
|||
$where[] = ['type', '=', 'text']; |
|||
} |
|||
$listRows = $param['limit'] ?: 20; |
|||
$pageSize = $param['page'] ?: 1; |
|||
$last_id = $param['last_id'] ?? 0; |
|||
if($last_id){ |
|||
$where[]=['msg_id','<',$last_id]; |
|||
} |
|||
$list = MessageModel::getList($map, $where, 'msg_id desc', $listRows, $pageSize); |
|||
$data = $this->recombileMsg($list); |
|||
return success('', $data, $list->total(),$list->currentPage()); |
|||
} |
|||
|
|||
protected function recombileMsg($list,$isPagination=true) |
|||
{ |
|||
$data = []; |
|||
if ($list) { |
|||
$listData = $isPagination ? $list->toArray()['data'] : $list; |
|||
$userList = User::matchUser($listData, true, 'from_user', 120); |
|||
foreach ($listData as $k => $v) { |
|||
$content = str_encipher($v['content'],false); |
|||
$preview = ''; |
|||
$ext=''; |
|||
if (in_array($v['type'], $this->fileType)) { |
|||
$content = getFileUrl($content); |
|||
$preview = previewUrl($content); |
|||
$ext=getExtUrl($content); |
|||
} |
|||
$fromUser = $userList[$v['from_user']]; |
|||
$toContactId=$v['is_group'] ==1 ? 'group-'.$v['to_user'] : $v['to_user']; |
|||
$atList=($v['at'] ?? null) ? explode(',',$v['at']): []; |
|||
if($v['is_group']==0){ |
|||
$toUser=User::where(['user_id'=>$v['to_user']])->field(User::$defaultField)->find() ?? []; |
|||
if($toUser){ |
|||
$toUser=[ |
|||
'name'=>$toUser['realname'] |
|||
]; |
|||
} |
|||
|
|||
}else{ |
|||
$toUser=Group::where(['group_id'=>$v['to_user']])->find(); |
|||
if($toUser){ |
|||
$toUser=[ |
|||
'name'=>$toUser['name'] |
|||
]; |
|||
} |
|||
} |
|||
$data[] = [ |
|||
'msg_id' => $v['msg_id'], |
|||
'id' => $v['id'], |
|||
'status' => "succeed", |
|||
'type' => $v['type'], |
|||
'sendTime' => $v['create_time'] * 1000, |
|||
'create_time' => is_string($v['create_time']) ? $v['create_time'] : date('Y-m-d H:i:s',$v['create_time']), |
|||
'content' => $content, |
|||
'preview' => $preview, |
|||
'download' => $v['file_id'] ? getMainHost().'/filedown/'.encryptIds($v['file_id']) : '', |
|||
'is_read' => $v['is_read'], |
|||
'is_group' => $v['is_group'], |
|||
'at' => $atList, |
|||
'toContactId' => $toContactId, |
|||
'from_user' => $v['from_user'], |
|||
'file_id' => $v['file_id'], |
|||
'file_cate' => $v['file_cate'], |
|||
'fileName' => $v['file_name'], |
|||
'fileSize' => $v['file_size'], |
|||
'fromUser' => $fromUser, |
|||
'toUser' => $toUser, |
|||
'extUrl'=>$ext, |
|||
'extends'=>is_string($v['extends'])?json_decode($v['extends'],true) : $v['extends'] |
|||
]; |
|||
} |
|||
} |
|||
return $data; |
|||
} |
|||
|
|||
// 获取某个联系人的好友列表 |
|||
public function getContacts(){ |
|||
$param = $this->request->param(); |
|||
$user_id=$param['user_id'] ?? 0; |
|||
if(!$user_id){ |
|||
return warning(lang('system.parameterError')); |
|||
} |
|||
$config=$this->globalConfig; |
|||
$listRows = $param['limit'] ?: 20; |
|||
$pageSize = $param['page'] ?: 1; |
|||
$keywords = $param['keywords'] ?: ''; |
|||
$where=[['status','=',1]]; |
|||
if($keywords){ |
|||
$where[] = ['realname', 'like', '%' . $keywords . '%']; |
|||
} |
|||
$hasConvo=$param['hasConvo'] ?? 0; |
|||
if($hasConvo){ |
|||
// 查询最近的联系人 |
|||
$map1 = [['to_user', '=', $user_id], ['is_last', '=', 1], ['is_group', '=', 0]]; |
|||
$map2 = [['from_user', '=', $user_id], ['is_last', '=', 1], ['is_group', '=', 0]]; |
|||
$msgField = 'from_user,to_user,content as lastContent,create_time as lastSendTime,chat_identify,type,del_user'; |
|||
$lasMsgList = Db::name('message') |
|||
->field($msgField) |
|||
->whereOr([$map1, $map2]) |
|||
->order('create_time desc') |
|||
->select(); |
|||
$ids1=\utils\Arr::arrayToString($lasMsgList,'from_user',false); |
|||
$ids2=\utils\Arr::arrayToString($lasMsgList,'to_user',false); |
|||
$ids=array_merge($ids1,$ids2); |
|||
$userList = array_diff($ids, [$user_id]); |
|||
$where[]=['user_id','in',$userList]; |
|||
}else{ |
|||
// 如果是社区模式,就只查询的好友,如果是企业模式,就查询所有用户 |
|||
if($config['sysInfo']['runMode']==1){ |
|||
$where[]=['user_id','<>',$user_id]; |
|||
}else{ |
|||
$friendList = Friend::getFriend(['create_user' => $user_id,'status'=>1]); |
|||
$userList = array_keys($friendList); |
|||
$where[]=['user_id','in',$userList]; |
|||
} |
|||
} |
|||
|
|||
$list = User::where($where)->field(User::$defaultField)->paginate(['list_rows'=>$listRows,'page'=>$pageSize]); |
|||
$data=[]; |
|||
if($list){ |
|||
$data=$list->toArray()['data']; |
|||
foreach ($data as $k => $v) { |
|||
$data[$k]['avatar'] = avatarUrl($v['avatar'], $v['realname'], $v['user_id'], 120); |
|||
$data[$k]['id'] = $v['user_id']; |
|||
} |
|||
} |
|||
return success('',$data,$list->total(),$list->currentPage()); |
|||
} |
|||
|
|||
// 消息处理 |
|||
public function dealMsg(){ |
|||
$param = $this->request->param(); |
|||
$id = $param['id']; |
|||
$message = MessageModel::where(['id' => $id])->find(); |
|||
if ($message) { |
|||
$dealType=$param['dealType'] ?? 0; |
|||
$content=$message['content'] ?? ''; |
|||
if($dealType==1){ |
|||
MessageModel::where(['id' => $id])->delete(); |
|||
// 如果是最后一条消息,需要将上一条设置为最后一条 |
|||
if($message['is_last']){ |
|||
MessageModel::where(['chat_identify'=>$message['chat_identify']])->order('msg_id desc')->limit(1)->update(['is_last'=>1]); |
|||
} |
|||
$action='delMessage'; |
|||
}else{ |
|||
$content=str_encipher(lang('im.forbidMsg'),true); |
|||
MessageModel::where(['id' => $id])->update(['content'=>$content,'type'=>'text']); |
|||
$action='updateMessage'; |
|||
} |
|||
$toContactId = $message['to_user']; |
|||
if ($message['is_group'] == 1) { |
|||
$toContactId = explode('-', $message['chat_identify'])[1]; |
|||
} |
|||
$data=[ |
|||
'id'=>$message['id'], |
|||
'content'=>str_encipher($content,false), |
|||
]; |
|||
wsSendMsg($toContactId, $action, $data, $message['is_group']); |
|||
return success(''); |
|||
} else { |
|||
return warning(lang('im.exist')); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,190 @@ |
|||
<?php |
|||
/** |
|||
* Created by PhpStorm |
|||
* User Julyssn |
|||
* Date 2022/12/14 17:24 |
|||
*/ |
|||
|
|||
|
|||
namespace app\manage\controller; |
|||
|
|||
|
|||
use app\BaseController; |
|||
use easyTask\Terminal; |
|||
use think\App; |
|||
use think\facade\Console; |
|||
use think\Response; |
|||
|
|||
class Task extends BaseController |
|||
{ |
|||
/** |
|||
* 项目根目录 |
|||
* @var string |
|||
*/ |
|||
protected $rootPath; |
|||
|
|||
protected $taskNames = []; |
|||
|
|||
public function __construct(App $app) |
|||
{ |
|||
parent::__construct($app); |
|||
|
|||
$this->rootPath = root_path(); |
|||
chdir($this->rootPath); |
|||
$this->taskNames = [ |
|||
'schedule' => lang('task.schedule'), |
|||
'queue' => lang('task.queue'), |
|||
'worker' => lang('task.worker'), |
|||
'clearStd' => lang('task.clearStd'), |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* 任务列表 |
|||
* @return Response |
|||
*/ |
|||
public function getTaskList() |
|||
{ |
|||
$data = $this->taskMsg(); |
|||
|
|||
if (!count($data)) { |
|||
return warning(''); |
|||
} |
|||
|
|||
foreach ($data as &$datum) { |
|||
$expName = explode('_', $datum['name']); |
|||
|
|||
$datum['remark'] = $this->taskNames[$expName[count($expName) - 1]] ?? lang('task.null'); |
|||
} |
|||
unset($datum); |
|||
return success('', $data); |
|||
} |
|||
|
|||
/** |
|||
* 启动全部进程 |
|||
* @return Response |
|||
*/ |
|||
public function startTask() |
|||
{ |
|||
if(strpos(strtolower(PHP_OS), 'win') === 0) |
|||
{ |
|||
return warning(lang('task.winRun')); |
|||
} |
|||
|
|||
if (count($this->taskMsg())) { |
|||
return warning(lang('task.alreadyRun')); |
|||
} |
|||
|
|||
// 启动 |
|||
$out = Terminal::instance(2)->exec('php think task start'); |
|||
if (!count($this->analysisMsg($out))) { |
|||
return warning(lang('task.startFail')); |
|||
} |
|||
|
|||
return success(lang('task.startOk')); |
|||
} |
|||
|
|||
/** |
|||
* 强制停止全部进程 |
|||
* @return Response |
|||
*/ |
|||
public function stopTask() |
|||
{ |
|||
if (!count($this->taskMsg())) { |
|||
return warning(lang('task.notRun')); |
|||
} |
|||
|
|||
// 强制停止 |
|||
Terminal::instance(2)->exec('php think task stop force'); |
|||
|
|||
return success(''); |
|||
} |
|||
|
|||
/** |
|||
* 获取单个任务日志 |
|||
* @return Response |
|||
*/ |
|||
public function getTaskLog() |
|||
{ |
|||
$name = $this->request->param('name'); |
|||
|
|||
$path = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'easy_task' . DIRECTORY_SEPARATOR . 'Std' . DIRECTORY_SEPARATOR; |
|||
|
|||
if (!file_exists($path . 'exec_' . $name . '.std')) { |
|||
$expName = explode('_', $name); |
|||
$name = $expName[count($expName) - 1]; |
|||
if (!file_exists($path . 'exec_' . $name . '.std')) { |
|||
return warning(lang('task.logExist')); |
|||
} |
|||
} |
|||
|
|||
return success('', file_get_contents($path . 'exec_' . $name . '.std')); |
|||
} |
|||
|
|||
/** |
|||
* 清理单个任务日志 |
|||
* @return Response |
|||
*/ |
|||
public function clearTaskLog() |
|||
{ |
|||
$name = $this->request->param('name'); |
|||
|
|||
$path = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'easy_task' . DIRECTORY_SEPARATOR . 'Std' . DIRECTORY_SEPARATOR; |
|||
|
|||
if (!file_exists($path . 'exec_' . $name . '.std')) { |
|||
$expName = explode('_', $name); |
|||
$name = $expName[count($expName) - 1]; |
|||
if (!file_exists($path . 'exec_' . $name . '.std')) { |
|||
return warning(lang('task.logExist')); |
|||
} |
|||
} |
|||
|
|||
file_put_contents($path . 'exec_' . $name . '.std', ''); |
|||
return success(''); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 获取运行状态 |
|||
* @return array |
|||
*/ |
|||
private function taskMsg() |
|||
{ |
|||
$out = Terminal::instance(2)->exec('php think task status'); |
|||
return $this->analysisMsg($out); |
|||
} |
|||
|
|||
/** |
|||
* 解析数据 |
|||
* @param string $out 带解析数据 |
|||
* @return array |
|||
*/ |
|||
private function analysisMsg(string $out) |
|||
{ |
|||
$re = '/│ *([\w+]+) *│ *([\w+]+)[ ]*│ *([\w+]+|[0-9- :]+) *│ *([\w+]+) *│ *([\w+]+) *│ *([\w+]+) *│/m'; |
|||
|
|||
preg_match_all($re, $out, $matches, PREG_SET_ORDER, 0); |
|||
|
|||
if (!count($matches)) { |
|||
return []; |
|||
} |
|||
|
|||
$data = []; |
|||
$names = $matches[0]; |
|||
unset($names[0]); |
|||
$names = array_values($names); |
|||
unset($matches[0]); |
|||
|
|||
foreach ($matches as $match) { |
|||
$temp = []; |
|||
foreach ($match as $key => $item) { |
|||
if ($key !== 0) { |
|||
$temp[$names[$key - 1]] = $item; |
|||
} |
|||
} |
|||
$data[] = $temp; |
|||
} |
|||
|
|||
return $data; |
|||
} |
|||
} |
|||
@ -0,0 +1,204 @@ |
|||
<?php |
|||
/** |
|||
* Created by PhpStorm |
|||
* User raingad@foxmail.com |
|||
* Date 2022/12/14 17:24 |
|||
*/ |
|||
namespace app\manage\controller; |
|||
use app\BaseController; |
|||
use app\enterprise\model\{User as UserModel,GroupUser,Friend}; |
|||
use think\facade\Db; |
|||
use think\facade\Cache; |
|||
|
|||
class User extends BaseController |
|||
{ |
|||
// 获取用户列表 |
|||
public function index() |
|||
{ |
|||
$map = []; |
|||
$model=new UserModel(); |
|||
$param = $this->request->param(); |
|||
//搜索关键词 |
|||
if ($keyword = $this->request->param('keywords')) { |
|||
$model = $model->whereLike('realname|account|name_py|email', '%' . $keyword . '%'); |
|||
} |
|||
// 排序 |
|||
$order='user_id DESC'; |
|||
if ($param['order_field'] ?? '') { |
|||
$order = orderBy($param['order_field'],$param['order_type'] ?? 1); |
|||
} |
|||
$list = $this->paginate($model->where($map)->order($order)); |
|||
if ($list) { |
|||
$data = $list->toArray()['data']; |
|||
foreach($data as $k=>$v){ |
|||
$data[$k]['avatar']=avatarUrl($v['avatar'],$v['realname'],$v['user_id'],120); |
|||
$data[$k]['location']=$v['last_login_ip'] ? implode(" ", \Ip::find($v['last_login_ip'])) : '--'; |
|||
$data[$k]['reg_location']=$v['register_ip'] ? implode(" ", \Ip::find($v['register_ip'])) : '--'; |
|||
$data[$k]['last_login_time']=$v['last_login_time'] ? date('Y-m-d H:i:s',$v['last_login_time']) : '--'; |
|||
unset($data[$k]['password']); |
|||
} |
|||
} |
|||
return success('', $data, $list->total(), $list->currentPage()); |
|||
} |
|||
|
|||
// 添加用户 |
|||
public function add() |
|||
{ |
|||
try{ |
|||
$data = $this->request->param(); |
|||
$user=new UserModel(); |
|||
$verify=$user->checkAccount($data); |
|||
if(!$verify){ |
|||
return warning($user->getError()); |
|||
} |
|||
$salt=\utils\Str::random(4); |
|||
$data['password'] = password_hash_tp($data['password'],$salt); |
|||
$data['salt'] =$salt; |
|||
$data['register_ip'] =$this->request->ip(); |
|||
$data['name_py'] = pinyin_sentence($data['realname']); |
|||
$user->save($data); |
|||
$data['user_id']=$user->user_id; |
|||
return success(lang('system.addOk'), $data); |
|||
}catch (\Exception $e){ |
|||
return error(lang('system.addFail')); |
|||
} |
|||
} |
|||
|
|||
// 修改用户 |
|||
public function edit() |
|||
{ |
|||
try{ |
|||
$data = $this->request->param(); |
|||
$user=new UserModel(); |
|||
$verify=$user->checkAccount($data); |
|||
if(!$verify){ |
|||
return warning($user->getError()); |
|||
} |
|||
$user=UserModel::find($data['user_id']); |
|||
$user->account =$data['account']; |
|||
$user->realname =$data['realname']; |
|||
$user->email =$data['email']; |
|||
$user->remark=$data['remark']; |
|||
$user->sex =$data['sex'] ?? 0; |
|||
$user->friend_limit =$data['friend_limit']; |
|||
$user->group_limit =$data['group_limit']; |
|||
$csUid=$data['cs_uid'] ?? 0; |
|||
if($csUid && $csUid==$data['user_id']){ |
|||
return warning(lang('user.notOwn')); |
|||
} |
|||
$user->cs_uid =$data['cs_uid']; |
|||
// 只有超管才能设置管理员 |
|||
if($this->userInfo['user_id']==1){ |
|||
$user->role =$data['role']; |
|||
} |
|||
$user->status =$data['status']; |
|||
$user->name_py= pinyin_sentence($data['realname']); |
|||
$user->save(); |
|||
return success(lang('system.editOk'), $data); |
|||
}catch (\Exception $e){ |
|||
return error(lang('system.editFail')); |
|||
} |
|||
} |
|||
|
|||
// 删除用户 |
|||
public function del() |
|||
{ |
|||
$user_id = $this->request->param('user_id'); |
|||
$user=UserModel::find($user_id); |
|||
if(!$user || $user->user_id==1){ |
|||
return warning(lang('user.exist')); |
|||
} |
|||
Db::startTrans(); |
|||
try{ |
|||
// 删除其好友关系 |
|||
Friend::where('create_user', $user_id)->whereOr(['friend_user_id'=>$user_id])->delete(); |
|||
// 删除其群组关系 |
|||
GroupUser::where('user_id', $user_id)->delete(); |
|||
UserModel::destroy($user_id); |
|||
Db::commit(); |
|||
return success(lang('system.delOk')); |
|||
}catch (\Exception $e){ |
|||
Db::rollback(); |
|||
return error($e->getMessage()); |
|||
} |
|||
} |
|||
|
|||
// 修改用户状态 |
|||
public function setStatus() |
|||
{ |
|||
$user_id = $this->request->param('user_id'); |
|||
$user=UserModel::find($user_id); |
|||
if(!$user){ |
|||
return warning(lang('user.exist')); |
|||
} |
|||
try{ |
|||
$status = $this->request->param('status',0); |
|||
// 将禁用状态写入缓存 |
|||
if(!$status){ |
|||
Cache::set('forbidUser_'.$user_id,true,env('jwt.ttl',86400)); |
|||
} |
|||
UserModel::where('user_id', $user_id)->update(['status'=>$status]); |
|||
return success(lang('system.editOk')); |
|||
}catch (\Exception $e){ |
|||
return error(lang('system.editFail')); |
|||
} |
|||
} |
|||
|
|||
// 获取用户信息 |
|||
public function detail() |
|||
{ |
|||
$user_id = $this->request->param('user_id'); |
|||
$user=UserModel::find($user_id); |
|||
if(!$user){ |
|||
return error(lang('user.exist')); |
|||
} |
|||
$user->avatar=avatarUrl($user->avatar,$user->realname,$user->user_id,120); |
|||
$location=''; |
|||
if($user->last_login_ip){ |
|||
$location=implode(" ", \Ip::find($user->last_login_ip)); |
|||
} |
|||
$user->location=$location; |
|||
$user->password=''; |
|||
return success('', $user); |
|||
} |
|||
|
|||
// 设置用户角色 |
|||
public function setRole() |
|||
{ |
|||
$user_id = $this->request->param('user_id'); |
|||
$user=UserModel::find($user_id); |
|||
if(!$user){ |
|||
return warning(lang('user.exist')); |
|||
} |
|||
try{ |
|||
$role = $this->request->param('role'); |
|||
UserModel::where('user_id', $user_id)->update(['role'=>$role]); |
|||
return success(''); |
|||
}catch (\Exception $e){ |
|||
return error(''); |
|||
} |
|||
} |
|||
|
|||
// 修改密码 |
|||
public function editPassword() |
|||
{ |
|||
$user_id = $this->request->param('user_id'); |
|||
$user=UserModel::find($user_id); |
|||
if(!$user){ |
|||
return warning(lang('user.exist')); |
|||
} |
|||
try{ |
|||
$password = $this->request->param('password',''); |
|||
if($password){ |
|||
$salt=$user->salt; |
|||
$user->password= password_hash_tp($password,$salt); |
|||
Cache::set('forbidUser_'.$user_id,true,env('jwt.ttl',86400)); |
|||
} |
|||
$user->save(); |
|||
return success(''); |
|||
}catch (\Exception $e){ |
|||
return error(''); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
<?php |
|||
return [ |
|||
"checkAuth", |
|||
"manageAuth" |
|||
]; |
|||
@ -0,0 +1,45 @@ |
|||
<?php |
|||
/** |
|||
* raingad IM [ThinkPHP6] |
|||
* @author xiekunyu <raingad@foxmail.com> |
|||
*/ |
|||
namespace app\manage\model; |
|||
|
|||
use app\BaseModel; |
|||
use think\facade\Cache; |
|||
class Config extends BaseModel |
|||
{ |
|||
protected $json = ['value']; |
|||
protected $jsonAssoc = true; |
|||
|
|||
// 获取系统配置信息 |
|||
public static function getSystemInfo($update=false){ |
|||
$name='systemInfo'; |
|||
// $auth=request()->header('Authorization'); |
|||
$nameFields=['sysInfo','fileUpload','chatInfo','compass']; |
|||
// 如果是登录状态才会返回chatINfo |
|||
// if($auth){ |
|||
// $name='all'.$name; |
|||
// $nameFields[]="chatInfo"; |
|||
// } |
|||
if(Cache::has($name) && !$update){ |
|||
$systemInfo=Cache::get($name); |
|||
}else{ |
|||
$systemInfo=[]; |
|||
$conf=Config::where([['name','in',$nameFields]])->select()->toArray(); |
|||
foreach($conf as $v){ |
|||
$value=[]; |
|||
if($v['name']=='fileUpload'){ |
|||
$value['size'] = $v['value']['size']; |
|||
$value['preview'] = $v['value']['preview']; |
|||
$value['fileExt'] = $v['value']['fileExt']; |
|||
}else{ |
|||
$value=$v['value']; |
|||
} |
|||
$systemInfo[$v['name']]=$value; |
|||
} |
|||
Cache::set($name,$systemInfo,7*86400); |
|||
} |
|||
return $systemInfo; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
<?php |
|||
// 全局中间件定义文件 |
|||
return [ |
|||
// 全局请求缓存 |
|||
|
|||
// 'think\middleware\CheckRequestCache', |
|||
|
|||
// 多语言加载 |
|||
|
|||
// 'think\middleware\LoadLangPack', |
|||
|
|||
// Session初始化 |
|||
|
|||
'think\middleware\SessionInit', |
|||
|
|||
// 页面Trace调试 |
|||
|
|||
// 'think\middleware\TraceDebug', |
|||
]; |
|||
@ -0,0 +1,9 @@ |
|||
<?php |
|||
use app\ExceptionHandle; |
|||
use app\Request; |
|||
|
|||
// 容器Provider定义文件 |
|||
return [ |
|||
'think\Request' => Request::class, |
|||
'think\exception\Handle' => ExceptionHandle::class, |
|||
]; |
|||
@ -0,0 +1,9 @@ |
|||
<?php |
|||
|
|||
use app\AppService; |
|||
|
|||
// 系统服务定义文件 |
|||
// 服务在完成全局初始化之后执行 |
|||
return [ |
|||
AppService::class, |
|||
]; |
|||
@ -0,0 +1,105 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\worker; |
|||
|
|||
use think\App; |
|||
use think\exception\Handle; |
|||
use think\exception\HttpException; |
|||
use Workerman\Connection\TcpConnection; |
|||
use Workerman\Protocols\Http\Response; |
|||
/** |
|||
* Worker应用对象 |
|||
*/ |
|||
class Application extends App |
|||
{ |
|||
/** |
|||
* 处理Worker请求 |
|||
* @access public |
|||
* @param \Workerman\Connection\TcpConnection $connection |
|||
* @param void |
|||
*/ |
|||
public function worker(TcpConnection $connection) |
|||
{ |
|||
try { |
|||
$this->beginTime = microtime(true); |
|||
$this->beginMem = memory_get_usage(); |
|||
$this->db->clearQueryTimes(); |
|||
|
|||
$pathinfo = ltrim(strpos($_SERVER['REQUEST_URI'], '?') ? strstr($_SERVER['REQUEST_URI'], '?', true) : $_SERVER['REQUEST_URI'], '/'); |
|||
|
|||
$this->request |
|||
->setPathinfo($pathinfo) |
|||
->withInput($GLOBALS['HTTP_RAW_POST_DATA']); |
|||
|
|||
while (ob_get_level() > 1) { |
|||
ob_end_clean(); |
|||
} |
|||
|
|||
ob_start(); |
|||
$response = $this->http->run(); |
|||
$content = ob_get_clean(); |
|||
|
|||
ob_start(); |
|||
|
|||
$response->send(); |
|||
$this->http->end($response); |
|||
|
|||
$content .= ob_get_clean() ?: ''; |
|||
|
|||
$this->httpResponseCode($response->getCode()); |
|||
$header=[]; |
|||
foreach ($response->getHeader() as $name => $val) { |
|||
// 发送头部信息 |
|||
$header[$name] =!is_null($val) ? $val : ''; |
|||
} |
|||
if (strtolower($_SERVER['HTTP_CONNECTION']) === "keep-alive") { |
|||
$connection->send(new Response(200, $header, $content)); |
|||
} else { |
|||
$connection->close(new Response(200, $header, $content)); |
|||
} |
|||
} catch (HttpException | \Exception | \Throwable $e) { |
|||
$this->exception($connection, $e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 是否运行在命令行下 |
|||
* @return bool |
|||
*/ |
|||
public function runningInConsole(): bool |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
protected function httpResponseCode($code = 200) |
|||
{ |
|||
new Response($code); |
|||
} |
|||
|
|||
protected function exception($connection, $e) |
|||
{ |
|||
if ($e instanceof \Exception) { |
|||
$handler = $this->make(Handle::class); |
|||
$handler->report($e); |
|||
|
|||
$resp = $handler->render($this->request, $e); |
|||
$content = $resp->getContent(); |
|||
$code = $resp->getCode(); |
|||
|
|||
$this->httpResponseCode(new Response($code, [], $content)); |
|||
$connection->send($content); |
|||
} else { |
|||
$connection->send(new Response(500, [], $e->getMessage())); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,142 @@ |
|||
<?php |
|||
/** |
|||
* This file is part of workerman. |
|||
* |
|||
* Licensed under The MIT License |
|||
* For full copyright and license information, please see the MIT-LICENSE.txt |
|||
* Redistributions of files must retain the above copyright notice. |
|||
* |
|||
* @author walkor<walkor@workerman.net> |
|||
* @copyright walkor<walkor@workerman.net> |
|||
* @link http://www.workerman.net/ |
|||
* @license http://www.opensource.org/licenses/mit-license.php MIT License |
|||
*/ |
|||
|
|||
namespace app\worker; |
|||
/** |
|||
* 推送主逻辑 |
|||
* 主要是处理 onMessage onClose |
|||
*/ |
|||
use GatewayWorker\Lib\Gateway; |
|||
use app\worker\Application; |
|||
use think\facade\Config; |
|||
use Lcobucci\JWT\Builder; |
|||
use Lcobucci\JWT\Parser; |
|||
use thans\jwt\provider\JWT\Lcobucci; |
|||
use utils\Aes; |
|||
|
|||
class Events |
|||
{ |
|||
// 使用TP框架 |
|||
public static function onWorkerStart() |
|||
{ |
|||
$app = new Application; |
|||
$app->initialize(); |
|||
} |
|||
|
|||
// 当有客户端连接时,将client_id返回,让mvc框架判断当前uid并执行绑定 |
|||
public static function onConnect($client_id) |
|||
{ |
|||
Gateway::sendToClient($client_id, json_encode(array( |
|||
'type' => 'init', |
|||
'client_id' => $client_id |
|||
))); |
|||
} |
|||
/** |
|||
* 有消息时 |
|||
* @param int $client_id |
|||
* @param mixed $message |
|||
*/ |
|||
public static function onMessage($client_id, $message) |
|||
{ |
|||
// 客户端传递的是json数据 |
|||
$message_data = json_decode($message, true); |
|||
if(!$message_data) |
|||
{ |
|||
return ; |
|||
} |
|||
|
|||
// 根据类型执行不同的业务 |
|||
switch($message_data['type']) |
|||
{ |
|||
// 客户端回应服务端的心跳 |
|||
case 'pong': |
|||
break; |
|||
case 'ping': |
|||
self::sendStatus($client_id); |
|||
break; |
|||
case 'bindUid': |
|||
self::auth($client_id,$message_data); |
|||
break; |
|||
} |
|||
return; |
|||
} |
|||
|
|||
protected static function sendStatus($client_id){ |
|||
$uid=$_SESSION['user_id'] ?? 0; |
|||
$multiport=false; |
|||
if($uid){ |
|||
$arr=Gateway::getClientIdByUid($uid); |
|||
if(count($arr)>1){ |
|||
$multiport=true; |
|||
} |
|||
} |
|||
Gateway::sendToClient($client_id, json_encode(array( |
|||
'type' => 'pong', |
|||
'multiport' => $multiport, |
|||
))); |
|||
} |
|||
|
|||
//验证用户的真实性并绑定 |
|||
protected static function auth($client_id, $msg){ |
|||
$token=$msg['token'] ?? ''; |
|||
$config = Config::get('jwt'); |
|||
$keys = $config['secret'] ?: [ |
|||
'public' => $config['public_key'], |
|||
'private' => $config['private_key'], |
|||
'password' => $config['password'], |
|||
]; |
|||
$provider = new Lcobucci(new Builder(), new Parser(), $config['algo'], $keys); |
|||
try { |
|||
$token=str_replace('bearer ','',$token); |
|||
$jwtData = $provider->decode((string)$token); |
|||
} catch (\Exception $exception) { |
|||
self::closeClient($client_id); |
|||
} |
|||
|
|||
$userInfo = $jwtData['info']->getValue(); |
|||
//解密token中的用户信息 |
|||
$userInfo = Aes::decrypt($userInfo, config('app.aes_token_key')); |
|||
//解析json |
|||
$userInfo = (array)json_decode($userInfo, true); |
|||
if(!$userInfo){ |
|||
self::closeClient($client_id); |
|||
} |
|||
$_SESSION['user_id']=$userInfo['user_id']; |
|||
self::sendStatus($client_id); |
|||
} |
|||
|
|||
//断开连接 |
|||
protected static function closeClient($client_id){ |
|||
$_SESSION['user_id']=null; |
|||
Gateway::closeClient($client_id); |
|||
} |
|||
|
|||
/** |
|||
* 当断开连接时 |
|||
* @param int $client_id |
|||
*/ |
|||
public static function onClose($client_id) |
|||
{ |
|||
$user_id=$_SESSION['user_id']; |
|||
if($user_id){ |
|||
Gateway::sendToAll(json_encode(array( |
|||
'type' => 'isOnline', |
|||
'time' => time(), |
|||
'data' => ['id'=>$user_id,'is_online'=>0] |
|||
))); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,201 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\worker\command; |
|||
|
|||
use GatewayWorker\BusinessWorker; |
|||
use GatewayWorker\Gateway; |
|||
use GatewayWorker\Register; |
|||
use think\console\Command; |
|||
use think\console\Input; |
|||
use think\console\input\Argument; |
|||
use think\console\input\Option; |
|||
use think\console\Output; |
|||
use think\facade\Config; |
|||
use Workerman\Worker; |
|||
|
|||
/** |
|||
* Worker 命令行类 |
|||
*/ |
|||
class GatewayWorker extends Command |
|||
{ |
|||
public function configure() |
|||
{ |
|||
$this->setName('worker:gateway') |
|||
->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start') |
|||
->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of workerman server.', null) |
|||
->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of workerman server.', null) |
|||
->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the workerman server in daemon mode.') |
|||
->setDescription('GatewayWorker Server for ThinkPHP'); |
|||
} |
|||
|
|||
public function execute(Input $input, Output $output) |
|||
{ |
|||
$action = $input->getArgument('action'); |
|||
|
|||
if (DIRECTORY_SEPARATOR !== '\\') { |
|||
if (!in_array($action, ['start', 'stop', 'reload', 'restart', 'status', 'connections'])) { |
|||
$output->writeln("Invalid argument action:{$action}, Expected start|stop|restart|reload|status|connections ."); |
|||
exit(1); |
|||
} |
|||
|
|||
global $argv; |
|||
array_shift($argv); |
|||
array_shift($argv); |
|||
array_unshift($argv, 'think', $action); |
|||
} else { |
|||
$output->writeln("GatewayWorker Not Support On Windows."); |
|||
exit(1); |
|||
} |
|||
|
|||
if ('start' == $action) { |
|||
$output->writeln('Starting GatewayWorker server...'); |
|||
} |
|||
|
|||
$option = Config::get('gateway'); |
|||
|
|||
if ($input->hasOption('host')) { |
|||
$host = $input->getOption('host'); |
|||
} else { |
|||
$host = !empty($option['host']) ? $option['host'] : '0.0.0.0'; |
|||
} |
|||
|
|||
if ($input->hasOption('port')) { |
|||
$port = $input->getOption('port'); |
|||
} else { |
|||
$port = !empty($option['port']) ? $option['port'] : '2347'; |
|||
} |
|||
|
|||
$this->start($host, (int) $port, $option); |
|||
} |
|||
|
|||
/** |
|||
* 启动 |
|||
* @access public |
|||
* @param string $host 监听地址 |
|||
* @param integer $port 监听端口 |
|||
* @param array $option 参数 |
|||
* @return void |
|||
*/ |
|||
public function start(string $host, int $port, array $option = []) |
|||
{ |
|||
$registerAddress = !empty($option['registerAddress']) ? $option['registerAddress'] : '127.0.0.1:1236'; |
|||
|
|||
if (!empty($option['register_deploy'])) { |
|||
// 分布式部署的时候其它服务器可以关闭register服务 |
|||
// 注意需要设置不同的lanIp |
|||
$this->register($registerAddress); |
|||
} |
|||
|
|||
// 启动businessWorker |
|||
if (!empty($option['businessWorker_deploy'])) { |
|||
$this->businessWorker($registerAddress, $option['businessWorker'] ?? []); |
|||
} |
|||
|
|||
// 启动gateway |
|||
if (!empty($option['gateway_deploy'])) { |
|||
$this->gateway($registerAddress, $host, $port, $option); |
|||
} |
|||
|
|||
Worker::runAll(); |
|||
} |
|||
|
|||
/** |
|||
* 启动register |
|||
* @access public |
|||
* @param string $registerAddress |
|||
* @return void |
|||
*/ |
|||
public function register(string $registerAddress) |
|||
{ |
|||
// 初始化register |
|||
new Register('text://' . $registerAddress); |
|||
} |
|||
|
|||
/** |
|||
* 启动businessWorker |
|||
* @access public |
|||
* @param string $registerAddress registerAddress |
|||
* @param array $option 参数 |
|||
* @return void |
|||
*/ |
|||
public function businessWorker(string $registerAddress, array $option = []) |
|||
{ |
|||
// 初始化 bussinessWorker 进程 |
|||
$worker = new BusinessWorker(); |
|||
|
|||
$this->option($worker, $option); |
|||
|
|||
$worker->registerAddress = $registerAddress; |
|||
} |
|||
|
|||
/** |
|||
* 启动gateway |
|||
* @access public |
|||
* @param string $registerAddress registerAddress |
|||
* @param string $host 服务地址 |
|||
* @param integer $port 监听端口 |
|||
* @param array $option 参数 |
|||
* @return void |
|||
*/ |
|||
public function gateway(string $registerAddress, string $host, int $port, array $option = []) |
|||
{ |
|||
// 初始化 gateway 进程 |
|||
if (!empty($option['socket'])) { |
|||
$socket = $option['socket']; |
|||
unset($option['socket']); |
|||
} else { |
|||
$protocol = !empty($option['protocol']) ? $option['protocol'] : 'websocket'; |
|||
$socket = $protocol . '://' . $host . ':' . $port; |
|||
unset($option['host'], $option['port'], $option['protocol']); |
|||
} |
|||
|
|||
$gateway = new Gateway($socket, $option['context'] ?? []); |
|||
|
|||
// 以下设置参数都可以在配置文件中重新定义覆盖 |
|||
$gateway->name = 'Gateway'; |
|||
$gateway->count = 4; |
|||
$gateway->lanIp = '127.0.0.1'; |
|||
$gateway->startPort = 2000; |
|||
$gateway->pingInterval = 30; |
|||
$gateway->pingNotResponseLimit = 0; |
|||
$gateway->pingData = '{"type":"ping"}'; |
|||
$gateway->registerAddress = $registerAddress; |
|||
|
|||
// 全局静态属性设置 |
|||
foreach ($option as $name => $val) { |
|||
if (in_array($name, ['stdoutFile', 'daemonize', 'pidFile', 'logFile'])) { |
|||
Worker::${$name} = $val; |
|||
unset($option[$name]); |
|||
} |
|||
} |
|||
|
|||
$this->option($gateway, $option); |
|||
} |
|||
|
|||
/** |
|||
* 设置参数 |
|||
* @access protected |
|||
* @param Worker $worker Worker对象 |
|||
* @param array $option 参数 |
|||
* @return void |
|||
*/ |
|||
protected function option(Worker $worker, array $option = []) |
|||
{ |
|||
// 设置参数 |
|||
if (!empty($option)) { |
|||
foreach ($option as $key => $val) { |
|||
$worker->$key = $val; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
<?php |
|||
/** |
|||
* This file is part of workerman. |
|||
* |
|||
* Licensed under The MIT License |
|||
* For full copyright and license information, please see the MIT-LICENSE.txt |
|||
* Redistributions of files must retain the above copyright notice. |
|||
* |
|||
* @author walkor<walkor@workerman.net> |
|||
* @copyright walkor<walkor@workerman.net> |
|||
* @link http://www.workerman.net/ |
|||
* @license http://www.opensource.org/licenses/mit-license.php MIT License |
|||
*/ |
|||
use \Workerman\Worker; |
|||
use \GatewayWorker\BusinessWorker; |
|||
|
|||
require_once __DIR__ . '/../../vendor/autoload.php'; |
|||
// bussinessWorker 进程 |
|||
$worker = new BusinessWorker(); |
|||
// worker名称 |
|||
$worker->name = 'PushBusinessWorker'; |
|||
// bussinessWorker进程数量 |
|||
$worker->count = 1; |
|||
// 服务注册地址 |
|||
$worker->registerAddress = '127.0.0.1:1236'; |
|||
$worker->eventHandler = 'app\worker\Events'; |
|||
// 如果不是在根目录启动,则运行runAll方法 |
|||
if(!defined('GLOBAL_START')) |
|||
{ |
|||
Worker::runAll(); |
|||
} |
|||
|
|||
@ -0,0 +1,42 @@ |
|||
<?php |
|||
/** |
|||
* This file is part of workerman. |
|||
* |
|||
* Licensed under The MIT License |
|||
* For full copyright and license information, please see the MIT-LICENSE.txt |
|||
* Redistributions of files must retain the above copyright notice. |
|||
* |
|||
* @author walkor<walkor@workerman.net> |
|||
* @copyright walkor<walkor@workerman.net> |
|||
* @link http://www.workerman.net/ |
|||
* @license http://www.opensource.org/licenses/mit-license.php MIT License |
|||
*/ |
|||
use \Workerman\Worker; |
|||
use \GatewayWorker\Gateway; |
|||
use \Workerman\Autoloader; |
|||
require_once __DIR__ . '/../../vendor/autoload.php'; |
|||
|
|||
// gateway 进程 |
|||
$gateway = new Gateway("Websocket://0.0.0.0:8282"); |
|||
// 设置名称,方便status时查看 |
|||
$gateway->name = 'pushMessage'; |
|||
// 设置进程数,gateway进程数建议与cpu核数相同 |
|||
$gateway->count = 1; |
|||
// 分布式部署时请设置成内网ip(非127.0.0.1) |
|||
$gateway->lanIp = '127.0.0.1'; |
|||
// 内部通讯起始端口。假如$gateway->count=4,起始端口为2300 |
|||
// 则一般会使用2300 2301 2302 2303 4个端口作为内部通讯端口 |
|||
$gateway->startPort = 2300; |
|||
// 心跳间隔 |
|||
$gateway->pingInterval = 20; |
|||
// 心跳数据 |
|||
$gateway->pingData = '{"type":"ping"}'; |
|||
// 服务注册地址 |
|||
$gateway->registerAddress = '127.0.0.1:1236'; |
|||
|
|||
// 如果不是在根目录启动,则运行runAll方法 |
|||
if(!defined('GLOBAL_START')) |
|||
{ |
|||
Worker::runAll(); |
|||
} |
|||
|
|||
@ -0,0 +1,25 @@ |
|||
<?php |
|||
/** |
|||
* This file is part of workerman. |
|||
* |
|||
* Licensed under The MIT License |
|||
* For full copyright and license information, please see the MIT-LICENSE.txt |
|||
* Redistributions of files must retain the above copyright notice. |
|||
* |
|||
* @author walkor<walkor@workerman.net> |
|||
* @copyright walkor<walkor@workerman.net> |
|||
* @link http://www.workerman.net/ |
|||
* @license http://www.opensource.org/licenses/mit-license.php MIT License |
|||
*/ |
|||
use \Workerman\Worker; |
|||
use \GatewayWorker\Register; |
|||
require_once __DIR__ . '/../../vendor/autoload.php'; |
|||
// register 服务必须是text协议 |
|||
$register = new Register('text://0.0.0.0:1236'); |
|||
|
|||
// 如果不是在根目录启动,则运行runAll方法 |
|||
if(!defined('GLOBAL_START')) |
|||
{ |
|||
Worker::runAll(); |
|||
} |
|||
|
|||
Loading…
Reference in new issue