wanghongjun 7 months ago
parent
commit
0a939e0725
  1. 1
      app/.htaccess
  2. 22
      app/AppService.php
  3. 141
      app/BaseController.php
  4. 103
      app/BaseModel.php
  5. 58
      app/ExceptionHandle.php
  6. 8
      app/Request.php
  7. 1081
      app/common.php
  8. 20
      app/event.php
  9. 62
      app/lang/en_us copy.php
  10. 156
      app/lang/en_us.php
  11. 155
      app/lang/zh_cn.php
  12. 104
      app/manage/controller/Config.php
  13. 166
      app/manage/controller/Group.php
  14. 107
      app/manage/controller/Index.php
  15. 209
      app/manage/controller/Message.php
  16. 190
      app/manage/controller/Task.php
  17. 204
      app/manage/controller/User.php
  18. 5
      app/manage/middleware.php
  19. 45
      app/manage/model/Config.php
  20. 19
      app/middleware.php
  21. 9
      app/provider.php
  22. 9
      app/service.php
  23. 105
      app/worker/Application.php
  24. 142
      app/worker/Events.php
  25. 201
      app/worker/command/GatewayWorker.php
  26. 32
      app/worker/start_businessworker.php
  27. 42
      app/worker/start_gateway.php
  28. 25
      app/worker/start_register.php

1
app/.htaccess

@ -0,0 +1 @@
deny from all

22
app/AppService.php

@ -0,0 +1,22 @@
<?php
declare (strict_types = 1);
namespace app;
use think\Service;
/**
* 应用服务类
*/
class AppService extends Service
{
public function register()
{
// 服务注册
}
public function boot()
{
// 服务启动
}
}

141
app/BaseController.php

@ -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);
}
}

103
app/BaseModel.php

@ -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();
}
}

58
app/ExceptionHandle.php

@ -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);
}
}

8
app/Request.php

@ -0,0 +1,8 @@
<?php
namespace app;
// 应用请求对象类
class Request extends \think\Request
{
}

1081
app/common.php

File diff suppressed because it is too large

20
app/event.php

@ -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' => [
],
];

62
app/lang/en_us copy.php

@ -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'
];

156
app/lang/en_us.php

@ -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'
]
];

155
app/lang/zh_cn.php

@ -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'=>'二维码已失效'
]
];

104
app/manage/controller/Config.php

@ -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'));
}
}

166
app/manage/controller/Group.php

@ -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('');
}
}
}

107
app/manage/controller/Index.php

@ -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('');
}
}

209
app/manage/controller/Message.php

@ -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'));
}
}
}

190
app/manage/controller/Task.php

@ -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;
}
}

204
app/manage/controller/User.php

@ -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('');
}
}
}

5
app/manage/middleware.php

@ -0,0 +1,5 @@
<?php
return [
"checkAuth",
"manageAuth"
];

45
app/manage/model/Config.php

@ -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;
}
}

19
app/middleware.php

@ -0,0 +1,19 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// 'think\middleware\CheckRequestCache',
// 多语言加载
// 'think\middleware\LoadLangPack',
// Session初始化
'think\middleware\SessionInit',
// 页面Trace调试
// 'think\middleware\TraceDebug',
];

9
app/provider.php

@ -0,0 +1,9 @@
<?php
use app\ExceptionHandle;
use app\Request;
// 容器Provider定义文件
return [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
];

9
app/service.php

@ -0,0 +1,9 @@
<?php
use app\AppService;
// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
AppService::class,
];

105
app/worker/Application.php

@ -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()));
}
}
}

142
app/worker/Events.php

@ -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]
)));
}
}
}

201
app/worker/command/GatewayWorker.php

@ -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;
}
}
}
}

32
app/worker/start_businessworker.php

@ -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();
}

42
app/worker/start_gateway.php

@ -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();
}

25
app/worker/start_register.php

@ -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…
Cancel
Save