用uniapp 及socket.io做一个简单聊天app 3

一直正开发时,又优化了相关的表,现在的表结构为:

c 复制代码
/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80012 (8.0.12)
 Source Host           : localhost:3306
 Source Schema         : chat

 Target Server Type    : MySQL
 Target Server Version : 80012 (8.0.12)
 File Encoding         : 65001

 Date: 03/08/2024 19:27:31
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for blacklists
-- ----------------------------
DROP TABLE IF EXISTS `blacklists`;
CREATE TABLE `blacklists`  (
  `user_id` int(11) NULL DEFAULT NULL,
  `blocked_user_id` int(11) NULL DEFAULT NULL,
  `created_at` datetime NULL DEFAULT NULL,
  INDEX `user_id`(`user_id` ASC) USING BTREE,
  INDEX `blocked_user_id`(`blocked_user_id` ASC) USING BTREE,
  CONSTRAINT `blacklists_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `blacklists_ibfk_2` FOREIGN KEY (`blocked_user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for chatgroups
-- ----------------------------
DROP TABLE IF EXISTS `chatgroups`;
CREATE TABLE `chatgroups`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `description` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `avatar_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `owner_id` int(11) NULL DEFAULT NULL,
  `created_at` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `owner_id`(`owner_id` ASC) USING BTREE,
  CONSTRAINT `chatgroups_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for friends
-- ----------------------------
DROP TABLE IF EXISTS `friends`;
CREATE TABLE `friends`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NULL DEFAULT NULL,
  `group_friend_id` int(11) NULL DEFAULT NULL,
  `type` enum('user','group') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `status` enum('pending','accepted','blocked') CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'pending',
  `created_at` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `user_id`(`user_id` ASC) USING BTREE,
  CONSTRAINT `friends_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for invites
-- ----------------------------
DROP TABLE IF EXISTS `invites`;
CREATE TABLE `invites`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `inviter_id` int(11) NULL DEFAULT NULL,
  `invitee_id` int(11) NULL DEFAULT NULL,
  `group_id` int(11) NULL DEFAULT NULL,
  `group_avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `inviter_avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `invitee_avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `status` enum('pending','accepted','declined') CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'pending',
  `created_at` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `inviter_id`(`inviter_id` ASC) USING BTREE,
  INDEX `invitee_id`(`invitee_id` ASC) USING BTREE,
  INDEX `group_id`(`group_id` ASC) USING BTREE,
  CONSTRAINT `invites_ibfk_1` FOREIGN KEY (`inviter_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `invites_ibfk_2` FOREIGN KEY (`invitee_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `invites_ibfk_3` FOREIGN KEY (`group_id`) REFERENCES `chatgroups` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for messages
-- ----------------------------
DROP TABLE IF EXISTS `messages`;
CREATE TABLE `messages`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `fid` int(11) NULL DEFAULT NULL,
  `tid` int(11) NULL DEFAULT NULL,
  `receiver_type` enum('user','group') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `type` enum('text','audio','video','image','join','left','broadcast','kick') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `is_retracted` tinyint(1) NULL DEFAULT 0,
  `retracted_at` datetime NULL DEFAULT NULL,
  `created_at` datetime NULL DEFAULT NULL,
  `sn` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '唯一码',
  `group_name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `sender_id`(`fid` ASC) USING BTREE,
  CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`fid`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `email` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `avatar_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `brief` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `created_at` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `username`(`username` ASC) USING BTREE,
  UNIQUE INDEX `email`(`email` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

用express 开发相关的model:

c 复制代码
module.exports = (sequelize, DataTypes) => {
    const User = require('./User')(sequelize, DataTypes); // 确保 User 模型正确导入

    const Blacklist = sequelize.define('Blacklist', {
        user_id: {
            type: DataTypes.INTEGER,
            references: {
                model: User,
                key: 'id',
            },
        },
        blocked_user_id: {
            type: DataTypes.INTEGER,
            references: {
                model: User,
                key: 'id',
            },
        },
        created_at: {
            type: DataTypes.DATE,
            defaultValue: DataTypes.NOW,
        },
    }, {
        timestamps: false,
        primaryKey: false,
    });

    // 移除默认的主键 'id'
    Blacklist.removeAttribute('id');

    return Blacklist;
};
c 复制代码
module.exports = (sequelize, DataTypes) => {
    const User = require('./User')(sequelize, DataTypes); // 确保 User 模型正确导入

    const ChatGroup = sequelize.define('ChatGroup', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        name: {
            type: DataTypes.STRING(100),
            allowNull: false,
        },
        description: {
            type: DataTypes.TEXT,
        },
        avatar_url: {
            type: DataTypes.STRING(255),
        },
        owner_id: {
            type: DataTypes.INTEGER,
            references: {
                model: User,
                key: 'id',
            },
        },
        created_at: {
            type: DataTypes.DATE,
            defaultValue: DataTypes.NOW,
        },
    }, {
        timestamps: false,
    });

    return ChatGroup;
};
c 复制代码
module.exports = (sequelize, DataTypes) => {
    const User = require('./User')(sequelize, DataTypes); // 确保 User 模型正确导入

    const Friend = sequelize.define('Friend', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        user_id: {
            type: DataTypes.INTEGER,
            references: {
                model: User,
                key: 'id',
            },
        },

        group_friend_id: {
            type: DataTypes.INTEGER,
        },
        type: {
            type: DataTypes.ENUM('user', 'group'),
            allowNull: false,
        },
        status: {
            type: DataTypes.ENUM('pending', 'accepted', 'blocked'),
            defaultValue: 'pending',
        },
        created_at: {
            type: DataTypes.DATE,
            defaultValue: DataTypes.NOW,
        },
    }, {
        timestamps: false,
        primaryKey: false,
    });

    //Friend.removeAttribute('id');
    return Friend;
};
c 复制代码
const Sequelize = require('sequelize');
const sequelize = new Sequelize('chat', 'root', 'asd123', {
    host: 'localhost',
    dialect: 'mysql', // 或 'postgres', 'sqlite', 'mssql'
});

const User = require('./user')(sequelize, Sequelize.DataTypes);
const Friend = require('./friend')(sequelize, Sequelize.DataTypes);
const ChatGroup = require('./chatGroup')(sequelize, Sequelize.DataTypes);
const Blacklist = require('./blacklist')(sequelize, Sequelize.DataTypes);
const Invite = require('./invite')(sequelize, Sequelize.DataTypes);
const Message = require('./message')(sequelize, Sequelize.DataTypes);

const db = {
    sequelize,
    Sequelize,
    User,
    Friend,
    ChatGroup,
    Blacklist,
    Invite,
    Message
};

module.exports = db;
c 复制代码
module.exports = (sequelize, DataTypes) => {
    const User = require('./User')(sequelize, DataTypes); // 确保 User 模型正确导入
    const ChatGroup = require('./ChatGroup')(sequelize, DataTypes); // 确保 ChatGroup 模型正确导入

    const Invite = sequelize.define('Invite', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        inviter_id: {
            type: DataTypes.INTEGER,
            references: {
                model: User,
                key: 'id',
            },
        },
        invitee_id: {
            type: DataTypes.INTEGER,
            references: {
                model: User,
                key: 'id',
            },
        },
        group_id: {
            type: DataTypes.INTEGER,
            references: {
                model: ChatGroup,
                key: 'id',
            },
        },
        group_avatar: {
            type: DataTypes.STRING(255),
        },
        inviter_avatar: {
            type: DataTypes.STRING(255),
        },
        invitee_avatar: {
            type: DataTypes.STRING(255),
        },
        status: {
            type: DataTypes.ENUM('pending', 'accepted', 'declined'),
            defaultValue: 'pending',
        },
        created_at: {
            type: DataTypes.DATE,
            defaultValue: DataTypes.NOW,
        },
    }, {
        timestamps: false,
    });

    return Invite;
};
c 复制代码
module.exports = (sequelize, DataTypes) => {
    const User = require('./User')(sequelize, DataTypes); // 确保 User 模型正确导入
    const ChatGroup = require('./ChatGroup')(sequelize, DataTypes); // 确保 ChatGroup 模型正确导入

    const Message = sequelize.define('Message', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        fid: {
            type: DataTypes.INTEGER,
            references: {
                model: User,
                key: 'id',
            },
            allowNull: true,
        },
        tid: {
            type: DataTypes.INTEGER,
            allowNull: true,
        },

        type: {
            type: DataTypes.ENUM('text', 'audio', 'video', 'image','broadcast','left','kick','withdraw','join'),
            allowNull: false,
        },
        content: {
            type: DataTypes.TEXT,
            allowNull: false,
        },
        avatar: {
            type: DataTypes.STRING(255),
            allowNull: true,
        },
        is_retracted: {
            type: DataTypes.BOOLEAN,
            defaultValue: false,
        },
        retracted_at: {
            type: DataTypes.DATE,
            allowNull: true,
        },
        created_at: {
            type: DataTypes.DATE,
            defaultValue: DataTypes.NOW,
        },

        group_name:{
            type:DataTypes.STRING(10),
            allowNull: true,
        },
        user_name:{
            type: DataTypes.STRING(255),
            allowNull: true,
        },
        sn: {
            type: DataTypes.STRING(255),
            allowNull: true,
        },
    }, {
        timestamps: false,
        underscored: true, // 使用下划线风格以符合数据库字段命名
    });

    return Message;
};
c 复制代码
module.exports = (sequelize, DataTypes) => {
    const User = sequelize.define('User', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        username: {
            type: DataTypes.STRING(50),
            allowNull: false,
            unique: true,
        },
        password: {
            type: DataTypes.STRING(100),
            allowNull: false,
        },
        email: {
            type: DataTypes.STRING(100),
            unique: true,
        },
        avatar_url: {
            type: DataTypes.STRING(255),
        },
        brief: {
            type: DataTypes.TEXT,
        },
        created_at: {
            type: DataTypes.DATE,
            defaultValue: DataTypes.NOW,
        },
    }, {
        timestamps: false,
    });

    return User;
};

