Browse Source

app.controller

author
wanghongjun 9 months ago
parent
commit
99d85921a8
  1. 99
      app/enterprise/controller/Emoji.php
  2. 92
      app/enterprise/controller/Files.php
  3. 181
      app/enterprise/controller/Friend.php
  4. 528
      app/enterprise/controller/Group.php
  5. 835
      app/enterprise/controller/Im.php
  6. 49
      app/enterprise/listener/GroupChange.php
  7. 4
      app/enterprise/middleware.php
  8. 12
      app/enterprise/model/Emoji.php
  9. 14
      app/enterprise/model/File.php
  10. 28
      app/enterprise/model/Friend.php
  11. 60
      app/enterprise/model/Group.php
  12. 66
      app/enterprise/model/GroupUser.php
  13. 263
      app/enterprise/model/Message.php
  14. 515
      app/enterprise/model/User.php
  15. 10
      app/enterprise/model/WechatMoments.php
  16. 24
      app/enterprise/validate/User.php
  17. 184
      app/index/controller/Index.php
  18. 568
      app/index/controller/Install.php
  19. 17
      app/index/route/app.php

99
app/enterprise/controller/Emoji.php

@ -0,0 +1,99 @@
<?php
namespace app\enterprise\controller;
use app\BaseController;
use app\enterprise\model\{Emoji as EmojiModel,File,Message};
use think\facade\Filesystem;
use think\facade\View;
class Emoji extends BaseController
{
// 表情列表
public function index()
{
$map=['status'=>1,'user_id'=>$this->uid,'type'=>2];
$list = EmojiModel::where($map)->field('id,name,src,file_id')->order('update_time desc')->select();
$data=[];
if($list){
$data=$list->toArray();
foreach ($data as $k => $v) {
$url=getFileUrl($v['src']);
$data[$k]['src'] =$url;
$data[$k]['title'] =$v['name'];
}
}
return success('', $data, count($data));
}
// 添加表情
public function add(){
$param = $this->request->param();
$file_id=$param['file_id'];
$fileInfo=File::find($file_id);
if(!$fileInfo){
return warning(lang('system.exits'));
}
$exist=EmojiModel::where(['user_id'=>$this->uid,'file_id'=>$file_id])->find();
// 判断是否已经有了当前表情,有了就更新
if($exist){
EmojiModel::where(['id'=>$exist['id']])->update(['update_time'=>time()]);
}else{
$info=[
'user_id'=>$this->uid,
'type'=>2,
'file_id'=>$file_id,
'name'=>$fileInfo->name,
'src'=>$fileInfo->src,
];
EmojiModel::create($info);
}
return success(lang('system.addOk'));
}
// 删除表情
public function del(){
$ids = $this->request->param('ids',[]);
if(!is_array($ids) || $ids==[]){
return warning(lang('system.parameterError'));
}
foreach($ids as $id){
$emoji=EmojiModel::where(['id'=>$id,'user_id'=>$this->uid])->find();
if(!$emoji){
continue;
}
$res=EmojiModel::where(['id'=>$id])->delete();
if($res){
$exist=EmojiModel::where(['file_id'=>$emoji['file_id']])->find();
$exist2=Message::where(['file_id'=>$emoji['file_id']])->find();
// 如果文件没有引用了,就删除掉源文件
if(!$exist || !$exist2){
$disk=env('filesystem.driver','local');
$file=File::find($emoji['file_id']);
Filesystem::disk($disk)->delete($file->src);
}
}
}
return success(lang('system.delOk'));
}
// 移动表情
public function move(){
$ids = $this->request->param('ids',[]);
if(!is_array($ids) || $ids==[]){
return warning(lang('system.parameterError'));
}
foreach($ids as $id){
$emoji=EmojiModel::where(['id'=>$id,'user_id'=>$this->uid])->find();
if(!$emoji){
continue;
}
EmojiModel::where(['id'=>$id])->update(['update_time'=>time()]);
}
return success(lang('system.success'));
}
}

92
app/enterprise/controller/Files.php

@ -0,0 +1,92 @@
<?php
namespace app\enterprise\controller;
use app\BaseController;
use app\enterprise\model\{File,User,Message};
use think\facade\View;
class Files extends BaseController
{
// 文件列表
public function index()
{
$param = $this->request->param();
$is_all = $param['is_all'] ?? 0;
$map = [];
$data=[];
// 如果是查询全部,就查询file表,否则查询message表
if ($is_all) {
if ($param['cate'] ?? 0) {
$map[] = ['cate', '=', $param['cate']];
}
$model = new File();
if ($param['keywords'] ?? '') {
$model = $model->where('name', 'like', '%' . $param['keywords'] . '%');
}
$list = $this->paginate($model->where($map)->order('file_id desc'));
if ($list) {
$data = $list->toArray()['data'];
$userList = User::matchUser($data, true, 'user_id', 120);
foreach ($data as $k => $v) {
$url=getFileUrl($v['src']);
$data[$k]['src'] =$url;
$data[$k]['preview'] = previewUrl($url);
$data[$k]['extUrl'] = getExtUrl($v['src']);
$data[$k]['name'] = $v['name'].'.'.$v['ext'];
$data[$k]['msg_type'] = getFileType($v['ext'],true);
$data[$k]['user_id_info'] = $userList[$v['user_id']] ?? [];
$data[$k]['download'] = getMainHost().'/filedown/'.encryptIds($v['file_id']);
}
}
} else {
$map = [
['file_id', '>', 0],
['type', '<>', 'voice'],
['is_group', '=', 0],
['is_undo', '=', 0],
];
if ($param['cate'] ?? 0) {
$map[] = ['file_cate', '=', $param['cate']];
}
$user_id = $this->uid;
$model = new Message();
if ($param['keywords'] ?? '') {
$map[] = ['file_name', 'like', '%' . $param['keywords'] . '%'];
}
$role = $param['role'] ?? 0;
$where=[];
if($role==1){
$map[] = ['from_user', '=', $user_id];
}elseif($role==2){
$map[] = ['to_user', '=', $user_id];
}else{
$where='(from_user='.$user_id.' or to_user='.$user_id.')';
}
$list = $this->paginate($model->where($map)->where($where)->order('create_time desc'));
if ($list) {
$data = $list->toArray()['data'];
$userList = User::matchUser($data, true, 'from_user', 120);
foreach ($data as $k => $v) {
$content=str_encipher($v['content'],false);
$url=getFileUrl($content);
$data[$k]['src'] = $url;
$data[$k]['preview'] = previewUrl($url);
$data[$k]['extUrl'] = getExtUrl($content);
$data[$k]['cate'] = $v['file_cate'];
$data[$k]['name'] = $v['file_name'];
$data[$k]['size'] = $v['file_size'];
$data[$k]['msg_type'] = $v['type'];
$ext=explode('.',$content);
$data[$k]['ext'] = end($ext);
$data[$k]['user_id_info'] = $userList[$v['from_user']] ?? [];
$data[$k]['download'] = getMainHost().'/filedown/'.encryptIds($v['file_id']);
}
}
}
return success('', $data, $list->total(), $list->currentPage());
}
}

181
app/enterprise/controller/Friend.php

@ -0,0 +1,181 @@
<?php
namespace app\enterprise\controller;
use app\BaseController;
use app\enterprise\model\{Friend as FriendModel,User};
class Friend extends BaseController
{
// 好友申请列表
public function index()
{
$param = $this->request->param();
$map = [];
$map[]=['is_invite','=',1];
$isMine=$param['is_mine'] ?? 0;
if($isMine){
// 我发起的
$map[]=['create_user','=',$this->uid];
}else{
// 我收到的
$map[]=['friend_user_id','=',$this->uid];
}
$data=[];
$model = new FriendModel();
$list = $this->paginate($model->where($map)->order('friend_id desc'));
if ($list) {
$data = $list->toArray()['data'];
$userList = User::matchUser($data, true, ['create_user','friend_user_id'], 120);
foreach ($data as $k => $v) {
$data[$k]['create_user_info'] = $userList[$v['create_user']] ?? [];
$data[$k]['user_id_info'] = $userList[$v['friend_user_id']] ?? [];
$data[$k]['is_group'] = 0;
}
}
return success('', $data,$list->total(),$list->currentPage());
}
// 添加好友
public function add()
{
$param = $this->request->param();
$user_id=$param['user_id'] ?? 0;
if($user_id==$this->uid){
return warning(lang('friend.notAddOwn'));
}
// 查看是否限制了好友上限
if($this->userInfo['friend_limit']!=0 && $this->userInfo['role']==0){
$myFriend=FriendModel::where(['create_user'=>$this->userInfo['user_id']])->count();
// 好友已达上限
if($myFriend>$this->userInfo['friend_limit'] || $this->userInfo['friend_limit']<0){
return warning(lang('friend.limit'));
}
}
$friend=FriendModel::where(['friend_user_id'=>$user_id,'create_user'=>$this->uid])->find();
if($friend){
if($friend->status==1){
return warning(lang('friend.already'));
}elseif($friend->status==2){
return warning(lang('friend.repeatApply'));
}
}
$status=2;
$otherFriend=FriendModel::where(['friend_user_id'=>$this->uid,'create_user'=>$user_id])->find();
if($otherFriend){
if($otherFriend->status>0){
$status=1;
}
}
$model = new FriendModel();
$data=[
'friend_user_id'=>$user_id,
'status'=>$status,
'create_user'=>$this->uid,
'remark'=>$param['remark'],
'is_invite'=>1 // 是否为发起方
];
$model->save($data);
$msg=[
'fromUser'=>[
'id'=>'system',
'displayName'=>lang('friend.new'),
'avatar'=>'',
],
'toContactId'=>'system',
'id'=>uniqid(),
'is_group'=>2,
'content'=>lang('friend.apply'),
'status'=>'succeed',
'sendTime'=>time()*1000,
'type'=>'event',
'fileSize'=>0,
'fileName'=>'',
];
// 发送好友申请
wsSendMsg($user_id,'simple',$msg);
return success(lang('system.addOk'));
}
// 接受或者拒绝好友申请
public function update()
{
$param = $this->request->param();
$friend=FriendModel::find($param['friend_id']);
if(!$friend){
return warning(lang('friend.notApply'));
}
$map=[
'friend_id'=>$param['friend_id']
];
FriendModel::where($map)->update(['status'=>$param['status']]);
// 如果是接收,就添加到好友列表
if($param['status']){
$data=[
'friend_user_id'=>$friend->create_user,
'create_user'=>$this->uid,
];
$newFriend=FriendModel::where($data)->find();
if($newFriend){
FriendModel::where($data)->update(['status'=>1]);
return success(lang('friend.already'));
}else{
$data['status']=1;
FriendModel::create($data);
}
$content=lang('friend.newChat');
$userM=new User;
// 将对方的信息发送给我,把我的信息发送对方
$user=$userM->setContact($friend->create_user,0,'event',$content);
if($user){
wsSendMsg($this->uid,'appendContact',$user);
}
$myInfo=$userM->setContact($this->uid,0,'event',$content);
if($myInfo){
wsSendMsg($friend->create_user,'appendContact',$myInfo);
}
}
return success('');
}
// 删除好友
public function del()
{
$param = $this->request->param();
$map=['friend_user_id'=>$param['id'],'create_user'=>$this->uid];
$friend=FriendModel::where($map)->find();
if(!$friend){
return warning(lang('friend.not'));
}
// 需要删除双方的好友关系
FriendModel::where($map)->delete();
FriendModel::where(['friend_user_id'=>$this->uid,'create_user'=>$param['id']])->delete();
// 性质和删除群聊一样
wsSendMsg($param['id'],'removeGroup',['group_id'=>$this->uid]);
return success(lang('system.delOk'));
}
// 设置好友备注
public function setNickname()
{
$param = $this->request->param();
if(!$param['nickname']){
return warning(lang('system.notNull'));
}
FriendModel::update(['nickname'=>$param['nickname']],['friend_id'=>$param['friend_id']]);
return success(lang('system.editOk'));
}
// 获取最新的一条和申请的总数
public function getApplyMsg(){
$model = new FriendModel();
$map[]=['friend_user_id','=',$this->uid];
$map[]=['status','=',2];
$count=$model->where($map)->count();
return success('', $count);
}
}

528
app/enterprise/controller/Group.php

