add notification API

GET /notification
DELETE /notification
POST /notification/read

Change-Id: I06f28ce188049015cb6a0fe221caf2f784687db5
diff --git a/src/test/java/com/g9/g9backend/controller/NotificationControllerTest.java b/src/test/java/com/g9/g9backend/controller/NotificationControllerTest.java
new file mode 100644
index 0000000..121258f
--- /dev/null
+++ b/src/test/java/com/g9/g9backend/controller/NotificationControllerTest.java
@@ -0,0 +1,99 @@
+package com.g9.g9backend.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.g9.g9backend.pojo.Notification;
+import com.g9.g9backend.service.NotificationService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.Date;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+public class NotificationControllerTest {
+
+    private MockMvc mockMvc;
+
+    @InjectMocks
+    private NotificationController notificationController;
+
+    @Mock
+    private NotificationService notificationService;
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    @BeforeEach
+    public void setup() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(notificationController).build();
+    }
+
+    // ✅ 测试标记已读
+    @Test
+    public void testGetNotificationRead_success() throws Exception {
+        Notification notification = new Notification();
+        notification.setNotificationId(1);
+
+        Notification updatedNotification = new Notification();
+        updatedNotification.setNotificationId(1);
+        updatedNotification.setRead(false);
+
+        when(notificationService.getById(1)).thenReturn(updatedNotification);
+        when(notificationService.updateById(any())).thenReturn(true);
+
+        mockMvc.perform(post("/notification/read")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(objectMapper.writeValueAsString(notification)))
+                .andExpect(status().isOk());
+    }
+
+    // ✅ 测试删除通知
+    @Test
+    public void testDeleteNotification_success() throws Exception {
+        when(notificationService.removeById(1)).thenReturn(true);
+
+        mockMvc.perform(delete("/notification")
+                        .param("notificationId", "1"))
+                .andExpect(status().isNoContent());
+    }
+
+    // ✅ 测试分页获取通知
+    @Test
+    public void testGetNotification_success() throws Exception {
+        Notification notification = new Notification();
+        notification.setNotificationId(1);
+        notification.setUserId(1);
+        notification.setTitle("title");
+        notification.setContent("content");
+        notification.setCreateAt(new Date());
+        notification.setRead(false);
+        notification.setTriggeredBy(2);
+        notification.setRelatedId(3);
+
+        Page<Notification> page = new Page<>(1, 10);
+        page.setRecords(List.of(notification));
+        page.setTotal(1);
+
+        when(notificationService.page(any(), any())).thenReturn(page);
+
+        mockMvc.perform(get("/notification")
+                        .param("userId", "1")
+                        .param("pageNumber", "1")
+                        .param("rows", "10"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.records[0].notificationId").value(1))
+                .andExpect(jsonPath("$.records[0].title").value("title"))
+                .andExpect(jsonPath("$.total").value(1));
+    }
+}