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

基于Angular 4、Node.js的实时通知实现示例及项目适配请求

Hey there! Let's tackle your real-time notification needs for both your current Symfony 3 + Angular 4 neighborhood sharing project and the Angular 4 + Node.js + MySQL setup you're curious about.


1. Real-time Notifications for Your Symfony 3 + Angular 4 Neighborhood Sharing Project

We'll use Socket.io as the real-time layer (with a lightweight Node.js relay server, since Symfony doesn't have native WebSocket support out of the box) to send instant alerts when a user clicks the "Borrow" button.

Architecture Overview

  1. Angular frontend clicks "Borrow" → sends request to Symfony backend
  2. Symfony processes the borrow request, then triggers a real-time alert via the Node.js Socket.io server
  3. Node.js server pushes the alert to the item owner's Angular client (if online)
  4. If the owner is offline, we store the notification in Symfony's database for later retrieval

Step 1: Set Up the Node.js Socket.io Relay Server

First, create a simple Node.js server to handle WebSocket connections:

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
  cors: {
    origin: "http://your-angular-frontend-url", // Replace with your frontend URL
    methods: ["GET", "POST"]
  }
});

// Map user IDs to their active socket connections
const userSocketMap = new Map();

// Handle client connections
io.on('connection', (socket) => {
  // Register user when they log in
  socket.on('register-user', (userId) => {
    userSocketMap.set(userId, socket);
    console.log(`User ${userId} connected`);
  });

  // Clean up on disconnect
  socket.on('disconnect', () => {
    for (const [userId, sock] of userSocketMap.entries()) {
      if (sock === socket) {
        userSocketMap.delete(userId);
        console.log(`User ${userId} disconnected`);
        break;
      }
    }
  });
});

// Endpoint for Symfony to trigger notifications
app.use(express.json());
app.post('/send-alert', (req, res) => {
  const { recipientId, message, data } = req.body;
  const targetSocket = userSocketMap.get(recipientId);

  if (targetSocket) {
    targetSocket.emit('borrow-request', {
      message,
      data: { ...data, timestamp: new Date().toISOString() }
    });
    res.status(200).json({ success: true });
  } else {
    res.status(404).json({ success: false, message: 'Recipient not online' });
  }
});

const PORT = 3001;
server.listen(PORT, () => console.log(`Socket.io relay running on port ${PORT}`));

Step 2: Symfony Backend Integration

Update your Symfony controller to handle borrow requests and trigger notifications:

// src/AppBundle/Controller/ItemController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use GuzzleHttp\Client;
use AppBundle\Entity\Notification;

class ItemController extends Controller
{
    public function borrowAction(Request $request)
    {
        // 1. Extract request data
        $itemId = $request->request->get('itemId');
        $borrower = $this->getUser();
        
        // 2. Fetch item and its owner
        $em = $this->getDoctrine()->getManager();
        $item = $em->getRepository('AppBundle:Item')->find($itemId);
        $owner = $item->getOwner();
        
        // 3. Save borrow request to database (customize this logic)
        $borrowRequest = new \AppBundle\Entity\BorrowRequest();
        $borrowRequest->setItem($item);
        $borrowRequest->setBorrower($borrower);
        $borrowRequest->setStatus('pending');
        $em->persist($borrowRequest);
        
        // 4. Send real-time alert (fallback to database if offline)
        $client = new Client();
        try {
            $client->post('http://your-node-server-url:3001/send-alert', [
                'json' => [
                    'recipientId' => $owner->getId(),
                    'message' => "Someone wants to borrow your item: {$item->getName()}",
                    'data' => ['itemId' => $itemId, 'borrowerName' => $borrower->getName()]
                ]
            ]);
        } catch (\Exception $e) {
            // Save notification to database for later retrieval
            $notification = new Notification();
            $notification->setRecipient($owner);
            $notification->setMessage("Someone wants to borrow your item: {$item->getName()}");
            $notification->setData(['itemId' => $itemId, 'borrowerName' => $borrower->getName()]);
            $em->persist($notification);
            
            $this->get('logger')->error('Failed to send real-time alert: ' . $e->getMessage());
        }
        
        $em->flush();
        return new JsonResponse(['success' => true]);
    }
}

Step 3: Angular Frontend Implementation

First install the Socket.io client:

npm install socket.io-client@2.x --save

Create a reusable Socket service:

// src/app/services/socket.service.ts
import { Injectable } from '@angular/core';
import * as io from 'socket.io-client';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class SocketService {
  private socket;
  private serverUrl = 'http://your-node-server-url:3001';

  constructor() {
    this.socket = io(this.serverUrl);
  }

  // Register user with the Socket server
  registerUser(userId: number): void {
    this.socket.emit('register-user', userId);
  }

  // Listen for borrow request alerts
  onBorrowRequest(): Observable<any> {
    return new Observable(observer => {
      this.socket.on('borrow-request', (alert) => {
        observer.next(alert);
      });
    });
  }

  disconnect(): void {
    this.socket.disconnect();
  }
}

Update your navbar component to register the user and listen for alerts:

// src/app/components/navbar/navbar.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SocketService } from '../../services/socket.service';
import { AuthService } from '../../services/auth.service';

@Component({
  selector: 'app-navbar',
  templateUrl: './navbar.component.html'
})
export class NavbarComponent implements OnInit, OnDestroy {
  private currentUserId: number;

  constructor(private socketService: SocketService, private authService: AuthService) {
    this.currentUserId = this.authService.getCurrentUser().id;
  }

  ngOnInit() {
    this.socketService.registerUser(this.currentUserId);
    
    // Listen for new borrow requests
    this.socketService.onBorrowRequest().subscribe(alert => {
      // Show alert (use a toast library like ngx-toastr for better UX)
      alert(`Alert: ${alert.message}\nBorrower: ${alert.data.borrowerName}`);
    });
  }