@ -0,0 +1,528 @@
<?php
namespace app\enterprise\controller;
use app\BaseController;
use app\enterprise\model\{User,Group as GroupModel,GroupUser,Message};
use think\Exception;
use think\facade\Db;
use app\common\controller\Upload;
use GatewayClient\Gateway;
use utils\Str;
class Group extends BaseController
{
protected $setting=['manage' => 0, 'invite' => 1, 'nospeak' => 0];
// 获取联系人列表
public function getAllUser(){
$param=$this->request->param();
$user_ids=isset($param['user_ids'])?$param['user_ids']:[];
$groupId=$param['group_id'] ?? '';
$group_id='';
if($groupId){
$group_id=explode('-',$groupId)[1];
}
$data=User::getAllUser([['status','=',1],['user_id','<>',$this->userInfo['user_id']]],$user_ids,$this->uid,$group_id);
return success('',$data);
}
// 获取群成员
public function groupUserList()
{
$param = $this->request->param();
try {
$group_id = explode('-', $param['group_id'])[1];
$listRows = $this->request->param('limit',0);
$pageSize = $this->request->param('page',1);
$map=['group_id' => $group_id];
if($listRows){
$list=GroupUser::where($map)->order('role asc')->paginate(['list_rows'=>$listRows,'page'=>$pageSize]);
$data=$list->toArray()['data'];
$count=$list->total();
}else{
$data=GroupUser::where($map)->order('role asc')->select();
$count=count($data);
}
$data =User::matchAllUser($data,true,'user_id');
return success('', $data,$count);
} catch (Exception $e) {
return error($e->getMessage());
}
}
// 获取群基本信息
public function groupInfo()
{
$param = $this->request->param();
try {
$jm='qr';
$groupId=$param['group_id'] ?? '';
$groupInfo = explode('-', $groupId);
$group_id=$groupInfo[1];
$group=GroupModel::find($group_id)->toArray();
$userList=User::matchUser($group,false,'owner_id');
$userCount=GroupUser::where(['group_id'=>$group_id])->count();
$userInfo=$userList[$group['owner_id']];
$expire=time()+7*86400;
$token=urlencode(authcode($this->uid.'-'.$group_id,'ENCODE', $jm,7*86400));
$qrUrl=getMainHost().'/scan/g/'.$token;
$group['id']=$groupId;
$group['qrUrl']=$qrUrl;
$group['qrExpire']=date('m月d日',$expire);
$group['userInfo']=$userInfo;
$group['ownerName']=$userInfo['realname'];
$group['groupUserCount']=$userCount;
$group['displayName']=$group['name'];
$group['avatar']=avatarUrl($group['avatar'],$group['name'],$group['group_id'],120);
$group['setting']=$group['setting']?json_decode($group['setting'],true):['manage' => 0, 'invite' => 1, 'nospeak' => 0];
$group['isJoin']=GroupUser::where(['group_id'=>$group_id,'user_id'=>$this->uid])->value('role') ?: 0;
return success('', $group);
} catch (Exception $e) {
return error($e->getMessage());
}
}
// 修改团队名称
public function editGroupName()
{
$param = $this->request->param();
$group_id = explode('-', $param['id'])[1];
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$this->userInfo['user_id']])->value('role');
if($role>2){
return warning(lang('group.notAuth'));
}
GroupModel::where(['group_id' => $group_id])->update(['name' => $param['displayName'],'name_py'=>pinyin_sentence($param['displayName'])]);
$param['editUserName'] = $this->userInfo['realname'];
$action='editGroupName';
event('GroupChange', ['action' => $action, 'group_id' => $group_id, 'param' => $param]);
wsSendMsg($group_id, $action, $param, 1);
return success(lang('system.editOk'));
}
// 添加群成员
public function addGroupUser(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
$group_id = explode('-', $param['id'])[1] ?? 0;
if(!$group_id){
return warning(lang('system.exits'));
}
$groupInfo=GroupModel::where(['group_id'=>$group_id])->find();
if(!$groupInfo){
return warning(lang('system.exits'));
}
$user_ids=$param['user_ids'];
$groupUserCount=GroupUser::where(['group_id'=>$group_id,'status'=>1])->count();
if((count($user_ids) + $groupUserCount) > $this->chatSetting['groupUserMax'] && $this->chatSetting['groupUserMax']!=0){
return warning(lang('group.userLimit',['userMax'=>$this->chatSetting['groupUserMax']]));
}
if(count($user_ids)>20){
return warning(lang('group.inviteLimit',['limit'=>20]));
}
try{
foreach($user_ids as $k=>$v){
$item=[
'group_id'=>$group_id,
'user_id'=>$v,
'role'=>3,
'invite_id'=>$uid
];
$hasUser=GroupUser::where(['group_id'=>$group_id,'user_id'=>$v])->find();
// 查询是否有人
if(!$hasUser){
GroupUser::create($item);
}
}
$url=GroupModel::setGroupAvatar($group_id);
// 更新原来群聊的头像和成员列表
wsSendMsg($group_id,"addGroupUser",['group_id'=>$param['id'],'avatar'=>$url],1);
// 给新成员添加新群聊信息
$user=new User();
$data=$user->setContact($group_id,1,'text',lang('group.invite',['username'=>$this->userInfo['realname']]),$groupInfo);
wsSendMsg($user_ids, 'addGroup', $data);
return success(lang('system.addOk'));
}catch(Exception $e){
return error($e->getMessage());
}
}
// 设置管理员
public function setManager(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
$group_id = explode('-', $param['id'])[1];
$user_id=$param['user_id'];
$role=$param['role'];
if(!GroupUser::checkAuth(['group_id'=>$group_id,'user_id'=>$uid])){
return warning(lang('system.notAuth'));
}
$groupUser=GroupUser::where(['group_id'=>$group_id,'user_id'=>$user_id])->find();
if($groupUser){
$groupUser->role=$role;
$groupUser->save();
$avatar=GroupModel::where(['group_id'=>$group_id])->value('avatar');
$url=avatarUrl($avatar);
wsSendMsg($group_id,"setManager",['group_id'=>$param['id'],'user_id'=>$user_id,'avatar'=>$url],1);
return success(lang('system.settingOk'));
}else{
return warning('');
}
}
// 添加群聊
public function add(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
$user_ids=$param['user_ids'] ?? [];
if($this->chatSetting['groupChat']==0){
return warning(lang('system.notAuth'));
}
// 查看是否限制了群聊创建的个数
if($this->userInfo['group_limit']!=0 && $this->userInfo['role']==0){
$myGroup=GroupModel::where(['owner_id'=>$uid])->count();
// 群聊已达上限
if($myGroup>$this->userInfo['group_limit'] || $this->userInfo['group_limit']<0){
return warning(lang('group.limit'));
}
}
if(count($user_ids)>$this->chatSetting['groupUserMax'] && $this->chatSetting['groupUserMax']!=0){
return warning(lang('group.userLimit',['userMax'=>$this->chatSetting['groupUserMax']]));
}
// 管理员可以单独创建一个人的群
if(count($user_ids)<=1 && $this->userInfo['role']>=2){
return warning(lang('group.atLeast'));
}
// 将自己也加入群聊
$user_ids[]=$uid;
Db::startTrans();
$setting=$this->setting;
try{
$create=[
'create_user'=>$uid,
'owner_id'=>$uid,
'name'=>lang('group.name'),
'name_py'=>"qunliao",
'setting'=>json_encode($setting),
];
$name=$param['name'] ?? '';
if($name){
$create['name']=$name;
$create['name_py']=pinyin_sentence($name);
}
$group=new GroupModel();
$group->save($create);
$group_id=$group->group_id;
$data=[];
array_unique($user_ids);
sort($user_ids);
foreach($user_ids as $k=>$v){
$info=[
'user_id'=>$v,
'invite_id'=>$uid,
'status'=>1,
'role'=>3,
'group_id'=>$group_id
];
if($v==$uid){
$info['invite_id']=0;
$info['role']=1;
}
$data[]=$info;
}
$groupUser=new GroupUser();
$groupUser->saveAll($data);
$url=GroupModel::setGroupAvatar($group_id);
$groupInfo=[
'displayName'=>$create['name'],
'owner_id'=>$create['owner_id'],
'role'=>3,
'name_py'=>$create['name_py'],
'id'=>'group-'.$group_id,
'avatar'=>avatarUrl($url,$create['name'],$group_id,120),
'is_group'=>1,
'lastContent'=>lang('group.add',['username'=>$this->userInfo['realname']]),
'lastSendTime'=>time()*1000,
'index'=>"[2]".lang('group.name'),
'is_notice'=>1,
'is_top'=>0,
'setting'=>$setting,
];
Message::create([
'from_user'=>$uid,
'to_user'=>$group_id,
'content'=>str_encipher(lang('group.add',['username'=>''])),
'type'=>'event',
'is_group'=>1,
'is_read'=>1,
'is_last'=>1,
'chat_identify'=>'group-'.$group_id
]);
wsSendMsg($user_ids, 'addGroup', $groupInfo);
Db::commit();
$groupInfo['role']=1;
return success('',$groupInfo);
}catch(Exception $e){
Db::rollback();
return error($e->getMessage());
}
}
// 移除成员
public function removeUser(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
$group_id = explode('-', $param['id'])[1];
$user_id=$param['user_id'];
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->value('role');
if($role>2 && $user_id!=$uid){
return warning(lang('system.notAuth'));
}
$groupUser=GroupUser::where(['group_id'=>$group_id,'user_id'=>$user_id])->find();
if(($groupUser && $groupUser['role']>$role) || $user_id==$uid){
GroupUser::destroy($groupUser->id);
Gateway::$registerAddress = config('gateway.registerAddress');
$clientIds=Gateway::getClientIdByUid($user_id);
// 解绑群组
if($clientIds){
foreach($clientIds as $k=>$v){
Gateway::leaveGroup($v, $group_id);
}
}
}else{
return warning(lang('system.notAuth'));
}
$url=GroupModel::setGroupAvatar($group_id);
wsSendMsg($group_id,"removeUser",['group_id'=>$param['id'],'avatar'=>$url,'user_id'=>$user_id],1);
return success(lang('system.delOk'));
}
// 设置群成员禁言
public function setNoSpeak(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
$group_id = explode('-', $param['id'])[1];
$user_id=$param['user_id'];
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->value('role');
if($role>2 && $user_id!=$uid){
return warning(lang('system.notAuth'));
}
$groupUser=GroupUser::where(['group_id'=>$group_id,'user_id'=>$user_id])->find();
if(!$groupUser){
return warning(lang('system.notAuth'));
}
$noSpeakTimer=$param['noSpeakTimer'] ?? 0;
$noSpeakList=[600,3600,10800,86400];
$noSpeakDay=$param['noSpeakDay'] ?? 1;
$noSpeakTime=$noSpeakDay*86400;
if($noSpeakTimer>0){
$noSpeakTime=$noSpeakList[$noSpeakTimer-1];
}
wsSendMsg($group_id,"setNoSpeak",['group_id'=>$param['id'],'user_id'=>$user_id],1);
GroupUser::where(['group_id'=>$group_id,'user_id'=>$user_id])->update(['no_speak_time'=>time()+$noSpeakTime]);
return success(lang('system.success'));
}
// 解散团队
public function removeGroup(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
$group_id = explode('-', $param['id'])[1];
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->value('role');
if($role>1){
return warning(lang('system.notAuth'));
}
Db::startTrans();
try{
// 删除团队成员
GroupUser::where(['group_id'=>$group_id])->delete();
// 删除团队
GroupModel::destroy($group_id);
wsSendMsg($group_id,"removeGroup",['group_id'=>$param['id']],1);
Db::commit();
return success('');
}catch(Exception $e){
Db::rollback();
return error($e->getMessage());
}
}
// 设置公告
public function setNotice(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
// 公告内容检测服务
event('GreenText',['content'=>$param['notice'],'service'=>"comment_detection"]);
$group_id = explode('-', $param['id'])[1];
if($param['notice']==''){
return warning(lang('system.notNull'));
}
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->value('role');
if($role>2){
return warning(lang('system.notAuth'));
}
GroupModel::update(['notice'=>$param['notice']],['group_id'=>$group_id]);
$msg=[
'id'=>\utils\Str::getUuid(),
'user_id'=>$uid,
'content'=>'<b>'.lang('group.notice').':</b>&nbsp;@'.lang('group.all').'<br/>'.$param['notice'].'<br/>',
'toContactId'=>$param['id'],
'sendTime'=>time()*1000,
'type'=>'text',
'is_group'=>1,
'status'=>'succeed',
'fromUser'=>$this->userInfo,
'at'=>[0]
];
$message=new Message();
$data = $message->sendMessage($msg,$this->globalConfig);
if (!$data) {
return warning($message->getError());
}
return success('');
}
// 群聊设置
public function groupSetting(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
$group_id = explode('-', $param['id'])[1];
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->value('role');
if($role!=1){
return warning(lang('system.notAuth'));
}
$setting=json_encode($param['setting']);
GroupModel::update(['setting'=>$setting],['group_id'=>$group_id]);
wsSendMsg($group_id,"groupSetting",['group_id'=>$param['id'],'setting'=>$param['setting']],1);
return success('');
}
//生成群聊头像
protected function setGroupAvatar($group_id){
$userList=GroupUser::where('group_id',$group_id)->limit(9)->column('user_id');
$userList=User::where('user_id','in',$userList)->select()->toArray();
$imgList=[];
$dirPath=app()->getRootPath().'public/temp';
foreach($userList as $k=>$v){
if($v['avatar']){
$imgList[]=avatarUrl($v['avatar'],$v['realname'],$v['user_id']);
}else{
$imgList[]=circleAvatar($v['realname'],80,$v['user_id'],1,$dirPath);
}
}
$groupId='group_'.$group_id;
$path=$dirPath.'/'.$groupId.'.jpg';
$a = getGroupAvatar($imgList,1,$path);
$url='';
if($a){
$upload=new Upload();
$newPath=$upload->uploadLocalAvatar($path,[],$groupId);
if($newPath){
GroupModel::where('group_id',$group_id)->update(['avatar'=>$newPath]);
$url=avatarUrl($newPath);
}
}
// 删除目录下的所有文件
$files = glob($dirPath . '/*'); // 获取目录下所有文件路径
foreach ($files as $file) {
if (is_file($file)) { // 如果是文件则删除
unlink($file);
}
}
return $url;
}
// 加入群
public function joinGroup(){
$param = $this->request->param();
$uid=$this->userInfo['user_id'];
try{
$group_id = explode('-', $param['group_id'])[1];
$inviteUid=$param['inviteUid'] ?? '';
$groupUserCount=GroupUser::where(['group_id'=>$group_id,'status'=>1])->count();
$groupUser=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->find();
$groupInfo=GroupModel::where(['group_id'=>$group_id])->find();
if(!$groupInfo){
return warning(lang('group.exist'));
}
if($groupUser){
return warning(lang('group.alreadyJoin'));
}
if(($groupUserCount+1) > $this->chatSetting['groupUserMax'] && $this->chatSetting['groupUserMax']!=0){
return warning(lang('group.userLimit',['userMax'=>$this->chatSetting['groupUserMax']]));
}
// 加入者的名称
$groupInfo['joinerName']=$this->userInfo['realname'];
GroupUser::joinGroup($uid,$inviteUid,$groupInfo);
return success(lang('system.joinOk'));
}catch(Exception $e){
return error($e->getMessage());
}
}
// 更换群主
public function changeOwner()
{
$user_id = $this->request->param('user_id');
$id = $this->request->param('id');
$group_id = explode('-', $id)[1];
$uid=$this->userInfo['user_id'];
$group=GroupModel::where('group_id',$group_id)->find();
if(!$group){
return warning(lang('group.exist'));
}
$user=User::where('user_id',$user_id)->find();
if(!$user){
return warning(lang('user.exist'));
}
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->value('role');
if($role>1){
return warning(lang('system.notAuth'));
}
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 clearMessage()
{
$id = $this->request->param('id');
$group_id = explode('-', $id)[1];
$uid=$this->userInfo['user_id'];
$group=GroupModel::where('group_id',$group_id)->find();
if(!$group){
return warning(lang('group.exist'));
}
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$uid])->value('role');
// 如果是群主或者后台管理员才有权限
if($role>1 && $this->userInfo['role']==0){
return warning(lang('system.notAuth'));
}
Db::startTrans();
try{
// 删除所有消息
Message::where(['chat_identify'=>$id])->delete();
// 该群聊的所有未读置为0
GroupUser::where('group_id',$group_id)->update(['unread'=>0]);
wsSendMsg($group_id,"clearMessage",['group_id'=>'group-'.$group_id],1);
Db::commit();
return success('');
}catch (\Exception $e){
Db::rollback();
return warning('');
}
}
}

