我尝试在我的订餐网络应用程序中通过客户和供应商门户发送实时推送通知。由于以下 2 个操作,屏幕角落应该会出现一个小的实时通知横幅:
但是,实时通知并没有完全发挥作用。
在首次运行期间,套接字始终不工作。它仅在我刷新任一客户端(即:客户方或供应商方)后立即起作用。我不确定这是由于我设置客户端-服务器连接的方式还是因为 useEffect。
目前我只使用了 .emit 和 .on 以及两个名为“orderStatusUpdate”和“newOrder”的事件。如果你们中有人能帮助我解决这个问题,我将不胜感激。如果您需要更多信息,我可以提供更多代码片段或 GitHub 存储库链接。预先感谢!
===服务器====
服务器.ts
import dotenv from "dotenv";
import express, { Express } from "express";
import http from "http";
import { Server } from "socket.io";
import cors from "cors"; //enables anyone to use the api
import helmet from "helmet";
import rateLimit from "express-rate-limit";
import bodyParser from "body-parser";
dotenv.config();
import auth from "./src/routers/auth";
import items from "./src/routers/items";
import carts from "./src/routers/carts";
import orders from "./src/routers/orders";
import constraint from "./src/routers/constraint";
import image from "./src/routers/image";
//allows api to be called 100 times within 15min interval
const limit = rateLimit({
windowMs: 1 * 60 * 1000, //1min
max: 1000,
standardHeaders: true,
legacyHeaders: false,
});
const app: Express = express();
const server = http.createServer(app);
export const io = new Server(server, {
cors: {
origin: "*",
},
});
// io.on("connection", () => {
// console.log("user connected");
// });
app.use(cors());
app.use(helmet());
app.use(limit);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use("/auth", auth);
app.use("/api", items);
app.use("/api", carts);
app.use("/api", orders);
app.use("/api", constraint);
app.use("/api", image);
const PORT = process.env.PORT || 5001;
// app.listen(PORT, () => {
// console.log(`server has started on port ${PORT}`);
// });
server.listen(PORT, () => {
console.log(`server has started on port ${PORT}`);
});
orders.ts(使用io.emit触发客户端useEffect)
import { Request, Response } from "express";
import pool from "../db/db";
import { io } from "../../server";
// orders
.....
const updateOrder = async (req: Request, res: Response) => {
try {
const {
status,
rating,
review,
is_active,
}: { status: String; rating: Number; review: String; is_active: Boolean } =
req.body;
if ("status" in req.body) {
const updateStatus = await pool.query(
"UPDATE orders SET status = $1 WHERE uuid = $2 RETURNING user_id",
[status, req.params.order_id]
);
// use socket.io to trigger the frontend receiver to trigger action of fetching new orderInfo
io.emit("orderStatusUpdate", updateStatus.rows[0].user_id);
}
if ("rating" in req.body) {
try {
// Begin a transaction
await pool.query("BEGIN");
// Update the order's rating and get the vendor_id
const updateResult = await pool.query(
"UPDATE orders SET rating = $1 WHERE uuid = $2 RETURNING vendor_id",
[rating, req.params.order_id]
);
const thisVendorId = updateResult.rows[0].vendor_id;
// Calculate the average rating
const avgRatingResult = await pool.query(
"SELECT ROUND(AVG(rating), 2) FROM orders WHERE vendor_id = $1",
[thisVendorId]
);
const avgRating = avgRatingResult.rows[0].round;
// Update the vendor's details with the calculated average rating
await pool.query(
"UPDATE vendor_details SET rating = $1 WHERE vendor_id = $2",
[avgRating, thisVendorId]
);
// Commit the transaction
await pool.query("COMMIT");
} catch (error: any) {
await pool.query("ROLLBACK");
console.error(error.message);
res
.status(500)
.json({ error: "An error occurred while updating the rating" });
}
}
if ("review" in req.body) {
await pool.query("UPDATE orders SET review = $1 WHERE uuid = $2", [
review,
req.params.order_id,
]);
}
if ("is_active" in req.body) {
await pool.query("UPDATE orders SET is_active = $1 WHERE uuid = $2", [
is_active,
req.params.order_id,
]);
}
res.status(201).json({ status: "ok", msg: "Order updated" });
} catch (error: any) {
console.log(error.message);
res.json({ status: "error", msg: "Update order failed" });
}
};
// items_orders
const createItemsOrders = async (req: Request, res: Response) => {
try {
// copy item_id, item_price, quantity_ordered and user_note from carts_items; insert order_id
const cart_id: String = req.body.cart_id;
const order = await pool.query(
"INSERT INTO items_orders (item_id, order_id, item_price, quantity_ordered, user_note) SELECT item_id, $1, item_price, quantity_ordered, user_note FROM carts_items WHERE cart_id = $2 AND is_deleted = FALSE RETURNING *",
[req.params.order_id, cart_id]
);
const items_id = order.rows.reduce((acc, item) => {
acc.push(item.item_id);
return acc;
}, []);
const vendor_id = await pool.query(
"SELECT vendor_id FROM orders WHERE uuid = $1 ",
[req.params.order_id]
);
io.emit("newOrder", vendor_id.rows[0].vendor_id);
res.status(201).json({
msg: "Items_orders created",
items_orders: order.rows,
items_id,
});
} catch (error: any) {
console.log(error.message);
res.json({ status: "error", msg: "Create items_orders failed" });
}
};
.....
export {
createOrder,
updateOrder,
createItemsOrders,
getItemsOrdersByOrderId,
getItemsOrdersByVendorId,
getItemsOrdersByUserId,
getLastOrderByUserId,
getActiveOrdersByVendorId,
};
====客户=====
socket.tsx(导入和设置io)
import { io } from "socket.io-client";
export const socket = io(import.meta.env.VITE_SERVER);
App.tsx(useEffect中的socket.on用于监听来自服务器的事件)
import { Route, Routes } from "react-router-dom";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import UserContext from "./context/user";
import { socket } from "./socket";
.......
import useFetch from "./hooks/useFetch";
import useFetchImg from "./hooks/useFetchImg";
import {
OrderInfo,
Props,
SnackbarMessage,
data,
userInfoType,
} from "./interfaces";
function App() {
const fetchData = useFetch();
const fetchDataImg = useFetchImg();
const navigate = useNavigate();
.......
// other states
const [vendorId, setVendorId] = useState<String>("");
const [vendorInfo, setVendorInfo] = useState<userInfoType>({});
const [haveActiveOrder, setHaveActiveOrder] = useState<boolean>(false);
const [activeOrderId, setActiveOrderId] = useState<String[]>([]);
const [cartItemInfo, setCartItemInfo] = useState<Props>({});
const [orderInfo, setOrderInfo] = useState<OrderInfo>([]);
const [imageUrl, setImageUrl] = useState<string>("");
const [snackPack, setSnackPack] = useState<readonly SnackbarMessage[]>([]);
.......
const getCustomerLastOrder = async () => {
const res: data = await fetchData(
"/api/orders/items/active/user_id",
"POST",
{
user_id: userId,
},
accessToken
);
if (res.ok) {
if (res.data[0].length) {
setOrderInfo(res.data);
setHaveActiveOrder(true);
setActiveOrderId([res.data[0][0].order_id]);
}
} else {
alert(JSON.stringify(res.data));
}
};
const getVendorActiveOrder = async () => {
const res: data = await fetchData(
"/api/orders/items/active/vendor_id",
"POST",
{
vendor_id: userId,
},
accessToken
);
if (res.ok) {
if (res.data.length) {
setOrderInfo(res.data);
setHaveActiveOrder(true);
} else {
setOrderInfo([]);
setHaveActiveOrder(false);
}
} else {
alert(JSON.stringify(res.data));
}
};
.........
// useEff to control instance of sockets received from server to 2
// was 6-8 times before useEff
useEffect(() => {
socket.on("orderStatusUpdate", (user_id) => {
if (userId === user_id) {
console.log("CUSTOMER SOCKET");
getCustomerLastOrder();
// activate snackbar for notifications
setSnackPack((prev) => [
...prev,
{ message: "test", key: new Date().getTime() },
]);
}
});
socket.on("newOrder", (vendor_id) => {
if (userId === vendor_id) {
console.log("VENDOR SOCKET");
getVendorActiveOrder();
// activate snackbar for notifications
setSnackPack((prev) => [
...prev,
{ message: "test", key: new Date().getTime() },
]);
}
});
}, [socket]);
.......
return (
<div>
.........
export default App;
我认为在客户端中你需要首先启动套接字,我在你的代码中没有看到它,也许你在其他地方有它,因为你没有发送整个代码?