diff --git a/app/chat/controller/CommonPhraseController.php b/app/chat/controller/CommonPhraseController.php index e229499..8f2edce 100644 --- a/app/chat/controller/CommonPhraseController.php +++ b/app/chat/controller/CommonPhraseController.php @@ -14,8 +14,7 @@ class CommonPhraseController extends ApiController */ public function index(Request $request): Response { - $uid = $request->get('uid'); - return $this->success(Db::name('chat_common_phrase')->where('user_id', $uid) + return $this->success(Db::name('chat_common_phrase')->where('user_id', $this->uid) ->order('sort', 'desc') ->field(['id', 'content', 'sort', 'created_time']) ->select()); @@ -26,19 +25,18 @@ class CommonPhraseController extends ApiController */ public function store(Request $request): Response { - $uid = $request->post('uid'); $content = $request->post('content', ''); $sort = $request->post('sort', 0); if (empty($content) || mb_strlen($content) > 500) { return $this->error('常用语内容不能为空且长度≤500字'); } // 有重复的内容不给添加 - $msg = Db::name('chat_common_phrase')->where(['user_id' => $uid, 'content' => $content])->find(); + $msg = Db::name('chat_common_phrase')->where(['user_id' => $this->uid, 'content' => $content])->find(); if($msg) { return $this->error('此内容已经存在,不能重复添加'); } $res = Db::name('chat_common_phrase')->insert([ - 'user_id' => $uid, + 'user_id' => $this->uid, 'content' => $content, 'sort' => $sort, 'created_time' => d(), @@ -53,15 +51,24 @@ class CommonPhraseController extends ApiController /** * 删除常用语 */ - public function destroy(Request $request, int $id): Response + public function destroy(Request $request): Response { - $uid = $request->uid; - $phrase = ChatCommonPhrase::where(['id' => $id, 'user_id' => $uid])->find(); + $id = $request->post('id'); + $phrase = Db::name('chat_common_phrase')->where([ + 'user_id' => $this->uid, + 'id' => $id, + ])->find(); if (!$phrase) { - return json(['code' => 404, 'msg' => '常用语不存在']); + return $this->error('常用语不存在'); + } + $phrase_del = Db::name('chat_common_phrase')->where([ + 'user_id' => $this->uid, + 'id' => $id, + ])->delete(); + if($phrase_del) { + return $this->success(); + }else { + return $this->error(); } - - $phrase->delete(); - return json(['code' => 200, 'msg' => '删除成功']); } } diff --git a/app/chat/controller/GroupController.php b/app/chat/controller/GroupController.php index ef49f4f..49dbf1c 100644 --- a/app/chat/controller/GroupController.php +++ b/app/chat/controller/GroupController.php @@ -5,60 +5,64 @@ use app\chat\model\ChatGroup; use app\chat\model\ChatGroupMember; use app\chat\model\ChatGroupMute; use app\chat\model\ChatDoNotDisturb; +use app\common\controller\ApiController; use app\utils\Session; use support\Request; use support\Response; +use support\think\Db; -class GroupController +class GroupController extends ApiController { /** * 创建群(仅商家可创建) */ public function create(Request $request): Response { - $uid = $request->uid; - $userType = $request->user_type; - - if ($userType != 2) { // 2=商家 - return json(['code' => 403, 'msg' => '仅商家可创建群聊']); + if ($this->user_type == 1) { // 2=商家 + return $this->error('仅商家可创建群聊'); } - - $name = $request->post('name', ''); - $avatar = $request->post('avatar', ''); + $shop_id = $request->post('shop_id', 0); + if(empty($shop_id)) { + return $this->error('参数不完整'); + } + $shop = Db::name('tb_shop_info')->where(['id' => $shop_id])->find(); + if(empty($shop)) { + return $this->error('店铺不存在'); + } + // 查找群数量 + $count = Db::name('chat_group')->where(['shop_id' => $shop_id])->count(); + $name = $request->post('name')?:$shop['shop_name'] . '粉丝福利' . $count + 1 . '群'; + $avatar = $request->post('avatar')?:$shop['logo']; $announcement = $request->post('announcement', ''); $isPublic = $request->post('is_public', 0); - - if (empty($name)) { - return json(['code' => 400, 'msg' => '群名称不能为空']); + $now = d(); + try { + Db::startTrans(); + // 创建群 + $group_id = Db::name('chat_group')->insertGetId([ + 'name' => $name, + 'shop_id' => $shop_id, + 'avatar' => $avatar, + 'owner_id' => $this->uid, + 'announcement' => $announcement, + 'is_public' => $isPublic, + 'created_time' => $now, + ]); + // 群主加入群 + Db::name('chat_group_member')->insert([ + 'group_id' => $group_id, + 'user_id' => $this->uid, + 'role' => 1, // 1=群主 + 'join_time' => $now, + ]); + Db::commit(); + return $this->success(['group_id' => $group_id]); + }catch (\Throwable $exception) { + Db::rollback(); + return $this->error($exception->getMessage()); } - $now = time(); - // 创建群 - $group = ChatGroup::create([ - 'name' => $name, - 'avatar' => $avatar, - 'owner_id' => $uid, - 'announcement' => $announcement, - 'is_public' => $isPublic, - 'created_at' => $now, - 'updated_at' => $now - ]); - // 群主加入群 - ChatGroupMember::create([ - 'group_id' => $group->id, - 'user_id' => $uid, - 'role' => 1, // 1=群主 - 'join_time' => $now, - 'quit_time' => null, - 'is_kicked' => 0 - ]); - - return json([ - 'code' => 200, - 'msg' => '群创建成功', - 'data' => ['group_id' => $group->id] - ]); } /** @@ -66,93 +70,130 @@ class GroupController */ public function join(Request $request): Response { - $uid = $request->uid; - $groupId = $request->post('group_id', 0); - - if (!$groupId) { - return json(['code' => 400, 'msg' => '缺少group_id']); + $group_id = $request->post('group_id'); + $invite = $request->post('invite'); + if (!$group_id) { + return $this->error('缺少group_id'); } // 验证群是否存在 - $group = ChatGroup::find($groupId); + $group = Db::name('chat_group')->where(['id' => $group_id])->find(); if (!$group) { - return json(['code' => 404, 'msg' => '群不存在']); + return $this->error('群不存在'); } // 验证是否已在群内 - $exists = ChatGroupMember::where([ - 'group_id' => $groupId, - 'user_id' => $uid, + $exists = Db::name('chat_group_member')->where([ + 'group_id' => $group_id, + 'user_id' => $this->uid, 'quit_time' => null, 'is_kicked' => 0 - ])->exists(); + ])->find(); + if ($exists) { - return json(['code' => 400, 'msg' => '已在群内']); + return $this->error('已在群内'); } // 被踢用户不能重新加入(需群主邀请) - $isKicked = ChatGroupMember::where([ - 'group_id' => $groupId, - 'user_id' => $uid, + $isKicked = Db::name('chat_group_member')->where([ + 'group_id' => $group_id, + 'user_id' => $this->uid, 'is_kicked' => 1 - ])->exists(); - if ($isKicked) { - return json(['code' => 403, 'msg' => '你已被移出该群,无法重新加入']); + ])->find(); + + // 如果不是邀请 + if(empty($invite) && $isKicked) { + return $this->error('你已被移出该群,无法重新加入'); + } + $insert_arr = [ + 'group_id' => $group_id, + 'user_id' => $this->uid, + 'role' => 3, + 'join_time' => d(), + ]; + $is_insert = true; + if($invite) { + // 验证邀请参数是否正常 + $decryptedText = simple_decrypt($invite, config('cons.sercer_key')); + $decryptedText_arr = json_decode($decryptedText, true); + if($isKicked) { + $is_insert = false; + $insert_arr['is_kicked'] = 0; + } + $insert_arr['pid'] = $decryptedText_arr['pid']; + $insert_arr['group_id'] = $decryptedText_arr['group_id']; + $group_id = $decryptedText_arr['group_id']; } // 加入群 - ChatGroupMember::create([ - 'group_id' => $groupId, - 'user_id' => $uid, - 'role' => 3, // 3=普通成员 - 'join_time' => time(), - 'quit_time' => null, - 'is_kicked' => 0 - ]); - - return json(['code' => 200, 'msg' => '加群成功']); + if($is_insert) { + $res = Db::name('chat_group_member')->insert($insert_arr); + }else { + $res = Db::name('chat_group_member')->where(['user_id' => $this->uid, 'group_id' => $group_id])->update($insert_arr); + } + if($res) { + return $this->success(); + }else { + return $this->error(); + } } + // 获取群邀请链接参数 + public function getgrepurl(Request $request): Response + { + $group_id = $request->post('group_id'); + if (!$group_id) { + return $this->error('缺少group_id'); + } + if($this->user_type == 1) { + return $this->error('角色有误'); + } + $data = [ + 'pid' => $this->uid, + 'group_id' => $group_id + ]; + return $this->success(simple_encrypt(json_encode($data), config('cons.sercer_key'))); + } + + /** * 退群 */ public function quit(Request $request): Response { - $uid = $request->uid; - $groupId = $request->post('group_id', 0); - + $groupId = $request->post('group_id')?:0; if (!$groupId) { - return json(['code' => 400, 'msg' => '缺少group_id']); + return $this->error('缺少group_id'); } // 验证是否是群主(群主不能退群,需转让) - $isOwner = ChatGroupMember::where([ + $owner = Db::name('chat_group_member')->where([ 'group_id' => $groupId, - 'user_id' => $uid, - 'role' => 1, + 'user_id' => $this->uid, 'quit_time' => null - ])->exists(); - if ($isOwner) { - return json(['code' => 403, 'msg' => '群主不能退群,请先转让群主']); - } - - // 标记退出时间 - $member = ChatGroupMember::where([ - 'group_id' => $groupId, - 'user_id' => $uid, - 'quit_time' => null, - 'is_kicked' => 0 ])->find(); - if (!$member) { - return json(['code' => 400, 'msg' => '不在群内']); + if ($owner) { + if($owner['role'] == 1) { + return $this->error('群主不能退群,请先转让群'); + } + }else { + return $this->error('不在群内'); } - - $member->quit_time = time(); - $member->save(); - - return json(['code' => 200, 'msg' => '退群成功']); + $res = Db::name('chat_group_member')->where([ + 'group_id' => $groupId, + 'user_id' => $this->uid, + ])->update(['quit_time' => d()]); + if($res) { + return $this->success(); + } + return $this->error(); } + + + + + /** * 设置群公告(仅群主/管理员) */ diff --git a/app/common/controller/ApiController.php b/app/common/controller/ApiController.php index dfb3df7..0776038 100644 --- a/app/common/controller/ApiController.php +++ b/app/common/controller/ApiController.php @@ -5,6 +5,14 @@ use app\exception\MyBusinessException; use support\exception\BusinessException; class ApiController { + public $uid; + public $user_type; + + public function __construct() + { + $this->uid = input('uid'); + $this->user_type = input('user_type'); + } public function success($data = []) { diff --git a/app/functions.php b/app/functions.php index f9ada1c..400b833 100644 --- a/app/functions.php +++ b/app/functions.php @@ -44,6 +44,114 @@ if (!function_exists('d')) { + + + +/** + * 加密函数(带随机字符,AES-256-CBC) + * @param string $plaintext 待加密明文(如字符串、数字) + * @param string $key 加密密钥(建议至少 8 位,越复杂越安全) + * @return string|false 加密后的密文(失败返回 false) + */ +function simple_encrypt(string $plaintext, string $key): string|false +{ + try { + // 1. 生成随机字符(Salt + IV) + $salt = random_bytes(8); // 8 位随机盐值(增加密钥多样性) + $iv = random_bytes(16); // 16 位随机 IV(AES-256-CBC 块大小要求) + + // 2. 密钥处理:Salt + 原始密钥 → SHA256 哈希 → 32 位密钥(AES-256 要求) + $encryptedKey = hash('sha256', $salt . $key, true); // true 表示返回二进制数据 + + // 3. 明文填充:PKCS7 填充(AES 要求明文长度是块大小的整数倍) + $blockSize = openssl_cipher_iv_length('aes-256-cbc'); + $padding = $blockSize - (strlen($plaintext) % $blockSize); + $paddedPlaintext = $plaintext . str_repeat(chr($padding), $padding); + + // 4. AES 加密(返回 Base64 编码的密文) + $ciphertext = openssl_encrypt( + $paddedPlaintext, + 'aes-256-cbc', + $encryptedKey, + OPENSSL_RAW_DATA, // 输出原始二进制数据(后续手动 Base64) + $iv + ); + + if ($ciphertext === false) { + throw new Exception('加密失败:' . openssl_error_string()); + } + + // 5. 拼接 Salt + IV + 密文(Base64 编码后用冒号分隔,避免字符冲突) + return base64_encode($salt) . ':' . base64_encode($iv) . ':' . base64_encode($ciphertext); + } catch (Exception $e) { + error_log('加密异常:' . $e->getMessage()); + return false; + } +} + +/** + * 解密函数(对应 simple_encrypt) + * @param string $ciphertext 加密后的密文 + * @param string $key 解密密钥(必须与加密密钥一致) + * @return string|false 解密后的明文(失败返回 false) + */ +function simple_decrypt(string $ciphertext, string $key): string|false +{ + try { + // 1. 拆分密文:Salt:IV:密文(按冒号分割) + $parts = explode(':', $ciphertext); + if (count($parts) !== 3) { + throw new Exception('密文格式错误'); + } + + // 2. Base64 解码,获取原始 Salt、IV、密文 + $salt = base64_decode($parts[0]); + $iv = base64_decode($parts[1]); + $ciphertextRaw = base64_decode($parts[2]); + + if ($salt === false || $iv === false || $ciphertextRaw === false) { + throw new Exception('Base64 解码失败'); + } + + // 3. 密钥处理(与加密时一致) + $encryptedKey = hash('sha256', $salt . $key, true); + + // 4. AES 解密 + $paddedPlaintext = openssl_decrypt( + $ciphertextRaw, + 'aes-256-cbc', + $encryptedKey, + OPENSSL_RAW_DATA, + $iv + ); + + if ($paddedPlaintext === false) { + throw new Exception('解密失败:' . openssl_error_string()); + } + + // 5. 去除 PKCS7 填充 + $padding = ord(substr($paddedPlaintext, -1)); + $plaintext = substr($paddedPlaintext, 0, -$padding); + + return $plaintext; + } catch (Exception $e) { + error_log('解密异常:' . $e->getMessage()); + return false; + } +} + + + + + + + + + + + + + /** * 多维数组去重并重新索引 * @param array $array 待处理的多维数组 diff --git a/app/middleware/JwtAuthMiddleware.php b/app/middleware/JwtAuthMiddleware.php index acce0d7..381d085 100644 --- a/app/middleware/JwtAuthMiddleware.php +++ b/app/middleware/JwtAuthMiddleware.php @@ -15,14 +15,18 @@ class JwtAuthMiddleware implements MiddlewareInterface { $uid = Redis::get('token:client:token:' . $request->header('token')); if($uid) { - if($request->isGet()) { - $request->setGet('uid', $uid); - }elseif ($request->post()) { - $request->setPost('uid', $uid); + // 用户 + $user_type = 1; + }else{ + $uid = Redis::get('token:admin:token:' . $request->header('token')); + if(!$uid) { + throw new MyBusinessException('请登录', 3000); } - }else { - throw new MyBusinessException('请登录', 3000); + // 商家 + $user_type = 2; } + $request->setPost('uid', $uid); + $request->setPost('user_type', $user_type); return $handler($request); } } diff --git a/config/cons.php b/config/cons.php index 50ddd14..120559b 100644 --- a/config/cons.php +++ b/config/cons.php @@ -19,5 +19,6 @@ return [ 'user' => 'chaozg', 'password' => 'chaozg123', 'queue_t' => 'dev', - ] + ], + 'sercer_key' => 'JVt1ZT5gwRqHR8pR7cX6DFvf8BxgKugB' ]; diff --git a/config/route.php b/config/route.php index c0a7baa..e0115a5 100644 --- a/config/route.php +++ b/config/route.php @@ -20,31 +20,32 @@ Route::group('/api/chat', function () { $jwt_middleware = app\middleware\JwtAuthMiddleware::class; // 常用语管理 Route::group('/common-phrase', function () { - Route::get('', app\chat\controller\CommonPhraseController::class . '@index'); // 列表 + Route::post('', app\chat\controller\CommonPhraseController::class . '@index'); // 列表 Route::post('/add', app\chat\controller\CommonPhraseController::class . '@store'); // 添加 - Route::post('/del/{id}', app\chat\controller\CommonPhraseController::class . '@destroy'); // 删除 + Route::post('/del', app\chat\controller\CommonPhraseController::class . '@destroy'); // 删除 })->middleware($jwt_middleware); // 群聊管理 Route::group('/group', function () { Route::post('/create', app\chat\controller\GroupController::class . '@create'); // 创建群(商家) Route::post('/join', app\chat\controller\GroupController::class . '@join'); // 加群 + Route::post('/getgrepurl', app\chat\controller\GroupController::class . '@getgrepurl'); // 群邀请参数 Route::post('/quit', app\chat\controller\GroupController::class . '@quit'); // 退群 Route::post('/announcement', app\chat\controller\GroupController::class . '@setAnnouncement'); // 群公告 Route::post('/do-not-disturb', app\chat\controller\GroupController::class . '@setDoNotDisturb'); // 免打扰 Route::post('/mute', app\chat\controller\GroupController::class . '@muteMember'); // 禁言 Route::post('/unmute', app\chat\controller\GroupController::class . '@unmuteMember'); // 解除禁言 Route::post('/kick', app\chat\controller\GroupController::class . '@kickMember'); // 踢人 - Route::get('/members', app\chat\controller\GroupController::class . '@getMembers'); // 群成员列表 + Route::post('/members', app\chat\controller\GroupController::class . '@getMembers'); // 群成员列表 })->middleware($jwt_middleware); // 消息管理 Route::group('/message', function () { - Route::get('/history', app\chat\controller\MessageController::class . '@history'); // 历史消息 + Route::post('/history', app\chat\controller\MessageController::class . '@history'); // 历史消息 Route::post('/mark-read', app\chat\controller\MessageController::class . '@markRead'); // 标记已读 Route::post('/mark-read-all', app\chat\controller\MessageController::class . '@markReadAll'); // 全部已读 - Route::get('/unread-count', app\chat\controller\MessageController::class . '@getUnreadCount'); // 未读总数 - Route::get('/session-list', app\chat\controller\MessageController::class . '@getSessionList'); // 会话列表 + Route::post('/unread-count', app\chat\controller\MessageController::class . '@getUnreadCount'); // 未读总数 + Route::post('/session-list', app\chat\controller\MessageController::class . '@getSessionList'); // 会话列表 })->middleware($jwt_middleware); // 图片上传(相册/拍照)