835
app/enterprise/controller/Im.php

@ -0,0 +1,835 @@
<?php
namespace app\enterprise\controller;
use app\BaseController;
use think\facade\Request;
use think\facade\Db;
use app\enterprise\model\{User, Message, GroupUser, Friend,Group};
use GatewayClient\Gateway;
use Exception;
use League\Flysystem\Util;
use think\facade\Cache;
class Im extends BaseController
{
protected $fileType = ['file', 'image','video','voice','emoji'];
// 获取联系人列表
public function getContacts()
{
$data = User::getUserList([['status', '=', 1], ['user_id', '<>', $this->userInfo['user_id']]], $this->userInfo['user_id']);
$count=Friend::where(['status'=>2,'friend_user_id'=>$this->uid])->count();
$time=Friend::where(['friend_user_id'=>$this->uid,'is_invite'=>1])->order('create_time desc')->value('create_time');
return success('', $data,$count,$time*1000);
}
// 获取单个人员的信息
public function getContactInfo(){
$id = $this->request->param('id');
$is_group = is_string($id) ? 1 : 0;
$user=new User;
$data=$user->setContact($id,$is_group);
if(!$data){
return warning($user->getError());
}
return success('',$data);
}
//发送消息
public function sendMessage()
{
$param = $this->request->param();
$param['user_id'] = $this->userInfo['user_id'];
$message=new Message();
$data = $message->sendMessage($param,$this->globalConfig);
if ($data) {
return success('', $data);
} else {
return warning($message->getError());
}
}
//转发消息
public function forwardMessage()
{
$param = $this->request->param();
$userIds=$param['user_ids'] ?? [];
if(!$userIds || count($userIds)>5){
return warning(lang('im.forwardLimit',['count'=>5]));
}
$msg_id=$param['msg_id'] ?? 0;
$message=Message::find($msg_id);
if(!$message){
return warning(lang('im.exist'));
}
$message=$message->toArray();
$userInfo=$this->userInfo;
try{
$is_group=0;
$error=0;
$chatSetting=$this->chatSetting;
foreach($userIds as $k=>$v){
$msgInfo=$message;
if(strpos($v,'group')!==false){
$is_group=1;
}else{
$is_group=0;
}
if($is_group==0 && $chatSetting['simpleChat']==0){
$error++;
continue;
}
$msgInfo['id']=\utils\Str::getUuid();
$msgInfo['status']='successd';
$msgInfo['user_id']=$userInfo['user_id'];
$msgInfo['sendTime']=time()*1000;
$msgInfo['toContactId']=$v;
$msgInfo['content']=str_encipher($msgInfo['content'],false);
$msgInfo['fromUser']=[
'id'=>$userInfo['user_id'],
'avatar'=>avatarUrl($userInfo['avatar'],$userInfo['realname'],$userInfo['user_id'],120),
'displayName'=>$userInfo['realname']
];
$msgInfo['is_group']=$is_group;
$message=new Message();
$data=$message->sendMessage($msgInfo,$this->globalConfig);
if(!$data){
return warning($message->getError());
}
}
}catch(\Exception $e){
return error($e->getMessage());
}
if ($error) {
$text=lang('im.forwardRule',['count'=>$error]);
} else {
$text=lang('im.forwardOk');
}
return success($text);
}
// 获取用户信息
public function getUserInfo()
{
$user_id = $this->request->param('user_id');
$user=User::find($user_id);
if(!$user){
return error(lang('user.exist'));
}
$user->avatar=avatarUrl($user->avatar,$user->realname,$user->user_id,120);
// 账号前面截取3位,后面截取两位,中间星号展示
$user->account=substr($user->account, 0, 3).'******'.substr($user->account, -2, 2);
// 查询好友关系
$friend=Friend::where(['friend_user_id'=>$user_id,'create_user'=>$this->userInfo['user_id'],'status'=>1])->find();
$user->friend=$friend ? : '';
$location='';
if($user->last_login_ip){
$location=implode(" ", \Ip::find($user->last_login_ip));
}
$user->location=$location;
$user->password='';
return success('', $user);
}
// 搜索用户
public function searchUser(){
$keywords=$this->request->param('keywords','');
if(!$keywords){
return success('',[]);
}
$map=['status'=>1,'account'=>$keywords];
$list=User::where($map)->field(User::$defaultField)->where([['account','<>',$this->userInfo['account']]])->select()->toArray();
if($list){
$ids=array_column($list,'user_id');
$friendList=Friend::getFriend([['create_user','=',$this->uid],['friend_user_id','in',$ids]]);
foreach($list as $k=>$v){
$list[$k]['avatar']=avatarUrl($v['avatar'],$v['realname'],$v['user_id'],120);
$list[$k]['friend']=$friendList[$v['user_id']] ?? '';
}
}
return success('', $list);
}
// 获取系统所有人员加搜索
public function userList(){
$keywords=$this->request->param('keywords','');
$listRows=$this->request->param('limit',20);
$page=$this->request->param('page',1);
$map=['status'=>1];
$field="user_id,realname,avatar";
if(!$keywords){
$list=User::where($map)->field($field)->order('user_id asc')->limit(20)->paginate(['list_rows'=>$listRows,'page'=>$page]);;
if($list){
$list=$list->toArray()['data'];
}
}else{
$list=User::where($map)->field($field)->where([['account','<>',$this->userInfo['account']]])->whereLike('account|realname|name_py','%'.$keywords.'%')->select()->toArray();
}
if($list){
foreach($list as $k=>$v){
$list[$k]['avatar']=avatarUrl($v['avatar'],$v['realname'],$v['user_id'],120);
$list[$k]['id']=$v['user_id'];
}
}
return success('', $list);
}
// 获取聊天记录
public function getMessageList()
{
$param = $this->request->param();
$is_group = isset($param['is_group']) ? $param['is_group'] : 0;
// 如果toContactId是数字,绝对是单聊
$is_group = is_numeric($param['toContactId']) ? 0 : $is_group;
// 设置当前聊天消息为已读
$chat_identify = $this->setIsRead($is_group, $param['toContactId']);
$type = isset($param['type']) ? $param['type'] : '';
$is_at = isset($param['is_at']) ? $param['is_at'] : '';
$map = ['chat_identify' => $chat_identify, 'status' => 1];
$where = [];
if ($type && $type != "all") {
$map['type'] = $type;
} else {
if (isset($param['type'])) {
$where[] = ['type', '<>', 'event'];
}
}
// 群聊查询入群时间以后的消息
if($is_group==1){
$group_id = explode('-', $param['toContactId'])[1];
$group=Group::where(['group_id'=> $group_id])->find();
if($group && $group['setting']){
$groupSetting=json_decode($group['setting'],true);
$history=$groupSetting['history'] ?? false;
// 如果开启了历史记录才可以查看所有记录,否者根据进群时间查询记录
if(!$history){
$createTime=GroupUser::where(['group_id'=> $group_id,'user_id'=>$this->userInfo['user_id']])->value('create_time');
$where[] = ['create_time', '>=', $createTime ? : 0];
}
}
}
$keywords = isset($param['keywords']) ? $param['keywords'] : '';
if ($keywords && in_array($type, ['text', 'all'])) {
$where[] = ['content', 'like', '%' . $keywords . '%'];
}
// 如果是查询@数据
if($is_at){
$atList=Db::name('message')->where($map)->where($where)->whereFindInSet('at',$this->userInfo['user_id'])->order('msg_id desc')->select()->toArray();
if($atList){
$data = $this->recombileMsg($atList,false);
Message::setAtread($data,$this->userInfo['user_id']);
return success('', $data, count($data));
}else{
return success('', [], 0);
}
}
$listRows = $param['limit'] ?: 20;
$pageSize = $param['page'] ?: 1;
$last_id = $param['last_id'] ?? 0;
if($last_id){
$where[]=['msg_id','<',$last_id];
}
$list = Message::getList($map, $where, 'msg_id desc', $listRows, $pageSize);
$data = $this->recombileMsg($list);
// 如果是群聊并且是第一页消息,需要推送@数据给用户
if($param['is_group']==1 && $param['page']==1){
$isPush=Cache::get('atMsgPush'.$chat_identify) ?? '';
$atList=Db::name('message')->where(['chat_identify'=>$chat_identify,'is_group'=>1])->whereFindInSet('at',$this->userInfo['user_id'])->order('msg_id desc')->select()->toArray();
$msgIda=array_column($atList,'msg_id');
// 如果两次推送at数据的列表不一样,则推送
if($isPush!=json_encode($msgIda)){
$atData=$this->recombileMsg($atList,false);
wsSendMsg($this->userInfo['user_id'],'atMsgList',[
'list'=>$atData,
'count'=>count($atData),
'toContactId'=>$param['toContactId']
]);
Cache::set('atMsgPush'.$chat_identify,json_encode($msgIda),60);
}
}
// 如果是消息管理器则不用倒序
if (!isset($param['type'])) {
$data = array_reverse($data);
}
return success('', $data, $list->total());
}
protected function recombileMsg($list,$isPagination=true)
{
$data = [];
$userInfo = $this->userInfo;
if ($list) {
$listData = $isPagination ? $list->toArray()['data'] : $list;
$userList = User::matchUser($listData, true, 'from_user', 120);
foreach ($listData as $k => $v) {
// 屏蔽已删除的消息
if ($v['del_user']) {
$delUser = explode(',', $v['del_user']);
if (in_array($userInfo['user_id'], $delUser)) {
unset($list[$k]);
continue;
// $v['type']="event";
// $v['content']="删除了一条消息";
}
}
$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']];
// 处理撤回的消息
if ($v['type'] == "event" && $v['is_undo']==1) {
if ($v['from_user'] == $userInfo['user_id']) {
$content = lang('im.you'). $content;
} elseif ($v['is_group'] == 1) {
$content = $fromUser['realname'] . $content;
} else {
$content = lang('im.other') . $content;
}
}
$toContactId=$v['is_group'] ==1 ? 'group-'.$v['to_user'] : $v['to_user'];
$atList=($v['at'] ?? null) ? explode(',',$v['at']): [];
$data[] = [
'msg_id' => $v['msg_id'],
'id' => $v['id'],
'status' => "succeed",
'type' => $v['type'],
'sendTime' => $v['create_time'] * 1000,
'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,
'extUrl'=>$ext,
'extends'=>is_string($v['extends'])?json_decode($v['extends'],true) : $v['extends']
];
}
}
return $data;
}
// 设置当前窗口的消息默认为已读
public function setMsgIsRead()
{
$param = $this->request->param();
// 判断是否是一个二维数组
if (is_array($param['messages'][0] ?? '')) {
$messages=$param['messages'];
} else {
$messages=[$param['messages']];
}
$this->setIsRead($param['is_group'], $param['toContactId'],$messages);
if (!$param['is_group']) {
wsSendMsg($param['fromUser'], 'isRead', $messages, 0);
}
return success('');
}
// 设置消息已读
protected function setIsRead($is_group, $to_user,$messages=[])
{
if ($is_group==1) {
$chat_identify = $to_user;
$toContactId = explode('-', $to_user)[1];
// 将@消息放到定时任务中逐步清理
if($messages){
Message::setAtRead($messages,$this->userInfo['user_id']);
}
// 更新群里面我的所有未读消息为0
GroupUser::editGroupUser(['user_id' => $this->userInfo['user_id'], 'group_id' => $toContactId], ['unread' => 0]);
} else if($is_group==0) {
$chat_identify = chat_identify($this->userInfo['user_id'], $to_user);
// 更新我的未读消息为0
Message::update(['is_read' => 1], [['chat_identify', '=', $chat_identify], ['to_user', '=', $this->userInfo['user_id']]]);
// 告诉对方我阅读了消息
wsSendMsg($to_user, 'readAll', ['toContactId' => $this->userInfo['user_id']]);
} else if($is_group==2){
$chat_identify = $to_user;
}
return $chat_identify;
}
// 聊天设置
public function setting()
{
$param = $this->request->param();
if ($param) {
User::where(['user_id' => $this->userInfo['user_id']])->update(['setting' => $param]);
return success('');
}
return warning('');
}
// 撤回消息
public function undoMessage()
{
$param = $this->request->param();
$id = $param['id'];
$message = Message::where(['id' => $id])->find();
if ($message) {
// 如果时间超过了2分钟也不能撤回
$createTime=is_string($message['create_time']) ? strtotime($message['create_time']) : $message['create_time'];
if(time()-$createTime>120 && $message['is_group']==0){
return warning(lang('im.redoLimitTime',['limit'=>2]));
}
$text = lang('im.redo');
$fromUserName = lang('im.other');
$toContactId = $message['to_user'];
if ($message['is_group'] == 1) {
$fromUserName = $this->userInfo['realname'];
$toContactId = explode('-', $message['chat_identify'])[1];
// 如果是群聊消息撤回,需要判断是否是群主或者管理员,如果是则可以撤回
if($message['from_user']!=$this->userInfo['user_id']){
$groupUser=GroupUser::where(['user_id'=>$this->userInfo['user_id'],'group_id'=>$toContactId])->find();
if(!$groupUser || !in_array($groupUser['role'],[1,2])){
return warning(lang('system.notAuth'));
}
$text=lang('im.manageRedo');
}
}
$message->content = str_encipher($text);
$message->type = 'event';
$message->is_undo = 1;
//@的数据清空
$message->at = '';
$message->save();
$info = $message->toArray();
// $data = $info;
$data['content'] = $fromUserName . $text;
$data['sendTime'] = $createTime * 1000;
$data['id'] = $info['id'];
$data['from_user'] = $info['from_user'];
$data['msg_id'] = $info['msg_id'];
$data['status'] = $info['status'];
$data['type'] = 'event';
$data['is_last'] = $info['is_last'];
$data['toContactId'] = $message['is_group'] == 1 ? $info['chat_identify'] : $toContactId;
$data['isMobile'] = $this->request->isMobile() ? 1 : 0;
wsSendMsg($toContactId, 'undoMessage', $data, $info['is_group']);
if($info['is_group']==0){
// 给自己也发一份推送,多端同步
$data['content'] =lang('im.you'). $text;
wsSendMsg($this->userInfo['user_id'], 'undoMessage', $data, $info['is_group']);
}
return success('');
} else {
return warning();
}
}
// 删除消息
public function removeMessage()
{
$param = $this->request->param();
$id = $param['id'];
$map = ['id' => $id];
$message = Message::where($map)->find();
if ($message) {
$message->del_user = $this->userInfo['user_id'];
if ($message['is_group'] == 1) {
if ($message['del_user']) {
$message->del_user .= ',' . $this->userInfo['user_id'];
}
} else {
if ($message['del_user'] > 0) {
$message->where($map)->delete();
return success(lang('system.delOk'));
}
}
$message->save();
return success('');
} else {
return warning('');
}
}
// 消息免打扰
public function isNotice()
{
$param = $this->request->param();
$user_id = $this->userInfo['user_id'];
$id = $param['id'];
if ($param['is_group'] == 1) {
$group_id = explode('-', $param['id'])[1];
GroupUser::update(['is_notice' => $param['is_notice']], ['user_id' => $user_id, 'group_id' => $group_id]);
} else {
$map = ['create_user' => $user_id, 'friend_user_id' => $id];
$friend = Friend::where($map)->find();
try {
if ($friend) {
$friend->is_notice = $param['is_notice'];
$friend->save();
} else {
$info = [
'create_user' => $user_id,
'friend_user_id' => $id,
'is_notice' => $param['is_notice']
];
Friend::create($info);
}
return success('');
} catch (Exception $e) {
return error($e->getMessage());
}
}
wsSendMsg($user_id,"setIsNotice",['id'=>$id,'is_notice'=>$param['is_notice'],'is_group'=>$param['is_group']]);
return success('');
}
// 设置聊天置顶
public function setChatTop()
{
$param = $this->request->param();
$user_id = $this->userInfo['user_id'];
$is_group = $param['is_group'] ?: 0;
$id = $param['id'];
try {
if ($is_group == 1) {
$group_id = explode('-', $param['id'])[1];
GroupUser::update(['is_top' => $param['is_top']], ['user_id' => $user_id, 'group_id' => $group_id]);
} else {
$map = ['create_user' => $user_id, 'friend_user_id' => $id];
$friend = Friend::where($map)->find();
if ($friend) {
$friend->is_top = $param['is_top'];
$friend->save();
} else {
$info = [
'create_user' => $user_id,
'friend_user_id' => $id,
'is_top' => $param['is_top']
];
Friend::create($info);
}
}
wsSendMsg($user_id,"setChatTop",['id'=>$id,'is_top'=>$param['is_top'],'is_group'=>$is_group]);
return success('');
} catch (Exception $e) {
return error($e->getMessage());
}
}
// 删除聊天
public function delChat()
{
$param = $this->request->param();
$user_id = $this->userInfo['user_id'];
$is_group = $param['is_group'] ?: 0;
$id = $param['id'];
if(!$is_group){
$chat_identify=chat_identify($user_id,$id);
}else{
return success('');
}
Message::where(['chat_identify' => $chat_identify])->update(['is_last' => 0]);
return success('');
}
// 向用户发送消息
public function sendToMsg(){
$param=$this->request->param();
$toContactId=$param['toContactId'];
$type=$param['type'];
$status=$param['status'];
$event=$param['event'] ?? 'calling';
if($event=='calling'){
$status=3;
}
$sdp=$param['sdp'] ?? '';
$iceCandidate=$param['iceCandidate'] ?? '';
$callTime=$param['callTime'] ?? '';
$msg_id=$param['msg_id'] ?? '';
$id=$param['id'] ?? '';
$code=($param['code'] ?? '') ?: 901;
// 如果该用户不在线,则发送忙线
Gateway::$registerAddress = config('gateway.registerAddress');
if(!Gateway::isUidOnline($toContactId)){
$toContactId=$this->userInfo['user_id'];
$code=907;
$event='busy';
sleep(1);
}
switch($code){
case 902:
$content=lang('webRtc.cancel');
break;
case 903:
$content=lang('webRtc.refuse');
break;
case 905:
$content=lang('webRtc.notConnected');
break;
case 906:
$content=lang('webRtc.duration',['time'=>date("i:s",$callTime)]);
break;
case 907:
$content=lang('webRtc.busy');
break;
case 908:
$content=lang('webRtc.other');
break;
default:
$content=$type==1 ?lang('webRtc.video') : lang('webRtc.audio');
break;
}
switch($event){
case 'calling':
$content=$type==1 ?lang('webRtc.video'): lang('webRtc.audio');
break;
case 'acceptRtc':
$content=lang('webRtc.answer');
break;
case 'iceCandidate':
$content=lang('webRtc.exchange');
break;
}
$userInfo=$this->userInfo;
$userInfo['id']=$userInfo['user_id'];
$user = new User();
$data=[
'id'=>$id,
'msg_id'=>$msg_id,
'sendTime'=>time()*1000,
'toContactId'=>$toContactId,
'content'=>$content,
'type'=>'webrtc',
'status'=>'succeed',
'is_group'=>0,
'is_read'=>0,
'fromUser'=>$userInfo,
'at'=>[],
'extends'=>[
'type'=>$type, //通话类型,1视频,0语音。
'status'=>$status, //,1拨打方,2接听方
'event'=>$event,
'callTime'=>$callTime,
'sdp'=>$sdp,
'code'=>$code, //通话状态:呼叫901,取消902,拒绝903,接听904,未接通905,接通后挂断906,忙线907,其他端操作908
'iceCandidate'=>$iceCandidate,
'isMobile'=>$this->request->isMobile() ? 1 : 0,
]
];
if($event=='calling'){
$chat_identify=chat_identify($userInfo['id'],$toContactId);
$msg=[
'from_user'=>$userInfo['id'],
'to_user'=>$toContactId,
'id'=>$id,
'content'=>str_encipher($content),
'chat_identify'=>$chat_identify,
'create_time'=>time(),
'type'=>$data['type'],
'is_group'=>0,
'is_read'=>0,
'extends'=>$data['extends'],
];
$message=new Message();
$message->update(['is_last'=>0],['chat_identify'=>$chat_identify]);
$message->save($msg);
$msg_id=$message->msg_id;
$data['msg_id']=$msg_id;
// 将接收人设置为发送人才能定位到该消息
$data['toContactId']=$userInfo['id'];
$data['toUser']=$toContactId;
}elseif($event=='hangup'){
$message=Message::where(['id'=>$id])->find();
if(!$message){
return error(lang('webRtc.fail'));
}
if($message){
$message->content=str_encipher($content);
$extends=$message->extends;
$extends['code']=$code;
$extends['callTime']=$callTime;
$message->extends=$extends;
$message->save();
}
}
wsSendMsg($toContactId,'webrtc',$data);
$wsData=$data;
if(in_array($event,['calling','acceptRtc','hangup'])){
if(in_array($event,['acceptRtc','hangup'])){
$data['extends']['event']='otherOpt'; //其他端操作
}
$data['toContactId']=$toContactId;
$data['contactInfo']=$user->setContact($toContactId,0,'webrtc',$content) ? : [];
wsSendMsg($userInfo['id'],'webrtc',$data);
}
return success('',$wsData);
}
// 修改密码
public function editPassword()
{
if(env('app.demon_mode',false)){
return warning(lang('system.demoMode'));
}
$user_id = $this->userInfo['user_id'];
$user=User::find($user_id);
if(!$user){
return warning(lang('user.exist'));
}
$account=$user->account;
$code=$this->request->param('code','');
$originalPassword = $this->request->param('originalPassword', '');
if($code){
if(Cache::get($account)!=$code){
return warning(lang('user.codeErr'));
}
}elseif($originalPassword){
if(password_hash_tp($originalPassword,$user->salt)!= $user->password){
return warning(lang('user.passErr'));
}
}else{
return warning(lang('system.parameterError'));
}
try{
$password = $this->request->param('password','');
if($password){
$salt=$user->salt;
$user->password= password_hash_tp($password,$salt);
}
$user->save();
return success(lang('system.editOk'));
}catch (\Exception $e){
return error(lang('system.editFail'));
}
}
// 修改用户信息
public function updateUserInfo(){
try{
$data = $this->request->param();
$user=User::find($this->uid);
if(!$user){
return warning(lang('user.exist'));
}
// 接入用户名检测服务
event('GreenText',['content'=>$data['realname'],'service'=>"nickname_detection"]);
// 个性签名检测服务
event('GreenText',['content'=>$data['motto'],'service'=>"comment_detection"]);
$user->realname =$data['realname'];
$user->email =$data['email'];
$user->motto=$data['motto'];
$user->sex =$data['sex'];
$user->name_py= pinyin_sentence($data['realname']);
$user->save();
return success(lang('system.editOk'), $data);
}catch (\Exception $e){
return error($e->getMessage());
}
}
// 修改账户
public function editAccount(){
if(env('app.demon_mode',false)){
return warning(lang('system.demoMode'));
}
$code=$this->request->param('code','');
$newCode=$this->request->param('newCode','');
$account=$this->request->param('account','');
$isUser=User::where('account',$account)->find();
if($isUser){
return warning(lang('user.already'));
}
$user=User::find($this->uid);
if(!$user){
return warning(lang('user.exist'));
}
// 如果已经认证过了,则需要验证验证码
if($user->is_auth){
if(Cache::get($user->account)!=$code){
return warning(lang('user.codeErr'));
}
}
if(Cache::get($account)!=$newCode){
return warning(lang('user.newCodeErr'));
}
try{
$user->account=$account;
$user->is_auth=1;
$user->save();
return success(lang('system.editOk'));
}catch (\Exception $e){
return error(lang('system.editFail'));
}
}
// 阅读@消息
public function readAtMsg(){
$param = $this->request->param();
$atList=Db::name('message')->where(['chat_identify'=>$param['toContactId'],'is_group'=>1])->whereFindInSet('at',$this->userInfo['user_id'])->order('msg_id desc')->select();
$atData=$this->recombileMsg($atList,false);
Message::setAtRead($atData,$this->userInfo['user_id']);
// $message=Message::where('msg_id',$param['msg_id'])->select();
// $atList=($message ?? null) ? explode(',',$message): [];
// // 两个数组取差集
// $newAtList = array_diff($atList, [$this->userInfo['user_id']]);
// Message::where('msg_id',$param['msg_id'])->update(['at'=>implode(',',$newAtList)]);
return success('');
}
// 获取系统公告
public function getAdminNotice(){
$data=Message::where(['chat_identify'=>'admin_notice'])->order('msg_id desc')->find();
$extends=$data['extends'] ?? [];
if(!$extends){
$extends['title']='';
}
$createTime=$data['create_time'] ?? 0;
if(!$createTime){
$extends['create_time']=$createTime;
}else{
$extends['create_time']=is_string($data['create_time']) ? strtotime($data['create_time']) : $data['create_time'];
}
return success('',$extends);
}
// 双向删除消息
public function delMessage(){
$param = $this->request->param();
$id = $param['id'];
if(!$this->globalConfig['chatInfo']['dbDelMsg']){
return warning(lang('system.noAuth'));
}
$message = Message::where(['id' => $id])->find();
if ($message) {
if($message['from_user']!=$this->userInfo['user_id']){
return warning(lang('system.noAuth'));
}
Message::where(['id' => $id])->delete();
// 如果是最后一条消息,需要将上一条设置为最后一条
if($message['is_last']){
Message::where(['chat_identify'=>$message['chat_identify']])->order('msg_id desc')->limit(1)->update(['is_last'=>1]);
}
$toContactId = $message['to_user'];
if ($message['is_group'] == 1) {
$toContactId = explode('-', $message['chat_identify'])[1];
}
wsSendMsg($toContactId, 'delMessage', $message, $message['is_group']);
return success('');
} else {
return warning(lang('im.exist'));
}
}
}