聊天代码进行了调整:

c 复制代码
const socketIo = require('socket.io');

function setupChat(server) {
    const io = socketIo(server, {
        cors: {
            origin: "*",
            methods: ["GET", "POST"]
        }
    });
    const groups = {};  // 存储用户与群组的映射
    const kickedUsers = {};  // 存储被踢出用户的信息
    const groupUsers = {};  // 存储每个群组中的用户列表
    io.on('connection', (socket) => {
       // console.log('New user connected');

        // 用户加入群组
        socket.on('joinGroup', ({ groupName, userName }) => {
            if (!kickedUsers[groupName] || !kickedUsers[groupName].includes(userName)) {
                socket.join(groupName);

                // 更新用户列表
                if (!groupUsers[groupName]) {
                    groupUsers[groupName] = [];
                }
                if (!groupUsers[groupName].includes(userName)) {
                    groupUsers[groupName].push(userName);
                }

                groups[socket.id] = { groupName, userName };
                console.log( `${userName} has joined the group`)
                socket.to(groupName).emit('message',{'type':'join',content:`${userName} 加入`});

                // 发送当前用户列表到群组
                io.to(groupName).emit('userList', groupUsers[groupName]);

                console.log(`${userName} joined group ${groupName}`);
            } else {
                socket.emit('message', `您已被踢出群组 ${groupName}, 无法重新加入。`);
            }
        });

        // 发送消息
        socket.on('sendMessage', ({sn, group_name,avatar, content, user_name,type,fid,tid ,created_at}) => {
            if (!kickedUsers[group_name] || !kickedUsers[group_name].includes(user_name)) {
                io.to(group_name).emit('message', {sn, group_name,avatar, content, user_name,type,fid,tid  ,created_at});
                io.emit('message', {sn, group_name,avatar, content, user_name,'type':'broadcast',fid,tid  ,created_at});
                console.log({sn, group_name,avatar, content, user_name,type,fid,tid ,created_at});
            } else {
                socket.emit('message', `您已被踢出群组 ${group_name}, 无法发送消息。`);
            }
        });

        // 踢人
        socket.on('kickUser', ({ groupName, userName }) => {
            for (let id in groups) {
                if (groups[id].userName === userName && groups[id].groupName === groupName) {
                    io.sockets.sockets.get(id).leave(groupName);
                    io.to(groupName).emit('message', `${userName} 已被踢出群组`);

                    // 从用户列表中删除
                    if (groupUsers[groupName]) {
                        groupUsers[groupName] = groupUsers[groupName].filter(user => user !== userName);
                        io.to(groupName).emit('userList', groupUsers[groupName]);
                    }

                    console.log(`${userName} 被踢出群组 ${groupName}`);
                    if (!kickedUsers[groupName]) {
                        kickedUsers[groupName] = [];
                    }
                    kickedUsers[groupName].push(userName);
                    break;
                }
            }
        });

        // 用户断开连接
        socket.on('disconnect', () => {
            if (groups[socket.id]) {
                const { groupName, userName } = groups[socket.id];
                // 从用户列表中删除
                if (groupUsers[groupName]) {
                    groupUsers[groupName] = groupUsers[groupName].filter(user => user !== userName);
                    io.to(groupName).emit('userList', groupUsers[groupName]);
                }
                socket.to(groupName).emit('message', {'type':'left',content:`${userName} 离开`});
                delete groups[socket.id];
                console.log(`${userName} 已离开群组 ${groupName}`);
            }
        });
    });

    return io;
}

