You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在宠物交友网站中实现用户与狗狗、活动的关联?

宠物交友网站用户-狗狗-活动关联问题解决方案

问题背景

开发宠物交友网站时,在实现用户与狗狗、活动的关联功能上遇到阻碍,需求是让已登录用户能够查看自己拥有的狗狗以及创建的活动,需要构建有效的关联结构并修正现有实现的问题。

现有代码

User模型

const { Schema, model } = require('mongoose');

const userSchema = new Schema(
    {
        firstName: {
            type: String,
            unique: true,
            required: [true, "First Name is required"],
            trim: true
        },
        lastName: {
            type: String,
            unique: true,
            required: [true, "Last Name is required"],
            trim: true
        },
        email: {
            type: String,
            lowercase: true,
            required: [true, "Email is required"],
            unique: true,
        },
        passwordHash: {
            type: String,
            required: [true, "HashPassword is required"],
            trim: true,
        },
        socialMedia: {
            type: String,
            enum: ['Instagram', 'Meta', 'X', 'Linkedin']
        },
        birthday: {
            type: Date,
            required: [true, 'Age is required'],
        },
        address: { type: Schema.Types.ObjectId, ref: 'Address' },
        civilStatus: {
            type: String,
            enum: ['single', 'married', 'just for fun']
        },
        dogs: [{ type: Schema.Types.ObjectId, ref: 'Dog' }],
        events: [{ type: Schema.Types.ObjectId, ref: 'Event' }]
    },
    {
        timestamps: true,
    }
);

const User = model('User', userSchema);

module.exports = User;

Dog模型

const { Schema, model } = require("mongoose");

const dogSchema = new Schema(
  {
    name: {
      type: String,
      required: [true, "Name is required."],
      trim: true,
    },
    picture: {
      type: String,
      required: [true, "Picture is required."],
      trim: true,
    },
    age: {
      type: Number,
      required: [true, "Age is required."],
    },
    breed: {
      type: String,
      required: [true, "Breed is required."],
    },
    civilStatus: {
      type: String,
      required: [true, "Status is required."],
      enum: ["single", "married", "looking to breed"],
    },
    size: {
      type: String,
      required: [true, "Size is required."],
      enum: ["big", "medium", "small"],
    },
    owner: {
      type: Schema.Types.ObjectId,
      ref: "User",
    }
  },
  {
    timestamps: true,
  }
);

const Dog = model("Dog", dogSchema);

module.exports = Dog;

用户路由

router.get("/event/:eventId", async (req, res, next) => {
  const testing = req.params;

  console.log('id', testing);

  try {
    const userList = await User.find().populate('events');

    console.log('userList:', userList);

    res.status(200).json(userList);
  } catch (error) {
    next(error);
  }
});

router.get("/dog/:dogId", async (req, res, next) => {
  console.log(req.params)

  try {
    const userList = await User.find().populate("dogs");

    res.status(200).json(userList);
  } catch (error) {
    next(error);
  }
});

前端展示代码

const [dogs, setDogs] = useState([]);
        const [users, setUsers] = useState([]);
        const fetchDogs = async () => {
            try {
                const dogResponse = await fetch(`${import.meta.env.VITE_API_URL}/api/dogs`);
                if (dogResponse.ok) {
                    const dogsData = await dogResponse.json();
                    setDogs(dogsData)
                }
            } catch (error) {
                console.log(error);
            }
        }

        const fetchUsers = async () => {
            try {
                const userResponse = await fetch(`${import.meta.env.VITE_API_URL}/api/users`);
                if (userResponse.ok) {
                    const usersData = await userResponse.json();
                    setUsers(usersData)
                }
            } catch (error) {
                console.error("Error fetching data:", error);
            }
        }

        useEffect(() => {
            fetchDogs()
        }, []);

        useEffect(() => {
            fetchUsers()
        }, []);

        let userDogs = combineData(dogs, users)

        function combineData(dogs, users) {
            return dogs.map((dog) => {
                const owner = users.find((user) => user.id === dog.userId);
                return {...dog, owner};
            });
        }

        return (
            <>
            <div>
                    {userDogs.map((dog) => (
                            <DogCard  key={dog.name} {...dog} />
                    ))}
            </div>
            </>
        );

核心问题分析

  1. 模型字段不匹配:Dog模型中关联用户的字段是owner,但前端代码里用dog.userId进行匹配,导致关联失败。
  2. 双向关联未同步:创建狗狗时没有同步更新对应用户的dogs数组,导致User和Dog的关联断裂。
  3. 路由逻辑错误:现有路由查询所有用户并关联资源,不是针对当前登录用户返回专属数据,不符合需求。
  4. User模型不合理约束:firstName和lastName设置为unique,重名场景下会导致用户创建失败,应移除该约束。

解决方案实现

1. 修正User模型约束

移除firstName和lastName的unique属性:

firstName: {
    type: String,
    required: [true, "First Name is required"],
    trim: true
},
lastName: {
    type: String,
    required: [true, "Last Name is required"],
    trim: true
},

2. 优化后端路由(针对登录用户)

添加获取当前用户专属资源的路由,假设登录中间件已将用户信息挂载到req.user:

// 获取当前用户的狗狗列表
router.get("/me/dogs", async (req, res, next) => {
  try {
    const currentUser = await User.findById(req.user._id).populate('dogs');
    res.status(200).json(currentUser.dogs);
  } catch (error) {
    next(error);
  }
});

// 获取当前用户创建的活动列表
router.get("/me/events", async (req, res, next) => {
  try {
    const currentUser = await User.findById(req.user._id).populate('events');
    res.status(200).json(currentUser.events);
  } catch (error) {
    next(error);
  }
});

3. 同步双向关联(创建Dog时)

在Dog的创建路由中,同步更新对应用户的dogs数组:

router.post("/dogs", async (req, res, next) => {
  try {
    // 创建狗狗并关联当前用户
    const newDog = await Dog.create({ ...req.body, owner: req.user._id });
    // 更新用户的狗狗列表
    await User.findByIdAndUpdate(
      req.user._id,
      { $push: { dogs: newDog._id } },
      { new: true }
    );
    res.status(201).json(newDog);
  } catch (error) {
    next(error);
  }
});

4. 前端代码优化

直接调用当前用户的专属接口,无需拉取所有用户和狗狗数据:

const [userDogs, setUserDogs] = useState([]);

const fetchUserDogs = async () => {
  try {
    // 从localStorage获取登录token
    const token = localStorage.getItem('authToken');
    const response = await fetch(`${import.meta.env.VITE_API_URL}/api/users/me/dogs`, {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });
    if (response.ok) {
      const dogsData = await response.json();
      setUserDogs(dogsData);
    }
  } catch (error) {
    console.error("获取用户狗狗失败:", error);
  }
};

useEffect(() => {
  fetchUserDogs();
}, []);

return (
  <div>
    {userDogs.map((dog) => (
      <DogCard key={dog._id} {...dog} />
    ))}
  </div>
);

内容的提问来源于stack exchange,提问作者Adrian Bueno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 18:09:50