49
app/enterprise/listener/GroupChange.php

@ -0,0 +1,49 @@
<?php
namespace app\enterprise\listener;
use app\enterprise\model\{Group,User,Message};
use app\manage\model\Config;
use GatewayClient\Gateway;
// 监听群聊变更事件
class GroupChange
{
public function handle(Group $group,User $user,$data){
Gateway::$registerAddress = config('gateway.registerAddress');
$groupInfo=$data['param'];
// 如果是扫码进群,表示手动操作,手动操作需要触发客户端ID推送
if($data['action'] == 'joinGroup'){
Gateway::joinGroup(request()->header('clientId'),$data['group_id']);
}elseif($data['action'] == 'autoCreateGroup'){
// 自动创建的群通知群主,如果在线则推送
if(Gateway::isUidOnline($groupInfo['owner_uid'])){
wsSendMsg([$groupInfo['owner_uid']], 'addGroup', $groupInfo);
}
}elseif($data['action'] == 'editGroupName'){
return;
}
$uid=$groupInfo['owner_uid'] ?? 1;
$userInfo=$user->field('user_id,realname,avatar')->where(['user_id'=>$uid])->find();
if($userInfo){
$userInfo=$userInfo->toArray();
$userInfo['id']=$userInfo['user_id'];
$userInfo['avatar']=avatarUrl($userInfo['avatar'],$userInfo['realname'],$userInfo['user_id']);
// 发送入群事件
$msg=[
'id'=>\utils\Str::getUuid(),
'user_id'=>$uid,
'content'=>lang('group.join',['username'=>$groupInfo['joinerName'] ?? 'xxx ']),
'toContactId'=>'group-'.$data['group_id'],
'sendTime'=>time()*1000,
'type'=>'event',
'is_group'=>1,
'status'=>'succeed',
'fromUser'=>$userInfo,
'at'=>[],
'action'=>$data['action'],
];
Message::sendMsg($msg,1);
}
return true;
}
}