module.exports = setupChat;

启动页为:

c 复制代码
const express = require('express');
const http = require('http');
const cors = require('cors');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const db = require('./src/models');
const { User, Friend, ChatGroup, Blacklist, Invite, Message, sequelize } = require('./src/models');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const { Op } = require('sequelize');
const setupChat = require('./src/chat');

const app = express();
const server = http.createServer(app);

// 设置聊天
const io = setupChat(server);
// 使用中间件
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const port = 3000;
const SECRET_KEY = 'mykeyssssssd%#@##$#@$#$@$#@$$'; // 更改为实际的密钥

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  if (token == null) return res.json({code:-1,message:'expire'});
  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) return res.json({code:-1,message:'expire'});
    req.user = user;
    next();
  });
};




// 设置文件存储配置
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    cb(null, `${Date.now()}-${file.originalname}`);
  }
});

const upload = multer({ storage: storage });

// 创建 uploads 目录


const uploadsDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadsDir)) {
  fs.mkdirSync(uploadsDir);
}

// 文件上传接口
app.post('/upload', upload.single('avatar'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }
  const fileUrl = `http://localhost:3000/uploads/${req.file.filename}`;
  res.json({code:0,message:'图片上传成功', data: fileUrl });
});

// 提供静态文件服务
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

app.post('/register', async (req, res) => {
  try {
    const { username, password } = req.body;
    console.log('Received registration request for:', username);

    if (username.length < 6 || username.length > 10 || password.length < 6 || password.length > 10) {
      console.log('Invalid username or password length');
      return res.status(400).json({ error: 'Username and password must be 6-10 characters long' });
    }

    const hashedPassword = await bcrypt.hash(password, 10);
    console.log('Password hashed successfully');

    console.log('Attempting to create user in database');
    const user = await User.create({ username, password: hashedPassword });
    console.log('User created:', user.toJSON());

    res.status(201).json({ message: 'User registered successfully' });
  } catch (error) {
    console.error('Error in registration:', error);
    if (error.name === 'SequelizeUniqueConstraintError') {
      return res.status(400).json({ error: 'Username already exists' });
    }
    res.status(500).json({ error: error.message || 'An error occurred during registration' });
  }
});

