This commit is contained in:
2025-11-18 13:38:05 +08:00
parent a3e5568f93
commit f9062837ab
7 changed files with 286 additions and 116 deletions

View File

@@ -14,8 +14,7 @@ class CommonPhraseController extends ApiController
*/ */
public function index(Request $request): Response public function index(Request $request): Response
{ {
$uid = $request->get('uid'); return $this->success(Db::name('chat_common_phrase')->where('user_id', $this->uid)
return $this->success(Db::name('chat_common_phrase')->where('user_id', $uid)
->order('sort', 'desc') ->order('sort', 'desc')
->field(['id', 'content', 'sort', 'created_time']) ->field(['id', 'content', 'sort', 'created_time'])
->select()); ->select());
@@ -26,19 +25,18 @@ class CommonPhraseController extends ApiController
*/ */
public function store(Request $request): Response public function store(Request $request): Response
{ {
$uid = $request->post('uid');
$content = $request->post('content', ''); $content = $request->post('content', '');
$sort = $request->post('sort', 0); $sort = $request->post('sort', 0);
if (empty($content) || mb_strlen($content) > 500) { if (empty($content) || mb_strlen($content) > 500) {
return $this->error('常用语内容不能为空且长度≤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) { if($msg) {
return $this->error('此内容已经存在,不能重复添加'); return $this->error('此内容已经存在,不能重复添加');
} }
$res = Db::name('chat_common_phrase')->insert([ $res = Db::name('chat_common_phrase')->insert([
'user_id' => $uid, 'user_id' => $this->uid,
'content' => $content, 'content' => $content,
'sort' => $sort, 'sort' => $sort,
'created_time' => d(), '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; $id = $request->post('id');
$phrase = ChatCommonPhrase::where(['id' => $id, 'user_id' => $uid])->find(); $phrase = Db::name('chat_common_phrase')->where([
'user_id' => $this->uid,
'id' => $id,
])->find();
if (!$phrase) { 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' => '删除成功']);
} }
} }

View File

@@ -5,60 +5,64 @@ use app\chat\model\ChatGroup;
use app\chat\model\ChatGroupMember; use app\chat\model\ChatGroupMember;
use app\chat\model\ChatGroupMute; use app\chat\model\ChatGroupMute;
use app\chat\model\ChatDoNotDisturb; use app\chat\model\ChatDoNotDisturb;
use app\common\controller\ApiController;
use app\utils\Session; use app\utils\Session;
use support\Request; use support\Request;
use support\Response; use support\Response;
use support\think\Db;
class GroupController class GroupController extends ApiController
{ {
/** /**
* 创建群(仅商家可创建) * 创建群(仅商家可创建)
*/ */
public function create(Request $request): Response public function create(Request $request): Response
{ {
$uid = $request->uid; if ($this->user_type == 1) { // 2=商家
$userType = $request->user_type; return $this->error('仅商家可创建群聊');
if ($userType != 2) { // 2=商家
return json(['code' => 403, 'msg' => '仅商家可创建群聊']);
} }
$shop_id = $request->post('shop_id', 0);
$name = $request->post('name', ''); if(empty($shop_id)) {
$avatar = $request->post('avatar', ''); 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', ''); $announcement = $request->post('announcement', '');
$isPublic = $request->post('is_public', 0); $isPublic = $request->post('is_public', 0);
$now = d();
if (empty($name)) { try {
return json(['code' => 400, 'msg' => '群名称不能为空']); Db::startTrans();
}
$now = time();
// 创建群 // 创建群
$group = ChatGroup::create([ $group_id = Db::name('chat_group')->insertGetId([
'name' => $name, 'name' => $name,
'shop_id' => $shop_id,
'avatar' => $avatar, 'avatar' => $avatar,
'owner_id' => $uid, 'owner_id' => $this->uid,
'announcement' => $announcement, 'announcement' => $announcement,
'is_public' => $isPublic, 'is_public' => $isPublic,
'created_at' => $now, 'created_time' => $now,
'updated_at' => $now
]); ]);
// 群主加入群 // 群主加入群
ChatGroupMember::create([ Db::name('chat_group_member')->insert([
'group_id' => $group->id, 'group_id' => $group_id,
'user_id' => $uid, 'user_id' => $this->uid,
'role' => 1, // 1=群主 'role' => 1, // 1=群主
'join_time' => $now, 'join_time' => $now,
'quit_time' => null,
'is_kicked' => 0
]); ]);
Db::commit();
return $this->success(['group_id' => $group_id]);
}catch (\Throwable $exception) {
Db::rollback();
return $this->error($exception->getMessage());
}
return json([
'code' => 200,
'msg' => '群创建成功',
'data' => ['group_id' => $group->id]
]);
} }
/** /**
@@ -66,92 +70,129 @@ class GroupController
*/ */
public function join(Request $request): Response public function join(Request $request): Response
{ {
$uid = $request->uid; $group_id = $request->post('group_id');
$groupId = $request->post('group_id', 0); $invite = $request->post('invite');
if (!$group_id) {
if (!$groupId) { return $this->error('缺少group_id');
return json(['code' => 400, 'msg' => '缺少group_id']);
} }
// 验证群是否存在 // 验证群是否存在
$group = ChatGroup::find($groupId); $group = Db::name('chat_group')->where(['id' => $group_id])->find();
if (!$group) { if (!$group) {
return json(['code' => 404, 'msg' => '群不存在']); return $this->error('群不存在');
} }
// 验证是否已在群内 // 验证是否已在群内
$exists = ChatGroupMember::where([ $exists = Db::name('chat_group_member')->where([
'group_id' => $groupId, 'group_id' => $group_id,
'user_id' => $uid, 'user_id' => $this->uid,
'quit_time' => null, 'quit_time' => null,
'is_kicked' => 0 'is_kicked' => 0
])->exists(); ])->find();
if ($exists) { if ($exists) {
return json(['code' => 400, 'msg' => '已在群内']); return $this->error('已在群内');
} }
// 被踢用户不能重新加入(需群主邀请) // 被踢用户不能重新加入(需群主邀请)
$isKicked = ChatGroupMember::where([ $isKicked = Db::name('chat_group_member')->where([
'group_id' => $groupId, 'group_id' => $group_id,
'user_id' => $uid, 'user_id' => $this->uid,
'is_kicked' => 1 'is_kicked' => 1
])->exists(); ])->find();
if ($isKicked) {
return json(['code' => 403, 'msg' => '你已被移出该群,无法重新加入']); // 如果不是邀请
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([ if($is_insert) {
'group_id' => $groupId, $res = Db::name('chat_group_member')->insert($insert_arr);
'user_id' => $uid, }else {
'role' => 3, // 3=普通成员 $res = Db::name('chat_group_member')->where(['user_id' => $this->uid, 'group_id' => $group_id])->update($insert_arr);
'join_time' => time(),
'quit_time' => null,
'is_kicked' => 0
]);
return json(['code' => 200, 'msg' => '加群成功']);
} }
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 public function quit(Request $request): Response
{ {
$uid = $request->uid; $groupId = $request->post('group_id')?:0;
$groupId = $request->post('group_id', 0);
if (!$groupId) { 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, 'group_id' => $groupId,
'user_id' => $uid, 'user_id' => $this->uid,
'role' => 1,
'quit_time' => null '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(); ])->find();
if (!$member) { if ($owner) {
return json(['code' => 400, 'msg' => '不在群内']); if($owner['role'] == 1) {
return $this->error('群主不能退群,请先转让群');
}
}else {
return $this->error('不在群内');
}
$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();
} }
$member->quit_time = time();
$member->save();
return json(['code' => 200, 'msg' => '退群成功']);
}
/** /**
* 设置群公告(仅群主/管理员) * 设置群公告(仅群主/管理员)

View File

@@ -5,6 +5,14 @@ use app\exception\MyBusinessException;
use support\exception\BusinessException; use support\exception\BusinessException;
class ApiController class ApiController
{ {
public $uid;
public $user_type;
public function __construct()
{
$this->uid = input('uid');
$this->user_type = input('user_type');
}
public function success($data = []) public function success($data = [])
{ {

View File

@@ -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 位随机 IVAES-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 待处理的多维数组 * @param array $array 待处理的多维数组

View File

@@ -15,14 +15,18 @@ class JwtAuthMiddleware implements MiddlewareInterface
{ {
$uid = Redis::get('token:client:token:' . $request->header('token')); $uid = Redis::get('token:client:token:' . $request->header('token'));
if($uid) { if($uid) {
if($request->isGet()) { // 用户
$request->setGet('uid', $uid); $user_type = 1;
}elseif ($request->post()) { }else{
$request->setPost('uid', $uid); $uid = Redis::get('token:admin:token:' . $request->header('token'));
} if(!$uid) {
}else {
throw new MyBusinessException('请登录', 3000); throw new MyBusinessException('请登录', 3000);
} }
// 商家
$user_type = 2;
}
$request->setPost('uid', $uid);
$request->setPost('user_type', $user_type);
return $handler($request); return $handler($request);
} }
} }

View File

@@ -19,5 +19,6 @@ return [
'user' => 'chaozg', 'user' => 'chaozg',
'password' => 'chaozg123', 'password' => 'chaozg123',
'queue_t' => 'dev', 'queue_t' => 'dev',
] ],
'sercer_key' => 'JVt1ZT5gwRqHR8pR7cX6DFvf8BxgKugB'
]; ];

View File

@@ -20,31 +20,32 @@ Route::group('/api/chat', function () {
$jwt_middleware = app\middleware\JwtAuthMiddleware::class; $jwt_middleware = app\middleware\JwtAuthMiddleware::class;
// 常用语管理 // 常用语管理
Route::group('/common-phrase', function () { 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('/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); })->middleware($jwt_middleware);
// 群聊管理 // 群聊管理
Route::group('/group', function () { Route::group('/group', function () {
Route::post('/create', app\chat\controller\GroupController::class . '@create'); // 创建群(商家) Route::post('/create', app\chat\controller\GroupController::class . '@create'); // 创建群(商家)
Route::post('/join', app\chat\controller\GroupController::class . '@join'); // 加群 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('/quit', app\chat\controller\GroupController::class . '@quit'); // 退群
Route::post('/announcement', app\chat\controller\GroupController::class . '@setAnnouncement'); // 群公告 Route::post('/announcement', app\chat\controller\GroupController::class . '@setAnnouncement'); // 群公告
Route::post('/do-not-disturb', app\chat\controller\GroupController::class . '@setDoNotDisturb'); // 免打扰 Route::post('/do-not-disturb', app\chat\controller\GroupController::class . '@setDoNotDisturb'); // 免打扰
Route::post('/mute', app\chat\controller\GroupController::class . '@muteMember'); // 禁言 Route::post('/mute', app\chat\controller\GroupController::class . '@muteMember'); // 禁言
Route::post('/unmute', app\chat\controller\GroupController::class . '@unmuteMember'); // 解除禁言 Route::post('/unmute', app\chat\controller\GroupController::class . '@unmuteMember'); // 解除禁言
Route::post('/kick', app\chat\controller\GroupController::class . '@kickMember'); // 踢人 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); })->middleware($jwt_middleware);
// 消息管理 // 消息管理
Route::group('/message', function () { 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', app\chat\controller\MessageController::class . '@markRead'); // 标记已读
Route::post('/mark-read-all', app\chat\controller\MessageController::class . '@markReadAll'); // 全部已读 Route::post('/mark-read-all', app\chat\controller\MessageController::class . '@markReadAll'); // 全部已读
Route::get('/unread-count', app\chat\controller\MessageController::class . '@getUnreadCount'); // 未读总数 Route::post('/unread-count', app\chat\controller\MessageController::class . '@getUnreadCount'); // 未读总数
Route::get('/session-list', app\chat\controller\MessageController::class . '@getSessionList'); // 会话列表 Route::post('/session-list', app\chat\controller\MessageController::class . '@getSessionList'); // 会话列表
})->middleware($jwt_middleware); })->middleware($jwt_middleware);
// 图片上传(相册/拍照) // 图片上传(相册/拍照)