4
app/enterprise/middleware.php

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

12
app/enterprise/model/Emoji.php

@ -0,0 +1,12 @@
<?php
/**
* raingad IM [ThinkPHP6]
* @author xiekunyu <raingad@foxmail.com>
*/
namespace app\enterprise\model;
use app\BaseModel;
class Emoji extends BaseModel
{
}

14
app/enterprise/model/File.php

@ -0,0 +1,14 @@
<?php
/**
* raingad IM [ThinkPHP6]
* @author xiekunyu <raingad@foxmail.com>
*/
namespace app\enterprise\model;
use app\BaseModel;
class File extends BaseModel
{
protected $pk="file_id";
}

28
app/enterprise/model/Friend.php

@ -0,0 +1,28 @@
<?php
/**
* raingad IM [ThinkPHP6]
* @author xiekunyu <raingad@foxmail.com>
*/
namespace app\enterprise\model;
use app\BaseModel;
use think\facade\Db;
class Friend extends BaseModel
{
protected $pk="friend_id";
public static function getFriend($map){
$list=self::where($map)->select();
$data=[];
if($list){
$list=$list->toArray();
foreach($list as $k=>$v){
$data[$v['friend_user_id']]=$v;
}
}
return $data;
}
}

60
app/enterprise/model/Group.php

@ -0,0 +1,60 @@
<?php
/**
* raingad IM [ThinkPHP6]
* @author xiekunyu <raingad@foxmail.com>
*/
namespace app\enterprise\model;
use app\BaseModel;
use think\facade\Db;
use app\common\controller\Upload;
class Group extends BaseModel
{
protected $pk="group_id";
// 获取我的团队
public static function getMyGroup($map){
return Db::name('group_user')
->alias('gu')
->field('gr.group_id,gr.avatar,gr.name as displayName,gu.unread,gr.name_py,gr.owner_id,gr.notice,gu.role,gu.is_notice,gu.is_top,gr.setting')
->join('group gr','gu.group_id=gr.group_id','left')
->where($map)
->select();
}
//生成群聊头像
public static function setGroupAvatar($group_id){
$userList=GroupUser::where('group_id',$group_id)->limit(9)->column('user_id');
$userList=User::where('user_id','in',$userList)->select()->toArray();
$imgList=[];
$dirPath=app()->getRootPath().'public/temp';
foreach($userList as $k=>$v){
if($v['avatar']){
$imgList[]=avatarUrl($v['avatar'],$v['realname'],$v['user_id']);
}else{
$imgList[]=circleAvatar($v['realname'],80,$v['user_id'],1,$dirPath);
}
}
$groupId='group_'.$group_id;
$path=$dirPath.'/'.$groupId.'.jpg';
$a = getGroupAvatar($imgList,1,$path);
$url='';
if($a){
$upload=new Upload();
$newPath=$upload->uploadLocalAvatar($path,[],$groupId);
if($newPath){
Group::where('group_id',$group_id)->update(['avatar'=>$newPath]);
$url=avatarUrl($newPath);
}
}
// 删除目录下的所有文件
$files = glob($dirPath . '/*'); // 获取目录下所有文件路径
foreach ($files as $file) {
if (is_file($file)) { // 如果是文件则删除
unlink($file);
}
}
return $url;
}
}

66
app/enterprise/model/GroupUser.php

@ -0,0 +1,66 @@
<?php
/**
* raingad IM [ThinkPHP6]
* @author xiekunyu <raingad@foxmail.com>
*/
namespace app\enterprise\model;
use app\BaseModel;
use think\facade\Db;
class GroupUser extends BaseModel
{
protected $pk="id";
// 编辑团队信息
public static function editGroupUser($map,$data){
return self::where($map)->update($data);
}
// 获取团队成员列表
public static function getGroupUser($map,$listRows,$pageSize=1){
if($listRows){
$list=self::where($map)->order('role asc')->paginate(['list_rows'=>$listRows,'page'=>$pageSize]);
$data=$list->toArray()['data'];
}else{
$data=self::where($map)->order('role asc')->select();
}
return User::matchAllUser($data,true,'user_id');
}
// 验证权限
public static function checkAuth($map,$role=1){
$info=self::where($map)->find()->toArray();
if($info['role']<=$role){
return true;
}else{
return false;
}
}
// 加入群聊,发送加入消息
public static function joinGroup($uid,$inviteId,$groupInfo,$action='joinGroup'){
$group_id=$groupInfo['group_id'];
GroupUser::create([
'user_id'=>$uid,
'invite_id'=>$inviteId,
'status'=>1,
'role'=>$action=='autoCreateGroup' ? 1 : 3,
'group_id'=>$group_id,
]);
$url=Group::setGroupAvatar($group_id);
event('GroupChange', ['action' => $action, 'group_id' => $group_id, 'param' => $groupInfo]);
wsSendMsg($group_id,"addGroupUser",['group_id'=>$group_id,'avatar'=>$url],1);
return true;
}
// 获取群管理
public static function getGroupManage($group_id){
$list=self::where([['group_id','=',$group_id],['role','<',3],['status','=',1]])->select()->toArray();
$data=[];
foreach($list as $k=>$v){
$data[$v['user_id']]=$v['role'];
}
return $data;
}
}

263
app/enterprise/model/Message.php

@ -0,0 +1,263 @@
<?php
/**
* raingad IM [ThinkPHP6]
* @author xiekunyu <raingad@foxmail.com>
*/
namespace app\enterprise\model;
use app\BaseModel;
use think\facade\Db;
use think\facade\Cache;
class Message extends BaseModel
{
protected $pk="msg_id";
protected $json = ["extends"];
protected $jsonAssoc = true;
protected static $fileType=['file','image','video','voice','emoji'];
// 添加聊天记录
public static function addData($data){
return Db::name('message')->insert($data);
}
// 更新消息状态
public static function editData($update,$map){
return Db::name('message')->where($map)->update($update);
}
// 查询聊天记录
public static function getList($map,$where,$sort,$listRows,$pageSize){
$list= Db::name('message')
->where($map)
->where($where)
->order($sort)
->paginate(['list_rows'=>$listRows,'page'=>$pageSize]);
return $list;
}
// 发送消息
public function sendMessage($param,$globalConfig=false){
$is_group = $param['is_group'] ?? 0;
$uid=self::$uid;
if($param['toContactId']==-1){
$is_group=0;
}
// 如果是系统账号,直接禁言
if($is_group>1){
$this->error=lang('im.forbidChat');
return false;
}
$sendInterval = $globalConfig['chatInfo']['sendInterval'] ?? 0;
// 如果设置了消息频率则验证
if ($sendInterval) {
if (Cache::has('send_' . $uid)) {
$this->error=lang('im.sendTimeLimit',['time'=>$sendInterval]);
return false;
}
}
if($param['type']=='text'){
// 限制文字内容长度
$text = strip_tags($param['content']);
$textLen = mb_strlen($text);
if ($textLen > 2048) {
$this->error=lang('im.msgContentLimit') . $textLen;
return false;
}
$param['content'] = preg_link($param['content']);
// 接入聊天内容检测服务
event('GreenText',['content'=>$param['content'],'service'=>"chat_detection"]);
}
$chatSetting = $globalConfig['chatInfo'];
if($param['toContactId']!=-1){
if ($is_group == 0) {
$kefuUser=$chatSetting['autoAddUser']['user_ids'] ?? [];
$manageUser=User::where([['status','=',1],['role','>',0]])->column('user_id');
$kefu=array_unique(array_merge($kefuUser,$manageUser));
$csUid = self::$userInfo['cs_uid'] ?? 0;
$manage=false;
// 发送者和接受者是客服或者管理员也可以发送消息
if(in_array($uid,$kefu) || in_array($param['toContactId'],$kefu)){
$manage=true;
}
if($chatSetting['simpleChat'] == 0 && !$manage){
$this->error=lang('im.forbidChat');
return false;
}
// 如果是单聊,并且是社区模式和不是自己的客服、需要判断是否是好友
if ($globalConfig['sysInfo']['runMode'] == 2 && $csUid != $param['toContactId'] && !$manage) {
// 判断我是不是对方的客服
$cus = User::where(['user_id' => $param['toContactId']])->value('cs_uid');
if ($cus != $uid) {
$friend = Friend::where(['friend_user_id' => $uid, 'create_user' => $param['toContactId']])->find();
if (!$friend) {
$this->error=lang('im.notFriend');
return false;
}
$otherFriend = Friend::where(['friend_user_id' => $param['toContactId'], 'create_user' => $uid])->find();
if (!$otherFriend) {
$this->error=lang('im.friendNot');
return false;
}
}
}
}else{
// 群聊必须群成员才能发送消息
$group_id = explode('-', $param['toContactId'])[1] ?? '';
$toContactId=$group_id;
if(!$group_id){
$this->error=lang('system.parameterError');
return false;
}
if(!self::nospeak($group_id,$uid)){
return shutdown(lang('group.notSpeak'));
}
// 群聊必须群成员才能发送消息
$groupUser=GroupUser::where(['user_id'=>$uid,'status'=>1,'group_id'=>$group_id,'delete_time'=>0])->find();
if(!$groupUser){
$this->error = lang('group.notCustom');
return false;
}
if($groupUser['no_speak_time']>time()){
$this->error = lang('group.notSpeak',['time'=>date('Y-m-d H:i:s',$groupUser['no_speak_time'])]);
return false;
}
}
}
if ($sendInterval) {
Cache::set('send_' . $uid, time(), $sendInterval);
}
return self::sendMsg($param,$is_group);
}
//实际发送消息
public static function sendMsg($param,$is_group=0){
$uid=self::$uid ?: ($param['user_id'] ?? 1);
$toContactId=$param['toContactId'];
$manage=[];
if($is_group==1){
$group_id = explode('-', $param['toContactId'])[1] ?? '';
$chat_identify=$toContactId;
$toContactId=$group_id;
$manage=GroupUser::getGroupManage($group_id);
}else{
$chat_identify=chat_identify($param['user_id'],$toContactId);
}
$fileSzie=isset($param['file_size'])?$param['file_size']:'';
$fileName=isset($param['file_name'])?$param['file_name']:'';
$ossUrl=getDiskUrl();
// 如果是转发图片文件的消息,必须把域名去除掉
$content=$param['content'];
if(in_array($param['type'],self::$fileType)){
if(strpos($param['content'],$ossUrl)!==false){
$content=str_replace($ossUrl,'',$param['content']);
}
}
$param['content']=$content;
$atList=($param['at'] ?? null) ? array_map('intval', $param['at']): [];
// 如果at里面有0,代表@所有人
if($atList && in_array(0,$atList)){
$atList=GroupUser::where([['group_id','=',$toContactId],['status','=',1],['user_id','<>',$param['user_id']]])->column('user_id');
}
$at=$atList ? implode(',',$atList) : null;
$data=[
'from_user'=>$param['user_id'],
'to_user'=>$toContactId,
'id'=>$param['id'],
'content'=>str_encipher($param['content'],true),
'chat_identify'=>$chat_identify,
'create_time'=>time(),
'type'=>$param['type'],
'is_group'=>$toContactId==-1 ? 3 : $is_group,
'is_read'=>$is_group ? 1 : 0,
'file_id'=>$param['file_id'] ?? 0,
"file_cate"=>$param['file_cate'] ?? 0,
'file_size'=>$fileSzie,
'file_name'=>$fileName,
'at'=>$at,
'pid'=>$param['pid'] ?? 0,
'extends'=>($param['extends'] ?? null) ? $param['extends'] : null,
];
$message=new self();
$message->update(['is_last'=>0],['chat_identify'=>$chat_identify]);
$message->save($data);
// 拼接消息推送
$type=$is_group?'group':'simple';
$sendData=$param;
$sendData['status']='succeed';
$sendData['at']=$atList;
$sendData['msg_id']=$message->msg_id;
$sendData['is_read']=0;
$sendData['to_user']=$toContactId;
$sendData['role']=$manage[self::$uid] ?? 3;
$sendData['sendTime']=(int)$sendData['sendTime'];
//这里单聊中发送对方的消息,对方是接受状态,自己是对方的联系人,要把发送对象设置为发送者的ID。
if($is_group){
$sendData['toContactId']=$param['toContactId'];
// 将团队所有成员的未读状态+1
GroupUser::editGroupUser([['group_id','=',$toContactId],['user_id','<>',$uid]],['unread'=>Db::raw('unread+1')]);
}else{
$sendData['toContactId']=$uid;
}
$sendData['fromUser']['id']=(int)$sendData['fromUser']['id'];
$sendData['fileSize']=$fileSzie;
$sendData['fileName']=$fileName;
if(in_array($sendData['type'],self::$fileType)){
$sendData['content']=getFileUrl($sendData['content']);
if($sendData['type']=='image'){
$pre=1;
}else{
$pre=2;
}
$sendData['preview']=previewUrl($sendData['content'],$pre);
$sendData['extUrl']=getExtUrl($sendData['content']);
$sendData['download']= $sendData['file_id'] ? getMainHost().'/filedown/'.encryptIds($sendData['file_id']) : '';
}
if($is_group==0){
$toContactId=[$toContactId,$param['user_id']];
}
$sendData['toUser']=$param['toContactId'];
$user=new User();
// 将聊天窗口的联系人信息带上,方便临时会话
$sendData['contactInfo']=$user->setContact($sendData['toContactId'],$is_group,$sendData['type'],$sendData['content']);
// 向发送方发送消息
wsSendMsg($toContactId,$type,$sendData,$is_group);
$sendData['toContactId']=$param['toContactId'];
return $sendData;
}
// 群禁言
public static function nospeak($group_id,$user_id){
$group=Group::find($group_id);
if($group->owner_id==$user_id){
return true;
}
if($group->setting){
$setting=json_decode($group->setting,true);
$nospeak=isset($setting['nospeak'])?$setting['nospeak']:0;
$role=GroupUser::where(['group_id'=>$group_id,'user_id'=>$user_id])->value('role');
if($nospeak==1 && $role>2){
return false;
}elseif($nospeak==2 && $role!=1){
return false;
}
}
return true;
}
// 将消息中的@用户加入到atListQueue中
public static function setAtread($messages,$user_id){
foreach($messages as $k=>$v){
if(!isset($v['at'])){
continue;
}
if($v['at'] && in_array($user_id,$v['at'])){
$atListQueue=Cache::get("atListQueue");
$atListQueue[$v['msg_id']][]=$user_id;
Cache::set("atListQueue",$atListQueue);
}
}
}
}