// 用户登录
app.post('/login', async (req, res) => {
  try {
    const { username, password } = req.body;
    console.log({ username, password });

    if (username.length < 6 || username.length > 10 || password.length < 6 || password.length > 10) {
      return res.status(400).json({ code: 0, msg: '用户名和密码必须是6-10个字符长', data: null });
    }

    const user = await User.findOne({ where: { username } });
    console.log(user);
    if (!user || !await bcrypt.compare(password, user.password)) {
      return res.status(401).json({ code: 0, msg: '无效凭证', data: null });
    }

    const token = jwt.sign({ id: user.id }, SECRET_KEY, { expiresIn: '1h' });
    res.json({ code: 1, msg: '登录成功', data: { token } });
  } catch (error) {
    console.log(error);
    res.status(500).json({ code: 0, msg: '登录时出错', data: null });
  }
});

// 添加好友
app.post('/addFriend', authenticateToken, async (req, res) => {
  const { friendId } = req.body;
  const userId = req.user.id;

  try {
    const friend = await User.findByPk(friendId);

    if (!friend){
      return res.status(404).json({ error:'未找到朋友' });
    }

    await Friend.create({ user_id: userId, friend_id: friendId });
    res.json({ message:'成功添加好友' });
  }catch (error){
    res.status(500).json({ error:'添加好友时出错' });
  }
});

