diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..ded820a
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,7 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(git:*)"
+ ]
+ }
+}
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..2191398
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,37 @@
+
+
+
+
+ mysql.8
+ true
+ true
+ $PROJECT_DIR$/server/src/main/resources/application-prod.yml
+ com.mysql.cj.jdbc.Driver
+ jdbc:mysql://mysql:3306/smartlab?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+ $ProjectFileDir$
+
+
+ h2.unified
+ true
+ true
+ $PROJECT_DIR$/server/src/main/resources/application-test.yml
+ org.h2.Driver
+ jdbc:h2:mem:smartlab;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+ $ProjectFileDir$
+
+
+ mysql.8
+ true
+ true
+ $PROJECT_DIR$/server/src/main/resources/application-dev.yml
+ com.mysql.cj.jdbc.Driver
+ jdbc:mysql://localhost:3306/smartlab?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+
+
+
+
+
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 6841a54..c2a88e8 100644
--- a/README.md
+++ b/README.md
@@ -52,8 +52,8 @@ docker compose up -d --build
### 3) 初始化数据库(首次部署建议执行)
```bash
-docker compose exec -T mysql mysql -uroot -proot smartlab < sql/schema.sql
-docker compose exec -T mysql mysql -uroot -proot smartlab < sql/seed.sql
+docker compose exec -T mysql mysql --default-character-set=utf8mb4 -uroot -proot smartlab < sql/schema.sql
+docker compose exec -T mysql mysql --default-character-set=utf8mb4 -uroot -proot smartlab < sql/seed.sql
```
### 4) 常用运维命令
diff --git a/docker-compose.yml b/docker-compose.yml
index 0d16375..669462b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -35,6 +35,7 @@ services:
DB_PASSWORD: root
JWT_SECRET: smartlab-prod-jwt-secret-change-me-32bytes
JWT_EXPIRE_HOURS: 24
+ CORS_ALLOWED_ORIGIN_PATTERNS: http://127.0.0.1:5173,http://localhost:5173
ports:
- "8080:8080"
@@ -51,4 +52,3 @@ services:
volumes:
mysql_data:
-
diff --git a/docs/debug.md b/docs/debug.md
new file mode 100644
index 0000000..475139e
--- /dev/null
+++ b/docs/debug.md
@@ -0,0 +1,210 @@
+# SmartLab Debug Notes
+
+日期:2026-03-17
+
+## 当前结论
+
+目前后端登录接口和前端代理链路都已经验证正常。
+
+已确认:
+
+- `mysql`、`server`、`web` 三个容器都在运行
+- `schema.sql` 已导入成功,业务表已存在
+- `seed.sql` 已至少成功导入到能查出 `sys_user` 和 `sys_role`
+- `sys_user` 中已有:
+ - `admin`, `status = 1`, `role_id = 1`
+ - `teacher`, `status = 1`, `role_id = 2`
+ - `student`, `status = 1`, `role_id = 3`
+- 直接请求后端登录接口成功:
+ - `POST http://localhost:8080/api/auth/login`
+- 通过前端代理请求登录接口也成功:
+ - `POST http://localhost:5173/api/auth/login`
+
+这说明以下链路没有问题:
+
+- MySQL 基本可用
+- 后端 `/api/auth/login` 可用
+- 前端 Nginx 代理 `/api/*` 到后端可用
+
+## 已确认的命令结果
+
+### 1. 容器状态
+
+`docker compose ps` 显示:
+
+- `smartlab-mysql` 正常运行
+- `smartlab-server` 正常运行
+- `smartlab-web` 正常运行
+
+### 2. 表结构已存在
+
+执行:
+
+```powershell
+docker compose exec -T mysql mysql -uroot -proot -e "USE smartlab; SHOW TABLES;"
+```
+
+已看到这些表:
+
+- `equipment`
+- `equipment_category`
+- `lab_room`
+- `maintenance_order`
+- `notice`
+- `reservation`
+- `reservation_item`
+- `sys_permission`
+- `sys_role`
+- `sys_role_permission`
+- `sys_user`
+
+### 3. 用户数据已存在
+
+执行:
+
+```powershell
+docker compose exec -T mysql mysql -uroot -proot -e "USE smartlab; SELECT id, username, status, role_id FROM sys_user;"
+```
+
+结果为:
+
+```text
+1 admin 1 1
+2 teacher 1 2
+3 student 1 3
+```
+
+### 4. 后端登录接口成功
+
+执行:
+
+```powershell
+Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/auth/login" -ContentType "application/json" -Body '{"username":"admin","password":"123456"}'
+```
+
+返回:
+
+- `code = 0`
+- `message = success`
+- `data.token` 存在
+
+### 5. 前端代理登录接口成功
+
+执行:
+
+```powershell
+Invoke-RestMethod -Method Post -Uri "http://localhost:5173/api/auth/login" -ContentType "application/json" -Body '{"username":"admin","password":"123456"}'
+```
+
+返回:
+
+- `code = 0`
+- `message = success`
+- `data.token` 存在
+
+## 之前出现过的问题
+
+### 1. PowerShell 命令经常因手误失败
+
+出现过这些拼写问题:
+
+- `-uroot` 被写成 `-urot`
+- `-proot` 被写成 `-proof`
+- `-uroot` 被拆成单独一行执行
+- `SHOW TABLES;` 被写成 `SHOW TABLES:`
+- `Invoke-RestMethod -Uri` 后漏空格
+
+这些都不是项目问题。
+
+### 2. PowerShell 管道导入 `seed.sql` 存在编码问题
+
+执行过:
+
+```powershell
+Get-Content .\sql\seed.sql | docker compose exec -T mysql mysql -uroot -proot smartlab
+```
+
+出现过中文内容变成 `????` 的情况,并导致 SQL 语法错误。
+
+这说明在 PowerShell 下,用 `Get-Content | docker compose exec ...` 导入含中文的 `seed.sql` 不可靠。
+
+### 3. 浏览器里曾出现旧 token 导致的 `/api/auth/me` 401
+
+Web 容器日志中看到:
+
+- 页面加载时曾直接请求 `GET /api/auth/me`
+- 该请求返回 `401`
+
+这很可能是浏览器本地存储里残留了旧 token。
+
+## 下次开机后优先做的事
+
+### 1. 先确认容器仍然正常
+
+```powershell
+docker compose ps
+```
+
+### 2. 直接验证登录接口和 `/auth/me`
+
+先拿 token:
+
+```powershell
+$login = Invoke-RestMethod -Method Post -Uri "http://localhost:5173/api/auth/login" -ContentType "application/json" -Body '{"username":"admin","password":"123456"}'
+$token = $login.data.token
+```
+
+再验证当前用户接口:
+
+```powershell
+Invoke-RestMethod -Method Get -Uri "http://localhost:5173/api/auth/me" -Headers @{ Authorization = "Bearer $token" }
+```
+
+需要确认:
+
+- 这条是否成功返回当前用户信息
+- 如果失败,失败码是不是 `401`
+
+### 3. 清浏览器本地 token
+
+在浏览器打开 `http://localhost:5173/login`,按 `F12`,在 `Console` 执行:
+
+```js
+localStorage.clear()
+location.reload()
+```
+
+然后重新用:
+
+- 用户名:`admin`
+- 密码:`123456`
+
+### 4. 若页面仍失败,重点抓两条请求
+
+浏览器开发者工具 `Network` 中只看:
+
+- `POST /api/auth/login`
+- `GET /api/auth/me`
+
+需要记录:
+
+- 状态码
+- 响应体
+
+## 建议的后续修复方向
+
+建议后续做两件事:
+
+1. 调整数据库初始化方式,避免手工导入 SQL
+2. 优化前端登录错误提示
+
+原因:
+
+- 当前初始化过度依赖手工命令,PowerShell 下很容易踩编码和重定向问题
+- 前端现在会把登录成功后 `/auth/me` 的失败笼统显示成“登录服务暂时不可用”,定位不够直接
+
+## 文档位置
+
+本次记录文件:
+
+- [docs/debug.md](/mnt/d/smartlab/docs/debug.md)
diff --git a/server/src/main/java/com/smartlab/server/common/config/MybatisConfig.java b/server/src/main/java/com/smartlab/server/common/config/MybatisConfig.java
index d4b62bd..f7ad517 100644
--- a/server/src/main/java/com/smartlab/server/common/config/MybatisConfig.java
+++ b/server/src/main/java/com/smartlab/server/common/config/MybatisConfig.java
@@ -1,11 +1,14 @@
package com.smartlab.server.common.config;
+import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -14,17 +17,34 @@
@Configuration
public class MybatisConfig {
+ @Value("${smartlab.id.worker-id:1}")
+ private long workerId;
+
+ @Value("${smartlab.id.datacenter-id:1}")
+ private long datacenterId;
+
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setPlugins(mybatisPlusInterceptor());
+ factoryBean.setGlobalConfig(globalConfig());
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setMapUnderscoreToCamelCase(true);
factoryBean.setConfiguration(configuration);
return factoryBean.getObject();
}
+ @Bean
+ public GlobalConfig globalConfig() {
+ GlobalConfig.Sequence sequence = new GlobalConfig.Sequence()
+ .setWorkerId(workerId)
+ .setDatacenterId(datacenterId);
+ return new GlobalConfig()
+ .setSequence(sequence)
+ .setIdentifierGenerator(new DefaultIdentifierGenerator(workerId, datacenterId));
+ }
+
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
diff --git a/server/src/main/java/com/smartlab/server/modules/auth/service/impl/AuthServiceImpl.java b/server/src/main/java/com/smartlab/server/modules/auth/service/impl/AuthServiceImpl.java
index e0541c1..08acbe9 100644
--- a/server/src/main/java/com/smartlab/server/modules/auth/service/impl/AuthServiceImpl.java
+++ b/server/src/main/java/com/smartlab/server/modules/auth/service/impl/AuthServiceImpl.java
@@ -3,6 +3,7 @@
import com.smartlab.server.common.exception.BusinessException;
import com.smartlab.server.modules.auth.dto.LoginRequest;
import com.smartlab.server.modules.auth.model.AuthUserInfo;
+import com.smartlab.server.modules.auth.support.AuthStatusSupport;
import com.smartlab.server.modules.auth.service.AuthService;
import com.smartlab.server.modules.auth.vo.CurrentUserVO;
import com.smartlab.server.modules.auth.vo.LoginResponse;
@@ -33,7 +34,7 @@ public LoginResponse login(LoginRequest request) {
if (userInfo == null || !passwordEncoder.matches(request.getPassword(), userInfo.getPassword())) {
throw new BusinessException(401, "用户名或密码错误");
}
- if (userInfo.getStatus() == null || userInfo.getStatus() != 1) {
+ if (!AuthStatusSupport.isUserEnabled(userInfo.getStatus())) {
throw new BusinessException(403, "用户已被禁用");
}
String token = jwtTokenProvider.generateToken(userInfo.getUserId(), userInfo.getUsername(), userInfo.getRoleCode());
diff --git a/server/src/main/java/com/smartlab/server/modules/auth/support/AuthStatusSupport.java b/server/src/main/java/com/smartlab/server/modules/auth/support/AuthStatusSupport.java
new file mode 100644
index 0000000..cb03733
--- /dev/null
+++ b/server/src/main/java/com/smartlab/server/modules/auth/support/AuthStatusSupport.java
@@ -0,0 +1,11 @@
+package com.smartlab.server.modules.auth.support;
+
+public final class AuthStatusSupport {
+
+ private AuthStatusSupport() {
+ }
+
+ public static boolean isUserEnabled(Integer status) {
+ return status != null && status == 1;
+ }
+}
diff --git a/server/src/main/java/com/smartlab/server/modules/dashboard/service/impl/DashboardServiceImpl.java b/server/src/main/java/com/smartlab/server/modules/dashboard/service/impl/DashboardServiceImpl.java
index 28b1e49..2445bf0 100644
--- a/server/src/main/java/com/smartlab/server/modules/dashboard/service/impl/DashboardServiceImpl.java
+++ b/server/src/main/java/com/smartlab/server/modules/dashboard/service/impl/DashboardServiceImpl.java
@@ -49,6 +49,9 @@ public DashboardSummaryVO summary() {
long todayReservationCount = reservationMapper.selectCount(
new LambdaQueryWrapper().eq(Reservation::getUseDate, today)
);
+ long pendingApprovalCount = reservationMapper.selectCount(
+ new LambdaQueryWrapper().eq(Reservation::getStatus, "PENDING")
+ );
long processingMaintenanceCount = maintenanceOrderMapper.selectCount(
new LambdaQueryWrapper().ne(MaintenanceOrder::getStatus, "FINISHED")
);
@@ -60,6 +63,7 @@ public DashboardSummaryVO summary() {
.collect(Collectors.groupingBy(Equipment::getStatus, Collectors.counting()));
DashboardSummaryVO vo = new DashboardSummaryVO();
vo.setTodayReservationCount(todayReservationCount);
+ vo.setPendingApprovalCount(pendingApprovalCount);
vo.setProcessingMaintenanceCount(processingMaintenanceCount);
vo.setPublishedNoticeCount(publishedNoticeCount);
vo.setEquipmentStatusDistribution(statusDistribution);
diff --git a/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardRankVO.java b/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardRankVO.java
index 0913558..b07a29f 100644
--- a/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardRankVO.java
+++ b/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardRankVO.java
@@ -8,5 +8,6 @@ public class DashboardRankVO {
private Long equipmentId;
private String assetNo;
private String name;
+ private String labName;
private Long reservationCount;
}
diff --git a/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardSummaryVO.java b/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardSummaryVO.java
index 69b888d..1f58b93 100644
--- a/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardSummaryVO.java
+++ b/server/src/main/java/com/smartlab/server/modules/dashboard/vo/DashboardSummaryVO.java
@@ -8,6 +8,7 @@
public class DashboardSummaryVO {
private long todayReservationCount;
+ private long pendingApprovalCount;
private long processingMaintenanceCount;
private long publishedNoticeCount;
private Map equipmentStatusDistribution;
diff --git a/server/src/main/java/com/smartlab/server/modules/reservation/mapper/ReservationItemMapper.java b/server/src/main/java/com/smartlab/server/modules/reservation/mapper/ReservationItemMapper.java
index a406041..2fe25dc 100644
--- a/server/src/main/java/com/smartlab/server/modules/reservation/mapper/ReservationItemMapper.java
+++ b/server/src/main/java/com/smartlab/server/modules/reservation/mapper/ReservationItemMapper.java
@@ -16,12 +16,14 @@ public interface ReservationItemMapper extends BaseMapper {
ri.equipment_id AS equipmentId,
e.asset_no AS assetNo,
e.name AS name,
+ lr.room_name AS labName,
COUNT(1) AS reservationCount
FROM reservation_item ri
JOIN reservation r ON r.id = ri.reservation_id
JOIN equipment e ON e.id = ri.equipment_id
+ JOIN lab_room lr ON lr.id = e.room_id
WHERE r.status IN ('PENDING', 'APPROVED', 'FINISHED')
- GROUP BY ri.equipment_id, e.asset_no, e.name
+ GROUP BY ri.equipment_id, e.asset_no, e.name, lr.room_name
ORDER BY reservationCount DESC
LIMIT #{limit}
""")
diff --git a/server/src/main/java/com/smartlab/server/modules/user/mapper/SysUserMapper.java b/server/src/main/java/com/smartlab/server/modules/user/mapper/SysUserMapper.java
index 3e40845..e836f5d 100644
--- a/server/src/main/java/com/smartlab/server/modules/user/mapper/SysUserMapper.java
+++ b/server/src/main/java/com/smartlab/server/modules/user/mapper/SysUserMapper.java
@@ -15,7 +15,11 @@ public interface SysUserMapper extends BaseMapper {
u.username,
u.password,
u.real_name,
- u.status,
+ CASE
+ WHEN u.status IS NULL THEN 0
+ WHEN UPPER(TRIM(CAST(u.status AS CHAR))) IN ('1', 'ENABLED', 'ACTIVE', 'TRUE') THEN 1
+ ELSE 0
+ END AS status,
r.role_code,
r.role_name
FROM sys_user u
@@ -31,7 +35,11 @@ public interface SysUserMapper extends BaseMapper {
u.username,
u.password,
u.real_name,
- u.status,
+ CASE
+ WHEN u.status IS NULL THEN 0
+ WHEN UPPER(TRIM(CAST(u.status AS CHAR))) IN ('1', 'ENABLED', 'ACTIVE', 'TRUE') THEN 1
+ ELSE 0
+ END AS status,
r.role_code,
r.role_name
FROM sys_user u
@@ -41,4 +49,3 @@ public interface SysUserMapper extends BaseMapper {
""")
AuthUserInfo selectAuthInfoById(Long userId);
}
-
diff --git a/server/src/main/java/com/smartlab/server/security/AuthUserDetailsService.java b/server/src/main/java/com/smartlab/server/security/AuthUserDetailsService.java
index 5f45a33..8bb4875 100644
--- a/server/src/main/java/com/smartlab/server/security/AuthUserDetailsService.java
+++ b/server/src/main/java/com/smartlab/server/security/AuthUserDetailsService.java
@@ -2,6 +2,7 @@
import com.smartlab.server.common.exception.BusinessException;
import com.smartlab.server.modules.auth.model.AuthUserInfo;
+import com.smartlab.server.modules.auth.support.AuthStatusSupport;
import com.smartlab.server.modules.user.mapper.SysUserMapper;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -31,7 +32,7 @@ public UserDetails loadUserByUsername(String username) {
public AuthUserPrincipal loadById(Long userId) {
AuthUserInfo userInfo = sysUserMapper.selectAuthInfoById(userId);
- if (userInfo == null || userInfo.getStatus() == null || userInfo.getStatus() != 1) {
+ if (userInfo == null || !AuthStatusSupport.isUserEnabled(userInfo.getStatus())) {
return null;
}
return toPrincipal(userInfo);
diff --git a/server/src/main/java/com/smartlab/server/security/SecurityConfig.java b/server/src/main/java/com/smartlab/server/security/SecurityConfig.java
index 49888f4..7f36c4b 100644
--- a/server/src/main/java/com/smartlab/server/security/SecurityConfig.java
+++ b/server/src/main/java/com/smartlab/server/security/SecurityConfig.java
@@ -3,10 +3,12 @@
import com.smartlab.server.security.filter.JwtAuthenticationFilter;
import com.smartlab.server.security.handler.JwtAccessDeniedHandler;
import com.smartlab.server.security.handler.JwtAuthenticationEntryPoint;
+import java.util.Arrays;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
-import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
@@ -14,6 +16,9 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Configuration
public class SecurityConfig {
@@ -22,6 +27,9 @@ public class SecurityConfig {
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
+ @Value("${smartlab.cors.allowed-origin-patterns:http://127.0.0.1:5173,http://localhost:5173}")
+ private String allowedOriginPatterns;
+
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter,
JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
JwtAccessDeniedHandler jwtAccessDeniedHandler) {
@@ -33,7 +41,7 @@ public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter,
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
- .cors(Customizer.withDefaults())
+ .cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(exception -> exception
@@ -43,6 +51,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/login").permitAll()
.requestMatchers("/health").permitAll()
+ .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers("/system/**").hasRole("ADMIN")
.requestMatchers(HttpMethod.GET, "/equipment", "/equipment/**", "/labs", "/labs/**").authenticated()
.requestMatchers("/labs/**", "/equipment/categories/**").hasRole("ADMIN")
@@ -65,4 +74,21 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
+
+ @Bean
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+ List origins = Arrays.stream(allowedOriginPatterns.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .collect(java.util.stream.Collectors.toList());
+ configuration.setAllowedOriginPatterns(origins);
+ configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+ configuration.setAllowedHeaders(Arrays.asList("*"));
+ configuration.setAllowCredentials(true);
+ configuration.setMaxAge(3600L);
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", configuration);
+ return source;
+ }
}
diff --git a/server/src/test/java/com/smartlab/server/modules/maintenance/MaintenanceNoticeDashboardIntegrationTest.java b/server/src/test/java/com/smartlab/server/modules/maintenance/MaintenanceNoticeDashboardIntegrationTest.java
index f702c94..9b91a25 100644
--- a/server/src/test/java/com/smartlab/server/modules/maintenance/MaintenanceNoticeDashboardIntegrationTest.java
+++ b/server/src/test/java/com/smartlab/server/modules/maintenance/MaintenanceNoticeDashboardIntegrationTest.java
@@ -101,8 +101,10 @@ void noticeCrudAndDashboardShouldWork() throws Exception {
HttpResponse summaryResponse = call("/dashboard/summary", "GET", null, studentToken);
Map summaryBody = readMap(summaryResponse.body());
+ Map summaryData = (Map) summaryBody.get("data");
assertThat(summaryResponse.statusCode()).isEqualTo(200);
assertThat(summaryBody.get("code")).isEqualTo(0);
+ assertThat(((Number) summaryData.get("pendingApprovalCount")).longValue()).isGreaterThanOrEqualTo(1L);
HttpResponse trendResponse = call("/dashboard/trend", "GET", null, studentToken);
Map trendBody = readMap(trendResponse.body());
@@ -111,8 +113,11 @@ void noticeCrudAndDashboardShouldWork() throws Exception {
HttpResponse rankResponse = call("/dashboard/rank", "GET", null, studentToken);
Map rankBody = readMap(rankResponse.body());
+ List