  ngOnDestroy() {
    this.socketService.disconnect();
  }
}

Finally, add the "Borrow" button logic to your item component:

// src/app/components/item/item.component.ts
import { Component, Input } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-item',
  templateUrl: './item.component.html'
})
export class ItemComponent {
  @Input() item: any;

  constructor(private http: HttpClient) {}

  onBorrowClick() {
    this.http.post('/api/items/borrow', { itemId: this.item.id })
      .subscribe(() => {
        alert('Borrow request submitted!');
      }, (error) => {
        alert('Failed to submit request. Please try again.');
      });
  }
}

2. Full Real-time Notification Stack with Angular 4 + Node.js + MySQL

For this setup, Node.js acts as both the API backend and WebSocket server, with MySQL storing users, items, and notification records (including unread alerts).

Step 1: Node.js Backend Setup

First, install dependencies:

npm install express socket.io mysql2 cors body-parser --save

Create a database connection:

// db.js
const mysql = require('mysql2/promise');

const pool = mysql.createPool({
  host: 'localhost',
  user: 'your-db-user',
  password: 'your-db-password',
  database: 'neighborhood_share',
  connectionLimit: 10
});

module.exports = pool;

Create the combined API + Socket server:

// server.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const cors = require('cors');
const bodyParser = require('body-parser');
const pool = require('./db');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
  cors: { origin: "http://your-angular-frontend-url" }
});

app.use(cors());
app.use(bodyParser.json());

const userSocketMap = new Map();

// Handle Socket connections
io.on('connection', async (socket) => {
  socket.on('register-user', async (userId) => {
    userSocketMap.set(userId, socket);
    
    // Push unread notifications to the user
    const [unreadAlerts] = await pool.execute(
      'SELECT * FROM notifications WHERE recipient_id = ? AND is_read = 0 ORDER BY created_at DESC',
      [userId]
    );
    if (unreadAlerts.length > 0) {
      socket.emit('unread-notifications', unreadAlerts);
    }
  });

  // Mark notification as read
  socket.on('mark-read', async (notificationId) => {
    await pool.execute('UPDATE notifications SET is_read = 1 WHERE id = ?', [notificationId]);
  });

  socket.on('disconnect', () => {
    for (const [userId, sock] of userSocketMap.entries()) {
      if (sock === socket) {
        userSocketMap.delete(userId);
        break;
      }
    }
  });
});

// API: Submit borrow request
app.post('/api/borrow', async (req, res) => {
  const { itemId, borrowerId } = req.body;
  
  try {
    // Get item owner
    const [itemRows] = await pool.execute('SELECT owner_id, name FROM items WHERE id = ?', [itemId]);
    if (itemRows.length === 0) return res.status(404).json({ success: false });
    
    const ownerId = itemRows[0].owner_id;
    const itemName = itemRows[0].name;
    
    // Save borrow request
    await pool.execute(
      'INSERT INTO borrow_requests (item_id, borrower_id, status) VALUES (?, ?, ?)',
      [itemId, borrowerId, 'pending']
    );
    
    // Create notification record
    const [notificationResult] = await pool.execute(
      'INSERT INTO notifications (recipient_id, message, data, is_read) VALUES (?, ?, ?, ?)',
      [
        ownerId,
        `Someone wants to borrow your item: ${itemName}`,
        JSON.stringify({ itemId, borrowerId }),
        0
      ]
    );
    
    // Push real-time alert
    const targetSocket = userSocketMap.get(ownerId);
    if (targetSocket) {
      targetSocket.emit('borrow-request', {
        id: notificationResult.insertId,
        message: `Someone wants to borrow your item: ${itemName}`,
        data: { itemId, borrowerId },
        is_read: 0,
        created_at: new Date().toISOString()
      });
    }
    
    res.status(200).json({ success: true });
  } catch (error) {
    console.error(error);
    res.status(500).json({ success: false });
  }
});

// API: Get all user notifications
app.get('/api/notifications/:userId', async (req, res) => {
  const userId = req.params.userId;
  const [notifications] = await pool.execute(
    'SELECT * FROM notifications WHERE recipient_id = ? ORDER BY created_at DESC',
    [userId]
  );
  res.status(200).json(notifications);
});

const PORT = 3000;
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Step 2: Angular Frontend Integration

The Angular setup is similar to the previous example, but update the Socket service to include API calls:

// src/app/services/socket.service.ts
import { Injectable } from '@angular/core';
import * as io from 'socket.io-client';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class SocketService {
  private socket;
  private serverUrl = 'http://your-node-server-url:3000';

  constructor(private http: HttpClient) {
    this.socket = io(this.serverUrl);
  }

  registerUser(userId: number): void {
    this.socket.emit('register-user', userId);
  }

  onBorrowRequest(): Observable<any> {
    return new Observable(observer => {
      this.socket.on('borrow-request', (alert) => observer.next(alert));
    });
  }

  onUnreadNotifications(): Observable<any> {
    return new Observable(observer => {
      this.socket.on('unread-notifications', (alerts) => observer.next(alerts));
    });
  }

  markNotificationRead(notificationId: number): void {
    this.socket.emit('mark-read', notificationId);
  }

  getUserNotifications(userId: number): Observable<any> {
    return this.http.get(`${this.serverUrl}/api/notifications/${userId}`);
  }

  submitBorrowRequest(itemId: number, borrowerId: number): Observable<any> {
    return this.http.post(`${this.serverUrl}/api/borrow`, { itemId, borrowerId });
  }
}

You can then create a notifications component to display all alerts and mark them as read, following similar patterns to the previous example.


内容的提问来源于stack exchange,提问作者Tayssir ben salem

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:35:48