// 获取好友列表
app.get('/friends', authenticateToken, async (req, res) => {
  try {
    const userId = req.user.id;
    const { page = 1, perPage = 20 } = req.query;
    // 计算分页参数
    const offset = (page - 1) * perPage;
    const limit = parseInt(perPage);
    const friends = await Friend.findAndCountAll({
      where: {
        [Op.or]: [
          {
            type: 'user',
            [Op.or]: [
              { user_id: userId },
              { group_friend_id: userId }
            ]
          },
          {
            type: 'group',
            user_id: userId
          }
        ]
      },
        offset,
        limit
    });

    // 判断是否还有更多数据
    const hasMoreFriends = friends.count > offset + limit;

    const friendDataPromises = friends.rows.map(async (item) => {
      if (item.type == 'user') {
        const user = await User.findOne({
          where: { id: item.group_friend_id },
          attributes: ['id', 'username', 'email', 'avatar_url', 'brief']
        });
        return { ...item.get(), user };
      }
      if (item.type == 'group') {
        const group = await ChatGroup.findOne({
          where: { id: item.group_friend_id },
          attributes: ['id', 'name', 'description', 'avatar_url']
        });
        return { ...item.get(), group };
      }
    });
    const friendsData = await Promise.all(friendDataPromises);
    return res.json({
      code: 0,
      message: '返回成功',
      data: friendsData,
      hasMoreFriends
    });
  } catch (error) {

    return res.json({ code: -1, message: error });
  }
});

app.get('/checkFriend', authenticateToken, async (req, res) => {
  try {
    const userId = req.user.id;
    let { Id } = req.query;



    if( /^g_\d+$/.test(Id)){
      //是group
      Id = Id.replace("g_", "");
      const isFriend =  await Friend.findOne({
        where: { user_id: userId, group_friend_id:Id},
        attributes: ['id', 'user_id', 'group_friend_id','type']
      });
      if(isFriend){
        return res.json({ code: 0, message: 'User is a friend', data: isFriend });
      }else{
        return res.json({ code: 1, message: 'User is not a friend' });
      }
    }




    const isFriend =  await Friend.findOne({
      where: { id: Id,type:'user' },
      attributes: ['id', 'user_id', 'group_friend_id','type']
    });

    if (isFriend){

        if(isFriend.user_id==userId ||isFriend.group_friend_id==userId){
          return res.json({ code: 0, message: 'User is a friend', data: isFriend });
        }
        return res.json({ code: 1, message: 'User is not a friend' });

      }else{

        return res.json({ code: 1, message: 'User is not a friend' });

      }



  } catch (error) {
    return res.json({ code: -1, message: error.message });
  }
});

app.post('/addmessage', authenticateToken, async (req, res) => {
  const { sn, group_name, avatar, content, type, user_name, fid, tid } = req.body;

  try {
    // 插入新消息
    const newMessage = await Message.create({
      sn,
      group_name,
      avatar,
      content,
      type,
      user_name,
      fid,
      tid,
      is_retracted:0,
    });

    res.json({ code: 0, message: '消息添加成功', data: newMessage });
  } catch (error) {
    console.error('添加消息失败:', error);
    res.json({ code: -1, message: '消息添加失败' });
  }
});



// 获取用户信息接口
app.get('/user', authenticateToken, async (req, res) => {

  try {
    // 确保 `req.user` 和 `req.user.id` 存在
    if (!req.user || !req.user.id) {
      return res.status(400).json({ error: '用户 ID 未找到' });
    }


    const user = await User.findByPk(req.user.id, {
      attributes: ['id', 'username', 'email', 'avatar_url', 'brief', 'created_at']
    });
    if (!user) {
      //console.warn("User not found with ID:", req.user.id);
      return res.status(404).json({ error: '用户未找到' });
    }

    res.json(user);
  } catch (error) {

    res.status(500).json({ error: '获取用户信息时出错' });
  }
});

app.post('/user/updateAvatar', authenticateToken, async (req, res) => {
  const { avatar_url } = req.body;

  try {
    await User.update({ avatar_url }, { where: { id: req.user.id } });

    res.json({code:0, message:'头像更新成功',data:avatar_url });
  } catch (error) {

    res.json({code:-1, message:'头象更新失败A'});
  }
});
app.post('/user/updateBrief', authenticateToken, async (req, res) => {
  const { brief } = req.body;
  const userId = req.user.id; // Assuming the user ID is stored in the token

  if (!brief) {
    return res.status(400).json({ code: 1, message: 'Brief is required' });
  }

  try {
    // Update the user's brief in the database
    const [affectedRows] = await User.update(
        { brief }, // Fields to update
        { where: { id: userId } } // Condition to find the user
    );

    if (affectedRows === 0) {
      return res.status(404).json({ code: 1, message: 'User not found' });
    }

    res.status(200).json({ code: 0, message: 'Brief updated successfully' });
  } catch (error) {
    //console.error('Error updating brief:', error);
    res.status(500).json({ code: 1, message: 'Internal server error' });
  }
});