515
app/enterprise/model/User.php

@ -0,0 +1,515 @@
<?php
/**
* raingad IM [ThinkPHP6]
* @author xiekunyu <raingad@foxmail.com>
*/
namespace app\enterprise\model;
use GatewayClient\Gateway;
use app\BaseModel;
use think\facade\Db;
use think\facade\Request;
use think\model\concern\SoftDelete;
use app\manage\model\Config;
use thans\jwt\facade\JWTAuth;
class User extends BaseModel
{
use SoftDelete;
protected $pk = "user_id";
public static $defaultField = 'user_id,realname,realname as displayName,account,avatar,name_py,email,last_login_ip';
protected $json = ['setting'];
protected $jsonAssoc = true;
// 系统人员或者其他静态人员
public static function staticUser(){
return [
'adminNotice'=>[
'id'=>'admin_notice',
'displayName'=>'系统通知',
'avatar'=>getMainHost().'/static/common/img/notice.png',
'name_py'=>'xitongtongzhi',
],
'fileTransfer'=>[
'id'=>-1,
'displayName'=>'我的收藏',
'avatar'=>getMainHost().'/static/common/img/file_transfer.png',
'name_py'=>'wodeshoucang',
]
];
}
public function getUid()
{
return self::$uid;
}
//查询用户信息
public static function getUserInfo($map=[])
{
if(!$map){
return self::$userInfo;
}
$data = self::where($map)->find();
if ($data) {
$data = $data->toArray();
}
return $data;
}
/**
* 刷新用户token 之前token将被拉黑
* 修改用户数据后 调用该方法 并返回前台更新token
* @param array $info 用户信息
* @param string $terminal 客户端标识
* @return string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public static function refreshToken($info,$terminal)
{
$info = str_encipher(json_encode($info),true, config('app.aes_token_key'));
$authToken = 'bearer '.JWTAuth::builder(['info' => $info, 'terminal' => $terminal]);
return $authToken;
}
// 获取所有用户列表
public static function getAllUser($map, $user_ids = [],$user_id,$group_id = 0)
{
$field = self::$defaultField;
$list=[];
if($group_id){
$groupUser=GroupUser::where([['group_id','=',$group_id],['role','<>',1],['status','=',1]])->column('user_id');
if($groupUser){
$list=User::where([['user_id','in',$groupUser]])->field($field)->select()->toArray();
}
}else{
$config=Config::getSystemInfo();
// 如果是社区模式,就只查询自己的好友,如果是企业模式,就查询所有用户
if($config['sysInfo']['runMode']==1){
$list = self::where($map)->field($field)->select()->toArray();
}else{
$friendList = Friend::getFriend(['create_user' => $user_id,'status'=>1]);
$userList = array_keys($friendList);
$list = self::where($map)->where('user_id', 'in', $userList)->field($field)->select()->toArray();
}
}
foreach ($list as $k => $v) {
$list[$k]['disabled'] = false;
$list[$k]['avatar'] = avatarUrl($v['avatar'], $v['realname'], $v['user_id']);
if ($user_ids) {
if (in_array($v['user_id'], $user_ids)) {
$list[$k]['disabled'] = true;
}
}
}
return $list;
}
//查询用户列表
public static function getUserList($map, $user_id, $field = "")
{
if (!$field) {
$field = self::$defaultField;
}
$config=Config::getSystemInfo();
// 如果是社区模式,就只查询自己的好友,如果是企业模式,就查询所有用户
if($config['sysInfo']['runMode']==1){
$friendList = Friend::getFriend(['create_user' => $user_id]);
$list = self::where($map)->field($field)->select();
}else{
$friendList = Friend::getFriend(['create_user' => $user_id,'status'=>1]);
$userList = array_keys($friendList);
// 将专属客服设置为好友
$csUid=request()->userInfo['cs_uid'] ?? 0;
if($csUid){
$userList[]=$csUid;
}
$list = self::where($map)->where('user_id', 'in', $userList)->field($field)->select();
}
$list_chart = chartSort($list, 'realname', false, 'index');
// 查询未读消息
$unread = Db::name('message')
->field('from_user,count(msg_id) as unread')
->where([['to_user', '=', $user_id], ['is_read', '=', 0], ['is_group', '=', 0]])
->group('from_user')
->select();
// 查询最近的联系人
$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();
// 查询群聊
$group = Group::getMyGroup(['gu.user_id' => $user_id, 'gu.status' => 1]);
if ($group) {
$group = $group->toArray();
$group_ids = arrayToString($group, 'group_id');
$getGroupLastMsg = Db::name('message')->field($msgField)->where([['to_user', 'in', $group_ids], ['is_group', '=', 1], ['is_last', '=', 1]])->select();
$getAtMsg=Db::name('message')->field($msgField)->where([['to_user', 'in', $group_ids], ['is_group', '=', 1]])->whereFindInSet('at',$user_id)->select();
// halt($getAtMsg);
foreach ($group as $k => $v) {
$setting = $v['setting'] ? json_decode($v['setting'], true) : ['manage' => 0, 'invite' => 1, 'nospeak' => 0];
$group_id = 'group-' . $v['group_id'];
$group[$k]['id'] = $group_id;
$group[$k]['account'] = $group_id;
$group[$k]['avatar'] = avatarUrl($v['avatar'], $v['displayName'], $v['group_id'], 120);
$group[$k]['name_py'] = $v['name_py'];
$group[$k]['owner_id'] = $v['owner_id'];
$group[$k]['role'] = $v['role'];
$group[$k]['is_group'] = 1;
$group[$k]['setting'] = $setting;
$group[$k]['index'] = "[2]群聊";
$group[$k]['realname'] = $v['displayName'] . " [群聊]";
$group[$k]['is_notice'] = $v['is_notice'];
$group[$k]['is_top'] = $v['is_top'];
$group[$k]['is_online'] = 1;
$group[$k]['is_at'] = 0;
if ($getGroupLastMsg) {
foreach ($getGroupLastMsg as $key=>$val) {
if ($val['to_user'] == $v['group_id']) {
$group[$k]['type'] =$val['type'];
$group[$k]['lastContent'] = str_encipher($val['lastContent'],false);
$group[$k]['lastSendTime'] = $val['lastSendTime'] * 1000;
// 已经赋值了删除掉提升下次循环的性能
unset($getGroupLastMsg[$key]);
break;
}
}
}
if($getAtMsg){
foreach ($getAtMsg as $key=> $val) {
if ($val['to_user'] == $v['group_id']) {
++$group[$k]['is_at'];
// 已经赋值了删除掉提升下次循环的性能
unset($getAtMsg[$key]);
}
}
}
}
}
try{
Gateway::$registerAddress = config('gateway.registerAddress');
$onlineList=Gateway::getAllUidList();
}catch(\Exception $e){
$onlineList=[];
}
foreach ($list_chart as $k => $v) {
// 是否有消息通知或者置顶聊天
$friend = isset($friendList[$v['user_id']]) ? $friendList[$v['user_id']] : [];
$list_chart[$k]['id'] = $v['user_id'];
$list_chart[$k]['displayName'] = ($friend['nickname'] ?? '') ? : $v['realname'];
$list_chart[$k]['name_py'] = $v['name_py'];
$list_chart[$k]['avatar'] = avatarUrl($v['avatar'], $v['realname'], $v['user_id'], 120);
$list_chart[$k]['lastContent'] = '';
$list_chart[$k]['unread'] = 0;
$list_chart[$k]['lastSendTime'] = time() * 1000;
$list_chart[$k]['is_group'] = 0;
$list_chart[$k]['setting'] = [];
$list_chart[$k]['is_at'] = 0;
$list_chart[$k]['last_login_ip'] = $v['last_login_ip'];
$list_chart[$k]['location'] =$v['last_login_ip'] ? implode(" ", \Ip::find($v['last_login_ip'])) : "未知";
$is_online=0;
if(isset($onlineList[$v['user_id']])){
$is_online=1;
}
$list_chart[$k]['is_online'] = $is_online;
$is_top = 0;
$is_notice = 1;
if ($friend) {
$is_top = $friend['is_top'];
$is_notice = $friend['is_notice'];
}
$list_chart[$k]['is_top'] = $is_top;
$list_chart[$k]['is_notice'] = $is_notice;
if ($unread) {
foreach ($unread as $val) {
if ($val['from_user'] == $v['user_id']) {
$list_chart[$k]['unread'] = $val['unread'];
break;
}
}
}
if ($lasMsgList) {
foreach ($lasMsgList as $val) {
if ($val['from_user'] == $v['user_id'] || $val['to_user'] == $v['user_id']) {
$content = str_encipher($val['lastContent'],false);
// 屏蔽已删除的消息
if ($val['del_user']) {
$delUser = explode(',', $val['del_user']);
if (in_array($user_id, $delUser)) {
$content = "";
}
}
$list_chart[$k]['type'] = $val['type'];
$list_chart[$k]['lastContent'] = $content;
$list_chart[$k]['lastSendTime'] = $val['lastSendTime'] * 1000;
break;
}
}
}
}
// 合并群聊和联系人
$data = array_merge($list_chart, $group);
// 合并助手消息和聊天消息
$helper=self::otherChat($user_id);
$data=array_merge($data,$helper);
return $data;
}
// 获取机器人聊天消息
public static function otherChat($uid){
$staticList=self::staticUser();
$adminNotice=$staticList['adminNotice'];
$fileTransfer=$staticList['fileTransfer'];
$count=Message::where(['chat_identify'=>$adminNotice['id']])->count();
$createTime=Message::where(['chat_identify'=>$adminNotice['id']])->order('id desc')->value('create_time');
$sendTime=0;
if($createTime){
$sendTime=is_string($createTime) ? strtotime($createTime) : $createTime;
}
$chat_identify=chat_identify($uid,$fileTransfer['id']);
$fileLast=Message::where(['is_last'=>1,'chat_identify'=>$chat_identify])->find();
$fileSendTime=$fileLast['create_time'] ?? '';
$content =$fileLast['content'] ?? '';
$friend=Friend::where(['create_user'=>$uid,'friend_user_id'=>$fileTransfer['id']])->find();
$notice=[
[
'id'=>$adminNotice['id'],
'user_id'=>$adminNotice['id'],
'displayName'=>$adminNotice['displayName'],
'realname'=>$adminNotice['displayName'],
'name_py'=>$adminNotice['name_py'],
'avatar'=>$adminNotice['avatar'],
'lastContent'=>$sendTime ? $count.'条公告' :'',
'unread'=>0,
'lastSendTime'=>$sendTime * 1000,
'is_group'=>2,
'setting'=>[],
'type'=>'text',
'is_top'=>0,
'is_notice'=>1,
'is_online'=>0,
'index'=>"[1]系统消息",
],
[
'id'=>$fileTransfer['id'],
'user_id'=>$fileTransfer['id'],
'displayName'=>$fileTransfer['displayName'],
'realname'=>$fileTransfer['displayName'],
'name_py'=>$fileTransfer['name_py'],
'avatar'=>$fileTransfer['avatar'],
'lastContent'=> str_encipher($content,false) ?: '传输你的文件',
'unread'=>0,
'lastSendTime'=>((is_string($fileSendTime) ? strtotime($fileSendTime) : $fileSendTime) * 1000) ?: time() * 1000,
'is_group'=>3,
'setting'=>[],
'type'=>$fileLast['type'] ?? 'text',
'is_top'=>$friend['is_top'] ?? 0,
'is_notice'=>$friend['is_notice'] ?? 1,
'is_online'=>0,
'index'=>"[1]系统消息",
],
];
return $notice;
}
public static function getList($map)
{
return self::field(self::$defaultField)->where($map)->select();
}
// 匹配用户列表信息(返回用户信息)
public static function matchUser($data, $many = false, $field = 'user_id', $cs = 80)
{
if ($many) {
$idr = 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);
}
$userList = self::where([['user_id', 'in', $idr]])->field(self::$defaultField)->select()->toArray();
$friend = Friend::where([['friend_user_id', 'in', $idr],['create_user','=',self::$uid]])->field('friend_user_id,nickname')->select()->toArray();
$list = [];
foreach ($userList as $v) {
$v['avatar'] = avatarUrl($v['avatar'], $v['realname'], $v['user_id'], $cs);
$v['id'] = $v['user_id'];
if($friend){
foreach($friend as $key=>$val){
if($val['friend_user_id']==$v['user_id']){
$v['realname']=$val['nickname'] ? : $v['displayName'];
break;
}
}
}
$list[$v['user_id']] = $v;
}
return $list;
}
// 匹配用户列表信息(返回data)
public static function matchAllUser($data, $many = false, $field = 'user_id', $key = "userInfo", $cs = 80)
{
if ($many) {
$idr = arrayToString($data, $field);
$userList = self::getList([['user_id', 'in', $idr]]);
foreach ($data as $k => $v) {
foreach ($userList as $vv) {
if ($v[$field] == $vv['user_id']) {
$data[$k][$key] = [
'id' => $vv['user_id'],
'displayName' => $vv['realname'],
'account' => $vv['account'],
'name_py' => $vv['name_py'],
'avatar' => avatarUrl($vv['avatar'], $vv['realname'], $vv['user_id'], $cs),
];
}
}
}
} else {
$user = self::getUserInfo(['user_id' => $data[$field]]);
$data[$key] = [
'id' => $user['user_id'],
'displayName' => $user['realname'],
'account' => $user['account'],
'name_py' => $user['name_py'],
'avatar' => avatarUrl($user['avatar'], $user['realname'], $user['user_id']),
];
}
return $data;
}
// 将id转换成联系人信息
public function setContact($id,$is_group=0,$type='text',$content='',$contactInfo=null){
$data=[
'id'=>$id,
'lastContent'=>$content,
'unread'=>0,
'lastSendTime'=> time() * 1000,
'is_group'=>$is_group,
'is_top'=>0,
'is_notice'=>1,
'is_top'=>0,
'is_at'=>0,
'setting'=>[],
'type'=>$type,
'location'=>'',
];
if($is_group==0){
$user=$contactInfo ?: User::where('user_id',$id)->find();
if(!$user){
$this->error=lang('user.exist');
return false;
}
$user->avatar=avatarUrl($user->avatar,$user->realname,$user->user_id,120);
// 查询好友关系
$friend= self::$userInfo ? Friend::where(['friend_user_id'=>$id,'create_user'=>self::$userInfo['user_id']])->find() : [];
$data['displayName'] = ($friend['nickname'] ?? '') ? : $user['realname'];
$data['avatar'] = avatarUrl($user['avatar'], $user['realname'], $user['user_id'], 120);
$data['location'] =$user['last_login_ip'] ? implode(" ", \Ip::find($user['last_login_ip'])) : "未知";
$data['name_py'] = $user['name_py'];
}else{
$group_id=is_numeric($id) ? $id : (explode('-',$id)[1] ?? 0);
$group=$contactInfo ?: Group::where(['group_id'=>$group_id])->find();
if(!$group){
$this->error=lang('group.exist');
return false;
}
$data['id'] = 'group-'.$group_id;
$data['displayName'] = $group['name'];
$data['avatar'] = avatarUrl($group['avatar'], $group['name'], $group['group_id'], 120);
$data['name_py'] = $group['name_py'];
$data['setting'] = $group['setting'];
$data['role'] = 3;
}
$data['index'] =getFirstChart($data['displayName']);
return $data;
}
// 验证账号的合法性
public function checkAccount(&$data){
$user_id=$data['user_id'] ?? 0;
if($user_id){
$user=self::find($data['user_id']);
if(!$user){
$this->error='账户不存在';
return false;
}
if($user->user_id==1 && self::$uid!=1){
$this->error='超管账户只有自己才能修改';
return false;
}
$other=self::where([['account','=',$data['account']],['user_id','<>',$data['user_id']]])->find();
if($other){
$this->error='账户已存在';
return false;
}
}else{
$user=self::where('account',$data['account'])->find();
if($user){
$this->error='账户已存在';
return false;
}
}
$config=Config::getSystemInfo();
$regauth=$config['sysInfo']['regauth'] ?? 0;
$acType=\utils\Regular::check_account($data['account']);
switch($regauth){
case 1:
if($acType!=1){
$this->error='当前系统只允许账号为手机号!';
return false;
}
break;
case 2:
if($acType!=2){
$this->error='当前系统只允许账号为邮箱!';
return false;
}
break;
case 3:
// 验证账号是否为手机号或者邮箱
if(!$acType){
$this->error='账户必须为手机号或者邮箱';
return false;
}
break;
default:
break;
}
$data['is_auth'] =$regauth ? 1 : 0;
$email=$data['email'] ?? '';
if($data['is_auth'] && $acType==2 && !$email){
$data['email'] =$data['account'];
}
return true;
}
}

10
app/enterprise/model/WechatMoments.php

@ -0,0 +1,10 @@
<?php
namespace app\enterprise\model;
use app\BaseModel;
class WechatMoments extends BaseModel
{
}

24
app/enterprise/validate/User.php

@ -0,0 +1,24 @@
<?php
/**
* lvzhe [a web admin based ThinkPHP5]
*/
namespace app\enterprise\validate;
use think\Validate;
class User extends Validate
{
protected $rule = [
'account|帐号' => 'require',
'password|密码' => 'require',
'captcha|验证码' => 'require|captcha',
'oldpassword|旧密码' => 'require',
'repassword|重复密码' => 'require',
];
protected $scene = [
'password' => ['password', 'oldpassword', 'repassword'],
'login' => ['account', 'password'],
];
}

184
app/index/controller/Index.php

@ -0,0 +1,184 @@
<?php
namespace app\index\controller;
use app\enterprise\model\{File,Group,User};
use think\facade\View;
use app\manage\model\Config;
use app\Request;
class Index
{
public function index()
{
if (!file_exists(PACKAGE_PATH . "install.lock")) {
return redirect(url('index/install/index'));
}
// 自动跳转后无法注册
// if(request()->isMobile() && !env('app.demon_mode',false)){
// return redirect("/h5");
// }
return redirect("/index.html");
}
public function view()
{
$url=request()->param('src');
$suffix=explode('.',$url);
$ext=$suffix[count($suffix)-1];
return View::fetch('',[
'url' => $url,
'ext'=>$ext,
'name'=>lang('file.preview')
]);
}
// 头像生成
public function avatar()
{
circleAvatar(input('str'), input('s') ?: 80, input('uid'));die;
}
// 文件下载
public function download()
{
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false) {
throw new \think\Exception(lang('file.browserDown'),400);
}
$param = request()->param();
$file_id = $param['file_id'] ?? 0;
if (!$file_id) {
throw new \think\Exception(lang('system.parameterError'), 502);
}
try {
$file_id = decryptIds($file_id);
} catch (\Exception $e) {
throw new \think\Exception($e->getMessage(), 400);
}
$file = File::find($file_id);
if (!$file) {
throw new \think\Exception(lang('file.exist'),404);
}
$file = $file->toArray();
// 兼容本地文件下载
$fileUrl=getDiskUrl();
if($fileUrl==getMainHost()){
$url=rtrim(public_path(),'/').$file['src'];
}else{
$url= getFileUrl($file['src']);
}
return \utils\File::download($url, $file['name'] . '.' . $file['ext'], $file['size'], $file['ext']);
}
// 扫码获取信息
public function scanQr(){
$param=request()->param();
$action=$param['action'] ?? '';
$token=$param['token'] ?? '';
$realToken=$param['realToken'] ?? '';
if(request()->isPost() && $action && $token && $realToken){
$actions=[
'g'=>'group',
'u'=>'user',
];
$a=$actions[$action] ?? '';
if(!$a){
return warning(lang('scan.failure'));
}
return $this->$a($param);
}else{
return $this->index();
}
}
protected function group($param)
{
$token=authcode(urldecode($param['realToken']),"DECODE", 'qr');
if(!$token){
return warning(lang('scan.failure'));
}
$groupInfo=explode('-',$token);
$uid=$groupInfo[0];
$group_id=$groupInfo[1];
$group=Group::find($group_id);
if($group){
$group=$group->toArray();
$group['avatar']=avatarUrl($group['avatar'],$group['name'],$group_id,120);
$group['invite_id']=$uid;
$group['id']='group-'.$group_id;
$group['action']='groupInfo';
return success('',$group);
}else{
return warning(lang('scan.failure'));
}
}
protected function user($param)
{
$id=decryptIds($param['token']);
if(!$id){
return warning(lang('scan.failure'));
}
$user=User::where(['user_id'=>$id])->field(User::$defaultField)->find();
if($user){
$user=$user->toArray();
$user['avatar']=avatarUrl($user['avatar'],$user['realname'],$user['user_id'],120);
$user['id']=$user['user_id'];
$user['action']='userInfo';
return success('',$user);
}else{
return warning(lang('scan.failure'));
}
}
// app下载页
public function downApp(){
// echo request()->domain(true);
$downAppUrl=env('app.downApp_url','');
if($downAppUrl){
return redirect($downAppUrl);
}
$config=Config::where('name','sysInfo')->value('value');
$andriod=getAppDowmUrl('andriod');
$winUrl=getAppDowmUrl('windows');
$macUrl=getAppDowmUrl('mac');
$client=[
'andriod_appid'=>env('app.andriod_appid',''),
'andriod_webclip'=>env('app.andriod_webclip','') ? : $andriod,
'ios_appid'=>env('app.ios_appid',''),
'ios_webclip'=>env('app.ios_webclip',''),
'win_webclip'=>env('app.win_webclip','') ? : $winUrl,
'mac_webclip'=>env('app.mac_webclip','') ? : $macUrl
];
$noUrl=false;
if(!$client['andriod_appid'] && !$client['andriod_webclip'] && !$client['ios_appid'] && !$client['ios_webclip']){
$noUrl=true;
}
View::assign('noUrl',$noUrl);
View::assign('client',$client);
View::assign('config',$config);
return View::fetch();
}
// 下载APP
public function downloadApp(){
$platform=request()->param('platform','windows');
$config=config('version.'.$platform);
$name=config('version.app_name');
if($platform=='andriod'){
$packageName=$name."_Setup_".$config['version'].".apk";
}elseif($platform=='mac'){
$packageName=$name."_Setup_".$config['version'].".dmg";
}else{
$packageName=$name."_Setup_".$config['version'].".exe";
}
$file=PACKAGE_PATH . $packageName;
if(is_file($file)){
return \utils\File::download($file, $packageName);
}else{
return shutdown(lang('file.exist'));
}
}
}