// 创建群组接口
app.post('/groups', authenticateToken, upload.single('avatar'), async (req, res) => {
  try {
    //console.log('req.body:', req.body); // 调试输出请求体
    const { name, description ,avatar_url} = req.body;
    const owner_id = req.user.id;




    if (name.length < 3 || name.length > 10) {
      return res.status(400).json({ error: '群名必须在3到10个汉字之间' });
    }

    if (description.length < 5 || description.length > 50) {
      return res.status(400).json({ error: '群说明必须在5到50个汉字之间' });
    }
    const existingGroup = await ChatGroup.findOne({ where: { name } });
    if (existingGroup) {
      return  res.json({code:-1, message: '群名已存在' });
    }
    const group = await ChatGroup.create({ name, description, avatar_url, owner_id });
    await Friend.create({ user_id: owner_id, group_friend_id: group.id, type: 'group', status: 'accepted' });
    return res.json({ code: 0, message: '群组创建成功', group });
  } catch (error) {
    return  res.json({code:-1, message: '创建群组时出错' });
  }
});

// 获取群组信息接口
app.get('/groups/:id', authenticateToken, async (req, res) => {
  try {
    const group = await ChatGroup.findByPk(req.params.id);

    if (!group){
      return res.status(404).json({ error:'群组未找到' });
    }

    res.json(group);
  }catch (error){
    res.status(500).json({ error:'获取群组信息时出错' });
  }
});

app.get('/groups', authenticateToken, async (req, res) => {
  try {
    const userId = req.user.id;
    //console.log('userId',userId)
    const groups = await ChatGroup.findAll({ where: { owner_id: userId } });

    //console.log('groups',groups)
    return res.json({ code: 0, data: groups });
  } catch (error) {
    //console.error('Error fetching groups:', error);
    return res.json({ code: -1, message: 'Failed to fetch groups' });
  }
});

app.post('/group/update', authenticateToken, async (req, res) => {
  try {
    const { id, name, description, avatar_url } = req.body;

    // Check if the group name already exists and is not the current group being updated
    const existingGroup = await ChatGroup.findOne({
      where: {
        name,
        id: { [Op.ne]: id } // Use Op from Sequelize
      }
    });


    //
    // console.log('Op:', Op); // Should not be undefined
    // console.log('Query:', {
    //   name,
    //   id: { [Op.ne]: id }
    // });


    if (existingGroup) {
      return res.json({ code: -2, message: '群名重复' });
    }

    await ChatGroup.update({ name, description, avatar_url }, { where: { id } });
    return res.json({ code: 0, message: '群名创建成功' });
  } catch (error) {
    // console.error('Error updating group:', error);
    return res.json({ code: -1, message: '创建失败' });
  }
});


app.post('/user/updateEmail', authenticateToken, async (req, res) => {
  try {
    const userId = req.user.id;
    const { email } = req.body;
    await User.update({ email }, { where: { id: userId } });
    return res.json({ code: 0, message: 'Email updated successfully' });
  } catch (error) {
    // console.error('Error updating email:', error);
    return res.json({ code: -1, message: 'Failed to update email' });
  }
});
app.get('/user', authenticateToken, async (req, res) => {
  try {
    const user = await User.findByPk(req.user.id, {
      attributes: ['id', 'username', 'email', 'avatar']
    });
    if (!user) {
      return res.status(404).json({ error: '用户未找到' });
    }
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: '获取用户信息时出错' });
  }
});
app.post('/logout', authenticateToken, (req, res) => {
  // 这里可以添加一些服务器端的登出逻辑
  // 比如清除服务器端的 session 或标记 token 为无效
  res.json({ message: '成功登出' });
});
const PORT = process.env.PORT || port;

server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
相关推荐
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
数据猎手小k3 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
你的小103 小时前
JavaWeb项目-----博客系统
android
清灵xmf3 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据4 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
风和先行4 小时前
adb 命令查看设备存储占用情况
android·adb
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
334554324 小时前
element动态表头合并表格
开发语言·javascript·ecmascript