568
app/index/controller/Install.php

@ -0,0 +1,568 @@
<?php
// +----------------------------------------------------------------------
// | Description: 安装
// +----------------------------------------------------------------------
// | Author: xiekunyu | raingad@foxmail.com
// +----------------------------------------------------------------------
namespace app\index\controller;
use think\facade\Request;
use think\facade\Db;
use think\facade\View;
use think\facade\Config;
use Env;
class Install
{
// private $count = 100;
// private $now = 0;
protected $status=1;
public function _initialize()
{
/*防止跨域*/
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, authKey, sessionId");
}
/**
* [index 安装步骤]
* @author Michael_xu
* @param
*/
public function index()
{
$protocol = strpos(strtolower($_SERVER['SERVER_PROTOCOL']), 'https') === false ? 'http' : 'https';
if (file_exists(PACKAGE_PATH . "install.lock")) {
echo "<meta http-equiv='content-type' content='text/html; charset=UTF-8'> <script>alert('请勿重复安装!');location.href='".$protocol."://".$_SERVER["HTTP_HOST"]."';</script>";
die();
}
if (!file_exists(PUBLIC_PATH . "sql/database.sql")) {
echo "<meta http-equiv='content-type' content='text/html; charset=UTF-8'> <script>alert('缺少必要的数据库文件!');location.href='".$protocol."://".$_SERVER["HTTP_HOST"]."';</script>";
die();
}
return View::fetch('index');
}
// 检测环境配置和文件夹读写权限
public function getEnv()
{
$data = [];
$data['env'] = self::checkEnv();
$data['dir'] = self::checkDir();
$data['version'] = $this->version();
$data['status'] = $this->status;
return success('',$data);
}
//版本
public function version()
{
$res = include(CONF_PATH.'app.php');
$data=[
'VERSION'=>$res['app_version'],
'RELEASE'=>$res['app_release'],
];
return $data ? : array('VERSION' => '0.5.18','RELEASE' => '20210518');
}
// 检查数据库
public function checkDatabase(){
if (file_exists(PACKAGE_PATH . "install.lock")) {
return warning('请勿重复安装!');
}
if (!file_exists(PUBLIC_PATH . "sql/database.sql")) {
return warning('缺少必要的数据库文件!');
}
$temp = request()->param();
$db_config = $temp['form'];
$db_config['type'] = 'mysql';
if (empty($db_config['hostname'])) {
return warning('请填写数据库主机!');
}
if (empty($db_config['hostport'])) {
return warning('请填写数据库端口!');
}
if (preg_match('/[^0-9]/', $db_config['hostport'])) {
return warning('数据库端口只能是数字!');
}
if (empty($db_config['database'])) {
return warning('请填写数据库名!');
}
if (empty($db_config['username'])) {
return warning('请填写数据库用户名!');
}
if (empty($db_config['password'])) {
return warning('请填写数据库密码!');
}
if (empty($db_config['prefix'])) {
return warning('请填写表前缀!');
}
if (empty($db_config['redishost'])) {
return warning('请填写redis主机地址!');
}
if (empty($db_config['redisport'])) {
return warning('请填写redis端口!');
}
if (preg_match('/[^a-z0-9_]/i', $db_config['prefix'])) {
return warning('表前缀只能包含数字、字母和下划线!');
}
// 创建数据库配置文件
self::mkDatabase($db_config);
// 检测数据库连接
try{
$conn=mysqli_connect($db_config['hostname'], $db_config['username'], $db_config['password'],'',$db_config['hostport']);
// 检测连接
if ($conn->connect_error) {
return warning("连接失败: " . $conn->connect_error);
}
// 创建数据库
$sql = "CREATE DATABASE IF NOT EXISTS `".$db_config['database']."` default collate utf8_general_ci ";
if ($conn->query($sql) === TRUE) {
return success('数据库连接成功',['status'=>1]);
} else{
return warning('没有找到您填写的数据库名且无法创建!请检查连接账号是否有创建数据库的权限!');
}
}catch(\Exception $e){
return warning('数据库连接失败,请检查数据库配置!');
}
}
// 执行安装
public function install(){
$db_config=Config::get('database.connections.mysql');
$sql = file_get_contents( PUBLIC_PATH . "sql/database.sql");
$sqlList = parse_sql($sql, 0, ['yu_' => $db_config['prefix']]);
$install_count=0;
if ($sqlList) {
$sqlList = array_filter($sqlList);
$install_count = count($sqlList);
foreach ($sqlList as $k=>$v) {
try {
$temp_sql = $v.';';
Db::query($temp_sql);
} catch(\Exception $e) {
touch(PACKAGE_PATH . "install.lock");
return error('数据库sql安装出错,请操作数据库手动导入sql文件'.$e->getMessage());
}
}
}
touch(PACKAGE_PATH . "install.lock");
return success('安装成功',['status'=>$this->status],$install_count);
}
//ajax 进度条
public function progress()
{
$data['length'] = session('install_count');
$data['now'] = session('install_now');
return success('',$data);
}
//添加database.php文件
private function mkDatabase(array $data)
{
$code = <<<INFO
APP_DEBUG = true
[APP]
NAME = IM
LOGO =
VERSION = 5.4.0
RELEASE = 20241226
# 主域名必填
HOST =
DEFAULT_TIMEZONE = Asia/Shanghai
ID = a1b2c3d4e5f
SECRET = GHJKUG123456sdfghjkl
API_STATUS = true
# thinkapi的令牌,目前只用于敏感词过滤,其他接口自行接入
THINKAPI_TOKEN =
# 下载页分发链接
DOWNAPP_URL =
# 安卓包名,如果上架了市场,根据市场ID跳转市场
ANDRIOD_APPID =
#安卓下载地址,如果未设置会检测根目录是否有app.apk
ANDRIOD_WEBCLIP =
#APPSTORE市场ID
IOS_APPID =
#IOS下载地址,如果没有市场的ID则使用下载地址
IOS_WEBCLIP =
#windows下载地址
WIN_WEBCLIP =
#mac下载地址
MAC_WEBCLIP =
[DATABASE]
TYPE = {$data['type']}
HOSTNAME = {$data['hostname']}
DATABASE = {$data['database']}
USERNAME = {$data['username']}
PASSWORD = {$data['password']}
HOSTPORT = {$data['hostport']}
CHARSET = utf8mb4
DEBUG = true
prefix = {$data['prefix']}
[LANG]
default_lang = zh-cn
[REDIS]
HOST = {$data['redishost']}
PORT = {$data['redisport']}
PASSWORD ={$data['redispass']}
视频封面截取配置,需要单独安装,宝塔安装默认地址为/www/server/ffmpeg/ffmpeg-6.1
[FFMPEG]
BIN_PATH =
[AES]
TOKEN_KEY = tHTi8USApxsdfnhTM
LOGIN_KEY = t2fe6HMnmssswDVi2
#最好是自定义自己能记的,不要太长,不要太短,不要太简单,不要太复杂,不要太难记,一旦确定之后就不需要再修改。否者无法解析聊天记录,开启后聊天记录不可被搜索
CHAT_KEY =
[JWT]
SECRET = 17b190c0d612321f94f57325ae5a8b4c
TTL = 2592000
[WORKER]
NAME = businessWorker
PORT = 8282
# 根据自己的核心数而配置
COUNT = 1
START_PORT = 2300
REGISTER_ADDRESS =127.0.0.1:1236
lAN_IP = 127.0.0.1
# 分部署部署只需要启动一个gateway,其他的gateway只需要配置register_address即可
REGISTER_DEPLOY = true
#配置预览功能,本系统主要使用第三方的预览工具,比如永中云转换,自带预览系统
[PREVIEW]
# 自带预览系统URL,主要用于预览媒体文件,已内置,必须要有最后的/斜杠
own=
# 永中云文件预览,主要用于文档预览,必须要有最后的/斜杠
yzdcs=
# 永中云api code
keycode=17444844212312
[UNIPUSH]
# unipush的云函数转url地址,主要用于推送
URL=
# unipush直接推送通知栏还是app接收后再创建通知栏
IS_FORCE=false
# 配置对象储存,主要用于聊天文件储存,可以通过后台进行配置
[FILESYSTEM]
driver=local
aliyun_accessId=false
aliyun_accessSecret=false
aliyun_bucket=false
aliyun_endpoint=false
aliyun_url=false
qiniu_accessKey=false
qiniu_secretKey=false
qiniu_bucket=false
qiniu_url=false
qcloud_region=false
qcloud_appId=false
qcloud_secretId=false
qcloud_secretKey=false
qcloud_bucket=false
qcloud_cdn=false
INFO;
@file_put_contents( root_path().'.env', $code);
$database=env('database.database');
// 判断写入是否成功
if (empty($database) || $database != $data['database']) {
return warning('[.env]数据库配置写入失败!');
}
return true;
}
//添加database.php文件
private function mkDatabase1(array $data)
{
$code = <<<INFO
<?php
return [
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
// true为自动识别类型 false关闭
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
'auto_timestamp' => true,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
'default' => '{$data['type']}',
'connections' => [
'mysql' => [
// 数据库类型
'type' =>env('database.type', '{$data['type']}'),
// 服务器地址
'hostname' => env('database.hostname','{$data['hostname']}'),
// 数据库名
'database' => env('database.database','{$data['database']}'),
// 用户名
'username' => env('database.username','{$data['username']}'),
// 密码
'password' => env('database.password','{$data['password']}'),
// 端口
'hostport' => env('database.hostport','{$data['hostport']}'),
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => env('database.charset', 'utf8'),
// 数据库表前缀
'prefix' => env('database.prefix', '{$data['prefix']}'),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('app_debug', true),
// 开启字段缓存
'fields_cache' => false,
// 字段缓存路径
'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR,
]
]
];
INFO;
file_put_contents( CONF_PATH.'database.php', $code);
// 判断写入是否成功
$config = include CONF_PATH.'database.php';
if (empty($config['database']) || $config['database'] != $data['database']) {
return warning('[config/database.php]数据库配置写入失败!');
}
return true;
}
//检查目录权限
public function check_dir_iswritable($dir_path){
$dir_path=str_replace( '\\','/',$dir_path);
$is_writale=1;
if (!is_dir($dir_path)) {
$is_writale=0;
return $is_writale;
} else {
$file_hd=@fopen($dir_path.'/test.txt','w');
if (!$file_hd) {
@fclose($file_hd);
@unlink($dir_path.'/test.txt');
$is_writale=0;
return $is_writale;
}
$dir_hd = opendir($dir_path);
while (false !== ($file=readdir($dir_hd))) {
if ($file != "." && $file != "..") {
if (is_file($dir_path.'/'.$file)) {
//文件不可写,直接返回
if (!is_writable($dir_path.'/'.$file)) {
return 0;
}
} else {
$file_hd2=@fopen($dir_path.'/'.$file.'/test.txt','w');
if (!$file_hd2) {
@fclose($file_hd2);
@unlink($dir_path.'/'.$file.'/test.txt');
$is_writale=0;
return $is_writale;
}
//递归
$is_writale=$this->check_dir_iswritable($dir_path.'/'.$file);
}
}
}
}
return $is_writale;
}
/**
* 环境检测
* @return array
*/
private function checkEnv()
{
// $items = [
// 'os' => ['操作系统', PHP_OS, '类Unix', 'ok'],
// 'php' => ['PHP版本', PHP_VERSION, '7.3 ( <em style="color: #888; font-size: 12px;">>= 7.0</em> )', 'ok','性能更佳'],
// 'gd' => ['gd', '开启', '开启', 'ok'],
// 'openssl' => ['openssl', '开启', '开启', 'ok'],
// 'pdo' => ['pdo', '开启', '开启', 'ok'],
// ];
$items = [
['name'=>'操作系统','alias'=>'os','value'=>PHP_OS,'status'=> 'ok','description'=>"操作系统需要类Unix"],
['name'=>'PHP版本','alias'=>'version','value'=> PHP_VERSION, 'status'=>'ok','description'=>"PHP版本必须大于7.0"],
['name'=>'gd库','alias'=>'gd', 'value'=>'开启', 'status'=>'ok','description'=>"开启GD库"],
['name'=>'pdo','alias'=>'pdo', 'value'=>'开启', 'status'=>'ok','description'=>"PDO扩展"],
['name'=>'openssl','alias'=>'openssl', 'value'=>'开启', 'status'=>'ok','description'=>"OPENSSL扩展"],
['name'=>'pcntl','alias'=>'pcntl', 'value'=>'开启', 'status'=>'ok','description'=>"pcntl扩展,消息推送必须开启"],
['name'=>'posix','alias'=>'posix', 'value'=>'开启', 'status'=>'ok','description'=>"posix扩展,消息推送必须开启"],
['name'=>'event','alias'=>'event', 'value'=>'开启', 'status'=>'ok','description'=>"event扩展(可选安装),处理消息推送高并发"],
];
foreach($items as $k=>$v){
$status='ok';
switch($v['alias']){
case 'php':
if (substr($v['value'],0,3) < '7.0') {
$status='no';
$this->status=0;
}
break;
case 'gd':
if (!extension_loaded('gd')) {
$items[$k]['value'] = '未开启';
$status='no';
$this->status=0;
}
break;
case 'openssl':
if (!extension_loaded('openssl')) {
$items[$k]['value'] = '未开启';
$status='no';
$this->status=0;
}
break;
case 'pdo':
if (!extension_loaded('pdo')) {
$this->status=0;
$items[$k]['value'] = '未开启';
$status='no';
}
break;
case 'pcntl':
if (PHP_OS === 'Linux') {
if (!extension_loaded('pcntl')) {
$items[$k]['value'] = '未开启';
$status='no';
}
} else {
$items[$k]['value'] = 'win无需开启';
}
break;
case 'posix':
if (PHP_OS === 'Linux') {
if (!extension_loaded('posix')) {
$this->status=0;
$items[$k]['value'] = '未开启';
$status='no';
}
} else {
$items[$k]['value'] = 'win无需开启';
}
break;
case 'event':
if (PHP_OS === 'Linux') {
if (!extension_loaded('event')) {
$items[$k]['value'] = '未开启';
$status='no';
}
} else {
$items[$k]['value'] = 'win无需开启';
}
break;
}
$items[$k]['status'] = $status;
}
return $items;
}
/**
* 目录权限检查
* @return array
*/
private function checkDir()
{
$items = [
['dir', root_path().'app', 'app', '读写', '读写', 'ok'],
['dir', root_path().'extend', 'extend', '读写', '读写', 'ok'],
['dir', root_path().'runtime', './temp', '读写', '读写', 'ok'],
['dir', root_path().'public', './upload', '读写', '读写', 'ok'],
['file', root_path().'config', 'config', '读写', '读写', 'ok'],
];
$items = [
['path'=>root_path().'app', 'dir'=>'app', 'value'=>'读写', 'type'=>'dir','status'=>'ok'],
['path'=>root_path().'extend', 'dir'=>'extend', 'value'=>'读写', 'type'=>'dir','status'=>'ok'],
['path'=> root_path().'runtime', 'dir'=>'runtime', 'value'=>'读写', 'type'=>'dir','status'=>'ok'],
['path'=>root_path().'public', 'dir'=>'public', 'value'=>'读写', 'type'=>'dir','status'=>'ok'],
['path'=>root_path().'config', 'dir'=>'config', 'value'=>'读写', 'type'=>'file','status'=>'ok'],
];
$status=1;
foreach ($items as $k=>$v) {
if ($v['type'] == 'dir') {// 文件夹
if (!is_writable($v['path'])) {
if (is_dir($v['path'])) {
$items[$k]['value'] = '不可写';
$items[$k]['status'] = 'no';
} else {
$items[$k]['value'] = '不存在';
$items[$k]['status'] = 'no';
}
$this->status=0;
}
} else {// 文件
if (!is_writable($v['path'])) {
$items[$k]['value'] = '不可写';
$items[$k]['status'] = 'no';
$this->status=0;
}
}
}
return $items;
}
/**
* 验证序列号
* @param
* @return
*/
public function checkCodeOld($username) {
$encryption = md5($username);
$substr = substr($username, strlen($username)-6);
$subArr = str_split($substr, 1);
$code = '';
for ($i = 0; $i <= 5; $i++) {
$code .= $encryption[$subArr[$i]];
}
return $code;
}
//写入license文件
private function mkLicense($wkcode)
{
file_put_contents( CONF_PATH.'license.dat', $wkcode);
// 判断写入是否成功
// $config = include CONF_PATH.'license.dat';
// if (empty($config)) {
// return resultArray(['error' => 'license配置写入失败!']);
// }
return true;
}
}

17
app/index/route/app.php

@ -0,0 +1,17 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
use think\facade\Route;
Route::rule('avatar/:str/:s/:uid','index/avatar');
Route::rule('view','index/index/view');
Route::rule('filedown/:file_id','index/download');
Route::rule('scan/:action/:token','index/scanQr');
Route::rule('downapp','index/index/downapp');
Route::rule('downloadApp/:platform','index/index/downloadApp');
Loading…
Cancel
Save