修正消息中心不显示

This commit is contained in:
2025-11-22 16:01:36 +08:00
parent f3a9926caf
commit 12592c5a24
15 changed files with 331 additions and 380 deletions

View File

@@ -69,8 +69,8 @@ INSERT INTO `tb_sys_permission` (id,permission_id, name, code, description, modu
('19','perm_sensitive_manage', '敏感词管理', 'sensitive:manage', '敏感词管理权限', 'module_sensitive', '1', now());
-- 插入角色-权限关联数据
INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, create_time) VALUES
-- 超级管理员:拥有所有权限
INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, create_time) VALUES
('1', 'superadmin', 'perm_default', '1', now()),
('2', 'superadmin', 'perm_system_manage', '1', now()),
('3', 'superadmin', 'perm_system_dept_manage', '1', now()),
@@ -116,8 +116,8 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat
('45', 'freedom', 'perm_message_view', '1', now());
-- 插入前端菜单数据
INSERT INTO `tb_sys_menu` VALUES
-- 用户前端菜单 (100-699)
INSERT INTO `tb_sys_menu` VALUES
('100', 'menu_home', '首页', NULL, '/home', 'user/home/HomeView', NULL, 1, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('101', 'menu_resource_hot', '热门资源', NULL, '/resource-hot', 'user/resource-center/HotResourceView', NULL, 2, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('200', 'menu_resource_center', '资源中心', NULL, '/resource-center', 'user/resource-center/ResourceCenterView', NULL, 2, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
@@ -128,13 +128,13 @@ INSERT INTO `tb_sys_menu` VALUES
('304', 'menu_course_detail', '课程详情', 'menu_study_plan', '/study-plan/course-detail', 'user/study-plan/CourseDetailView', NULL, 4, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('305', 'menu_course_study', '课程学习', 'menu_study_plan', '/study-plan/course-study', 'user/study-plan/CourseStudyView', NULL, 5, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('400', 'menu_user_dropdown', '用户下拉菜单', NULL, '', '', NULL, 4, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('401', 'menu_user_center', '个人中心', 'menu_user_dropdown', '/user-center', 'user/user-center/UserCenterView', NULL, 4, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('401', 'menu_user_center', '个人中心', 'menu_user_dropdown', '/user-center', 'user/user-center/UserCenterLayout', NULL, 4, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('402', 'menu_learning_records', '学习记录', 'menu_user_center', '/user-center/learning-records', 'user/user-center/LearningRecordsView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('403', 'menu_my_favorites', '我的收藏', 'menu_user_center', '/user-center/favorites', 'user/user-center/MyFavoritesView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('404', 'menu_my_achievements', '我的成就', 'menu_user_center', '/user-center/achievements', 'user/user-center/MyAchievementsView', NULL, 3, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/profile/ProfileView', NULL, 5, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/profile/PersonalInfoView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/profile/AccountSettingsView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/user-center/UserCenterLayout', NULL, 5, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/user-center/profile/PersonalInfoView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/user-center/profile/AccountSettingsView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('503', 'menu_search', '搜索', NULL, '/search', 'user/resource-center/SearchView', NULL, 3, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
-- 管理后台菜单 (1000-8999)
('1000', 'menu_admin_overview', '系统总览', NULL, '/admin/overview', 'admin/overview/SystemOverviewView', 'admin/overview.svg', 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:32', NULL, 0),
@@ -177,8 +177,8 @@ INSERT INTO `tb_sys_menu` VALUES
('651', 'menu_user_message_detail', '消息详情', 'menu_user_message_center', '/user/message/detail/:messageID', 'user/message/MyMessageDetailView', NULL, 1, 3, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0);
-- 插入菜单权限关联数据
INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES
-- 前端菜单权限关联
INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES
('100', 'perm_default', 'menu_home', '1', now()),
('102', 'perm_default', 'menu_resource_hot', '1', now()),
('101', 'perm_default', 'menu_resource_center', '1', now()),
@@ -241,11 +241,7 @@ INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, creat
-- 消息通知管理菜单权限关联
('240', 'perm_message_manage', 'menu_admin_message_manage', '1', now()),
('241', 'perm_message_manage', 'menu_admin_message_list', '1', now()),
('242', 'perm_message_send', 'menu_admin_message_create', '1', now()),
('243', 'perm_message_manage', 'menu_admin_message_detail', '1', now()),
-- 用户端消息中心权限关联
('250', 'perm_message_view', 'menu_user_message_center', '1', now()),
('251', 'perm_message_view', 'menu_user_message_detail', '1', now());
('250', 'perm_default', 'menu_user_message_center', '1', now()),
('251', 'perm_default', 'menu_user_message_detail', '1', now());

View File

@@ -97,6 +97,51 @@ execute_init_script() {
fi
}
# 执行敏感词导入
import_sensitive_words() {
print_message $BLUE "开始导入敏感词数据..."
# 检查conda是否可用
if ! command -v conda &> /dev/null; then
print_message $YELLOW "conda命令未找到跳过敏感词导入"
return 0
fi
# 检查schoolNewsCrawler环境是否存在
if ! conda env list | grep -q "schoolNewsCrawler"; then
print_message $YELLOW "conda环境 'schoolNewsCrawler' 不存在,跳过敏感词导入"
print_message $YELLOW "提示: 可以使用以下命令创建环境: conda create -n schoolNewsCrawler python=3.10"
return 0
fi
# 切换到敏感词脚本目录
local sensitive_dir="$SCRIPT_DIR/sensitiveData"
if [ ! -d "$sensitive_dir" ]; then
print_message $YELLOW "敏感词脚本目录不存在: $sensitive_dir"
return 0
fi
if [ ! -f "$sensitive_dir/writeWord.py" ]; then
print_message $YELLOW "敏感词脚本不存在: $sensitive_dir/writeWord.py"
return 0
fi
print_message $BLUE "激活conda环境: schoolNewsCrawler"
cd "$sensitive_dir"
# 使用conda run来在指定环境中执行命令添加-y参数自动确认
conda run -n schoolNewsCrawler python writeWord.py -y
if [ $? -eq 0 ]; then
print_message $GREEN "敏感词数据导入成功"
else
print_message $YELLOW "敏感词数据导入失败,但不影响系统运行"
fi
# 返回脚本目录
cd "$SCRIPT_DIR"
}
# 验证初始化结果
verify_initialization() {
print_message $BLUE "验证初始化结果..."
@@ -120,6 +165,14 @@ verify_initialization() {
else
print_message $YELLOW "默认用户创建失败"
fi
# 检查敏感词数量
local sensitive_count=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" -D"$DB_NAME" -e "SELECT COUNT(*) FROM tb_sensitive_word;" 2>/dev/null | tail -n 1)
if [ -n "$sensitive_count" ] && [ "$sensitive_count" -gt 0 ]; then
print_message $GREEN "敏感词数据: $sensitive_count"
else
print_message $YELLOW "敏感词数据: 0 个 (可能未导入或表不存在)"
fi
}
# 显示初始化信息
@@ -146,6 +199,8 @@ show_initialization_info() {
print_message $YELLOW " - AI智能体"
print_message $YELLOW " - 系统配置"
print_message $YELLOW " - 文件管理"
print_message $YELLOW " - 敏感词过滤"
print_message $YELLOW " - 消息通知"
print_message $BLUE "====================================================="
}
@@ -153,7 +208,7 @@ show_initialization_info() {
main() {
print_message $BLUE "====================================================="
print_message $BLUE "校园思政新闻平台数据库重新初始化脚本"
print_message $BLUE "版本: 1.0.0"
print_message $BLUE "版本: 1.1.0"
print_message $BLUE "====================================================="
# 检查MySQL连接
@@ -170,11 +225,14 @@ main() {
# 执行初始化脚本
execute_init_script
# 导入敏感词数据
# 验证初始化结果
verify_initialization
# 显示初始化信息
show_initialization_info
import_sensitive_words
print_message $GREEN "初始化完成!"
}

View File

@@ -8,6 +8,7 @@
import pymysql
import os
import sys
import argparse
from datetime import datetime
# 数据库配置
@@ -110,13 +111,25 @@ def check_duplicates(connection, words):
def main():
"""主函数"""
# 解析命令行参数
parser = argparse.ArgumentParser(description='敏感词批量导入工具')
parser.add_argument('-y', '--yes', action='store_true',
help='自动确认导入,跳过交互式确认')
parser.add_argument('--file', type=str,
help='指定敏感词文件路径(默认: sensitive_word_dict.txt')
args = parser.parse_args()
print("=" * 50)
print("敏感词批量导入工具")
print("=" * 50)
# 获取脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))
dict_file = os.path.join(script_dir, 'sensitive_word_dict.txt')
# 获取敏感词文件路径
if args.file:
dict_file = args.file
else:
script_dir = os.path.dirname(os.path.abspath(__file__))
dict_file = os.path.join(script_dir, 'sensitive_word_dict.txt')
# 检查敏感词文件是否存在
if not os.path.exists(dict_file):
@@ -150,11 +163,20 @@ def main():
# 确认导入
print(f"准备导入 {len(words)} 个敏感词到数据库")
confirm = input("是否继续?(y/N): ").strip().lower()
if confirm != 'y':
print("用户取消导入")
return
if args.yes:
print("自动确认模式,开始导入...")
else:
try:
confirm = input("是否继续?(y/N): ").strip().lower()
if confirm != 'y':
print("用户取消导入")
return
except EOFError:
print("检测到非交互式环境,自动确认导入...")
except KeyboardInterrupt:
print("\n用户中断导入")
return
# 批量插入
print("开始批量导入敏感词...")

View File

@@ -1,331 +0,0 @@
# 成就模块迁移指南
## 📝 迁移概述
成就相关功能已从 `usercenter` 模块迁移到独立的 `achievement` 模块。
### 迁移日期
2025-10-24
### 迁移原因
-**模块职责分离** - usercenter负责用户中心achievement专注于成就系统
-**更好的可维护性** - 独立模块易于管理和扩展
-**清晰的依赖关系** - 减少模块间的耦合
-**可扩展性** - 便于添加新的成就类型和功能
## 🔄 包路径变更
### API接口变更
**旧路径(已删除):**
```java
org.xyzh.api.usercenter.achievement.UserAchievementService
```
**新路径:**
```java
org.xyzh.api.achievement.AchievementService
```
### 服务实现变更
**旧路径(已删除):**
```java
org.xyzh.usercenter.service.UCUserAchievementService
org.xyzh.usercenter.service.impl.UCUserAchievementServiceImpl
```
**新路径:**
```java
org.xyzh.api.achievement.AchievementService
org.xyzh.achievement.service.impl.ACHAchievementServiceImpl
```
### Mapper变更
**旧路径(已删除):**
```java
org.xyzh.usercenter.mapper.AchievementMapper
org.xyzh.usercenter.mapper.UserAchievementMapper
```
**新路径:**
```java
org.xyzh.achievement.mapper.AchievementMapper
org.xyzh.achievement.mapper.UserAchievementMapper
org.xyzh.achievement.mapper.UserAchievementProgressMapper // 新增
```
### Controller变更
**旧路径(已删除):**
```java
org.xyzh.usercenter.controller.UserAchievementController
```
**新路径:**
```java
org.xyzh.achievement.controller.AchievementController
```
## 📦 依赖变更
### 如果你的模块之前依赖了成就功能
#### 旧的依赖配置(需要删除):
```xml
<dependency>
<groupId>org.xyzh</groupId>
<artifactId>api-usercenter</artifactId> <!-- 包含成就接口 -->
<version>1.0.0</version>
</dependency>
```
#### 新的依赖配置(需要添加):
```xml
<!-- API接口依赖 -->
<dependency>
<groupId>org.xyzh</groupId>
<artifactId>api-achievement</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 如果需要使用实现类通常不需要使用Dubbo远程调用 -->
<dependency>
<groupId>org.xyzh</groupId>
<artifactId>achievement</artifactId>
<version>1.0.0</version>
</dependency>
```
## 🔧 代码迁移步骤
### 1. 更新导入语句
**旧代码:**
```java
import org.xyzh.api.usercenter.achievement.UserAchievementService;
```
**新代码:**
```java
import org.xyzh.api.achievement.AchievementService;
```
### 2. 更新服务注入
**旧代码:**
```java
@Autowired
private UserAchievementService userAchievementService;
```
**新代码:**
```java
@Autowired
private AchievementService achievementService;
```
### 3. 更新方法调用
大部分方法签名保持不变,只需更新服务名称:
**旧代码:**
```java
ResultDomain<TbUserAchievement> result = userAchievementService.getUserAchievements(userID, type);
```
**新代码:**
```java
ResultDomain<TbUserAchievement> result = achievementService.getUserAchievements(userID, type);
```
### 4. 新增功能调用
新的成就系统增加了事件驱动机制:
```java
// 发布成就事件
AchievementEvent event = AchievementEvent.builder(userID, AchievementEventType.COURSE_COMPLETED)
.value(1)
.build();
// 方式1使用Spring事件发布推荐
eventPublisher.publishEvent(event);
// 方式2直接调用服务
achievementService.processAchievementEvent(event);
```
## 🗄️ 数据库变更
### 新增表
```sql
-- 用户成就进度表(新增)
CREATE TABLE tb_user_achievement_progress (
id VARCHAR(32) NOT NULL,
user_id VARCHAR(32) NOT NULL,
achievement_id VARCHAR(32) NOT NULL,
current_value INT DEFAULT 0,
target_value INT DEFAULT 0,
progress_percentage INT DEFAULT 0,
completed TINYINT(1) DEFAULT 0,
last_update_time DATETIME,
create_time DATETIME,
PRIMARY KEY (id),
UNIQUE KEY uk_user_achievement_progress (user_id, achievement_id)
);
```
### 已有表
`tb_achievement``tb_user_achievement` 表结构保持不变,无需迁移数据。
## 🌐 REST API变更
### 接口路径变更
**旧路径:**
```
/usercenter/achievement/*
```
**新路径:**
```
/achievement/*
```
### 具体接口映射
| 功能 | 旧接口 | 新接口 | 变化 |
|-----|-------|-------|-----|
| 获取所有成就 | GET /usercenter/achievement/list | GET /achievement/list | 路径变更 |
| 获取用户成就 | GET /usercenter/achievement/user/{userID} | GET /achievement/user/{userID} | 路径变更 |
| 获取我的成就 | - | GET /achievement/my | **新增** |
| 授予成就 | POST /usercenter/achievement/grant | POST /achievement/grant | 路径变更 |
| 检查条件 | GET /usercenter/achievement/condition/{userID}/{achievementID} | GET /achievement/condition/check/{userID}/{achievementID} | 路径变更 |
| 获取进度 | - | GET /achievement/progress/{userID} | **新增** |
| 处理事件 | - | POST /achievement/event/process | **新增** |
| 获取统计 | - | GET /achievement/statistics/{userID} | **新增** |
| 排行榜 | - | GET /achievement/ranking | **新增** |
## ✨ 新增功能
### 1. 事件驱动机制
```java
// 自动监听业务事件并触发成就检测
@Autowired
private ApplicationEventPublisher eventPublisher;
AchievementEvent event = AchievementEvent.builder(userID, eventType)
.value(value)
.build();
eventPublisher.publishEvent(event);
```
### 2. 成就进度追踪
```java
// 查询用户的成就进度
ResultDomain<TbUserAchievementProgress> result =
achievementService.getMyAchievementProgress(achievementID);
```
### 3. 策略模式检测器
- 可扩展的成就检测器
- 支持多种成就类型
- 易于添加新的成就类型
### 4. 统计和排行榜
```java
// 获取用户统计
ResultDomain<Map<String, Object>> stats =
achievementService.getUserAchievementStatistics(userID);
// 获取排行榜
ResultDomain<Map<String, Object>> ranking =
achievementService.getAchievementRanking(10);
```
## 🚨 注意事项
### 1. 破坏性变更
#### ❌ 已删除的类(不再可用)
- `org.xyzh.api.usercenter.achievement.UserAchievementService`
- `org.xyzh.usercenter.service.UCUserAchievementService`
- `org.xyzh.usercenter.mapper.AchievementMapper`
- `org.xyzh.usercenter.controller.UserAchievementController`
#### ✅ 对应的新类
- `org.xyzh.api.achievement.AchievementService`
- `org.xyzh.achievement.service.impl.ACHAchievementServiceImpl`
- `org.xyzh.achievement.mapper.AchievementMapper`
- `org.xyzh.achievement.controller.AchievementController`
### 2. 接口变化
新的 `AchievementService` 接口方法更多、更完善:
- ✅ 所有旧方法都有对应的新方法
- ✅ 新增了事件处理、进度查询、统计等功能
- ✅ 方法签名大部分保持兼容
### 3. 前端调用
如果你的前端代码调用了成就相关API需要
1. 更新API路径`/usercenter/achievement/*``/achievement/*`
2. 检查返回数据格式(大部分保持兼容)
3. 利用新增的API进度、统计、排行榜等
## 📋 迁移检查清单
迁移时请检查以下项目:
- [ ] 更新 pom.xml 依赖
- [ ] 更新 import 语句
- [ ] 更新服务注入
- [ ] 更新方法调用
- [ ] 测试成就创建功能
- [ ] 测试成就授予功能
- [ ] 测试成就查询功能
- [ ] 测试事件触发功能(如有)
- [ ] 更新前端API路径如有
- [ ] 更新相关文档
- [ ] 执行集成测试
## 🆘 常见问题
### Q1: 编译报错找不到 UserAchievementService
**A:** 更新 import 语句为:
```java
import org.xyzh.api.achievement.AchievementService;
```
### Q2: 旧的成就数据会丢失吗?
**A:** 不会。数据库表结构保持不变,所有数据完整保留。
### Q3: 需要修改数据库吗?
**A:** 需要执行新表创建SQL`tb_user_achievement_progress`),见 `.bin/mysql/sql/createTableAchievement.sql`
### Q4: 如何使用新的事件驱动功能?
**A:** 参考 `README.md` 中的"使用方法"章节。
### Q5: admin模块已经自动配置好了吗
**A:** 是的admin模块的pom.xml已自动添加achievement依赖。
## 📚 相关文档
- [成就系统使用文档](../README.md)
- [数据库建表SQL](../../.bin/mysql/sql/createTableAchievement.sql)
## 👥 联系支持
如果在迁移过程中遇到问题,请联系:
- 开发者yslg
- 创建Issue或PR
---
**迁移完成日期**: 2025-10-24
**版本**: 1.0.0

View File

@@ -18,6 +18,7 @@ import org.xyzh.common.core.event.AchievementEvent;
import org.xyzh.common.core.page.PageDomain;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.dto.user.TbSysUser;
import org.xyzh.common.dto.user.TbSysUserInfo;
import org.xyzh.common.dto.usercenter.TbAchievement;
import org.xyzh.common.dto.usercenter.TbUserAchievement;
import org.xyzh.common.dto.usercenter.TbUserAchievementProgress;
@@ -26,6 +27,7 @@ import org.xyzh.common.utils.IDUtils;
import org.xyzh.common.vo.AchievementVO;
import org.xyzh.system.utils.LoginUtil;
import org.xyzh.api.system.permission.ResourcePermissionService;
import org.xyzh.system.mapper.UserInfoMapper;
import org.xyzh.common.vo.UserDeptRoleVO;
import org.xyzh.common.core.enums.ResourceType;
@@ -63,6 +65,9 @@ public class ACHAchievementServiceImpl implements AchievementService {
@Autowired
private RedisService redisService;
@Autowired
private UserInfoMapper userInfoMapper;
// ==================== 成就定义管理 ====================
@Override
@@ -551,7 +556,7 @@ public class ACHAchievementServiceImpl implements AchievementService {
// 检查是否达成
if (checker.check(event, achievement, currentProgress)) {
TbUserAchievement userAchievement = grantAchievementInternal(event.getUserID(), achievement.getAchievementID());
TbUserAchievement userAchievement = grantAchievementInternal(event.getUserID(), achievement);
if (userAchievement != null) {
newAchievements.add(userAchievement);
logger.info("用户 {} 通过事件 {} 获得成就: {}", event.getUserID(), event.getEventType(), achievement.getName());
@@ -748,17 +753,21 @@ public class ACHAchievementServiceImpl implements AchievementService {
/**
* 内部授予成就方法(不检查是否已获得)
*/
private TbUserAchievement grantAchievementInternal(String userID, String achievementID) {
private TbUserAchievement grantAchievementInternal(String userID, TbAchievement achievement) {
try {
TbUserAchievement userAchievement = new TbUserAchievement();
userAchievement.setID(IDUtils.generateID());
userAchievement.setUserID(userID);
userAchievement.setAchievementID(achievementID);
userAchievement.setAchievementID(achievement.getAchievementID());
userAchievement.setObtainTime(new Date());
int result = userAchievementMapper.insertUserAchievement(userAchievement);
if (result > 0) {
updateProgressToCompleted(userID, achievementID);
updateProgressToCompleted(userID, achievement.getAchievementID());
// 检查是否需要更新用户等级learning_time开头的成就
updateUserLevelIfNeeded(userID, achievement);
return userAchievement;
}
return null;
@@ -824,5 +833,56 @@ public class ACHAchievementServiceImpl implements AchievementService {
}
return null;
}
/**
* 检查并更新用户等级仅针对learning_time开头的成就
* @param userID 用户ID
* @param achievement 成就对象
*/
private void updateUserLevelIfNeeded(String userID, TbAchievement achievement) {
try {
// 检查成就ID是否以learning_time开头
if (achievement == null || achievement.getAchievementID() == null ||
!achievement.getAchievementID().startsWith("learning_time")) {
return;
}
// 检查成就是否有等级信息
if (achievement.getLevel() == null) {
logger.warn("成就没有等级信息: {}", achievement.getAchievementID());
return;
}
// 获取用户当前信息
TbSysUserInfo userInfo = userInfoMapper.selectById(userID);
if (userInfo == null) {
logger.warn("用户信息不存在: {}", userID);
return;
}
// 检查是否需要更新等级(只有当成就等级高于当前用户等级时才更新)
Integer currentLevel = userInfo.getLevel() != null ? userInfo.getLevel() : 0;
Integer achievementLevel = achievement.getLevel();
if (achievementLevel > currentLevel) {
userInfo.setLevel(achievementLevel);
int updateResult = userInfoMapper.updateUserInfo(userInfo);
if (updateResult > 0) {
logger.info("用户 {} 通过成就 {} 等级从 {} 提升到 {}",
userID, achievement.getAchievementID(), currentLevel, achievementLevel);
} else {
logger.error("更新用户等级失败: userID={}, achievementID={}", userID, achievement.getAchievementID());
}
} else {
logger.debug("用户 {} 当前等级 {} 已达到或超过成就等级 {},无需更新",
userID, currentLevel, achievementLevel);
}
} catch (Exception e) {
logger.error("更新用户等级时发生异常: userID={}, achievementID={}, error={}",
userID, achievement.getAchievementID(), e.getMessage(), e);
}
}
}

View File

@@ -272,8 +272,8 @@ public class NewsCrawlerTask extends PythonCommandTask {
// 状态和时间
item.setStatus(0); // 未处理
item.setCrawlTime(now);
ResultDomain<Boolean> pass = auditService.auditText(item.getContent());
if(pass.isSuccess() && pass.getData()){
ResultDomain<String> pass = auditService.auditText(item.getContent());
if(pass.isSuccess()){
item.setIsAudited(true);
passList.add(item);
}else{

View File

@@ -265,15 +265,15 @@ public class SysMenuServiceImpl implements SysMenuService {
}
// 检查菜单名称是否已存在(排除自身)
ResultDomain<Boolean> checkResult = checkMenuNameExists(menu.getName(), menu.getID());
if (!checkResult.isSuccess()) {
resultDomain.fail(checkResult.getMessage());
return resultDomain;
}
if (checkResult.getData()) {
resultDomain.fail("菜单名称已存在");
return resultDomain;
}
// ResultDomain<Boolean> checkResult = checkMenuNameExists(menu.getName(), menu.getID());
// if (!checkResult.isSuccess()) {
// resultDomain.fail(checkResult.getMessage());
// return resultDomain;
// }
// if (checkResult.getData()) {
// resultDomain.fail("菜单名称已存在");
// return resultDomain;
// }
// 设置更新时间
menu.setUpdateTime(new Date());

View File

@@ -139,7 +139,7 @@
SELECT COUNT(1)
FROM tb_sys_menu m
WHERE deleted = 0
AND m.name = #{name}
AND m.name = #{menuName}
<if test="excludeId != null and excludeId != ''">
AND m.id != #{excludeId}
</if>

View File

@@ -64,7 +64,7 @@ export const menuApi = {
* @returns Promise<ResultDomain<string>> 返回菜单ID
*/
async createMenu(menu: SysMenu): Promise<ResultDomain<string>> {
const response = await api.post<string>('/menu', menu);
const response = await api.post<string>('/menus/menu', menu);
return response.data;
},
@@ -75,7 +75,7 @@ export const menuApi = {
* @returns Promise<ResultDomain<boolean>>
*/
async updateMenu(menu: SysMenu): Promise<ResultDomain<boolean>> {
const response = await api.put<boolean>(`/menus`, menu);
const response = await api.put<boolean>(`/menus/menu`, menu);
return response.data;
},
@@ -85,7 +85,7 @@ export const menuApi = {
* @returns Promise<ResultDomain<boolean>>
*/
async deleteMenu(menuID: string): Promise<ResultDomain<boolean>> {
const response = await api.delete<boolean>(`/menus`, { menuID });
const response = await api.delete<boolean>(`/menus/menu`, { menuID });
return response.data;
},

View File

@@ -195,7 +195,7 @@ $spacing-xxl: 24px;
justify-content: flex-end;
align-items: center;
padding: $spacing-md 0;
margin-top: 32px;
margin-top: auto; // 自动推到底部
// background: #F9FAFB;
border-radius: $border-radius-large;

View File

@@ -195,7 +195,6 @@ function handleWheel(event: WheelEvent) {
// 处理导航点击
function handleNavClick(menu: SysMenu) {
activeDropdown.value = null;
if (menu.url) {
router.push(menu.url);
} else if (menu.children && menu.children.length > 0) {

View File

@@ -161,14 +161,41 @@ function generateRouteFromMenu(menu: SysMenu, isTopLevel = true): RouteRecordRaw
}
// 处理子路由
if (layout && LAYOUT_MAP[layout] && !hasChildren && menu.component && isTopLevel) {
// 如果指定了布局但没有子菜单,将页面组件作为子路由
if (layout && LAYOUT_MAP[layout] && menu.component && isTopLevel) {
// 如果指定了布局,将页面组件作为子路由
route.children = [{
path: '',
name: `${menu.menuID}_page`,
component: getComponent(menu.component),
meta: route.meta
}];
// 如果还有其他子菜单,继续添加
if (hasChildren) {
const pageChildren: SysMenu[] = [];
const normalChildren: SysMenu[] = [];
menu.children!.forEach(child => {
if (child.type === MenuType.PAGE) {
pageChildren.push(child);
} else {
normalChildren.push(child);
}
});
// 添加普通子菜单
normalChildren.forEach(child => {
const childRoute = generateRouteFromMenu(child, false);
if (childRoute) {
route.children!.push(childRoute);
}
});
// PAGE 类型的菜单保存到 meta
if (pageChildren.length > 0) {
route.meta.pageChildren = pageChildren;
}
}
} else if (hasChildren) {
// 处理有子菜单的情况
route.children = [];
@@ -290,6 +317,11 @@ function getComponent(componentName: string) {
return module;
})
.catch(error => {
console.error('[路由生成] 组件加载失败:', {
原始组件名: componentName,
最终路径: componentPath,
错误: error
});
// 返回404组件
return import('@/views/public/error/404.vue').catch(() =>
Promise.resolve({

View File

@@ -346,10 +346,12 @@ onMounted(() => {
<style lang="scss" scoped>
.my-message-list {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
min-height: calc(100vh - 76px - 40px);
display: flex;
flex-direction: column;
.header {
display: flex;
justify-content: space-between;

113
消息中心调试步骤.md Normal file
View File

@@ -0,0 +1,113 @@
# 消息中心前端加载问题调试步骤
## 问题描述
- 用户端消息中心页面空白(白屏)
- 消息详情页可以正常渲染
- 管理端消息管理页可以正常渲染
## 已完成的修改
### 1. 路由生成器添加调试日志
文件:`schoolNewsWeb/src/utils/route-generator.ts`
-`generateRoutes()` 函数中添加了路由生成日志
-`getComponent()` 函数中添加了组件加载日志
### 2. 添加分页容器样式
文件:`schoolNewsWeb/src/views/user/message/MyMessageListView.vue`
- 添加了 `.pagination-container` 样式
## 调试步骤
### 步骤1重启前端开发服务器
```bash
cd f:\Project\schoolNews\schoolNewsWeb
npm run dev
```
### 步骤2打开浏览器控制台
1. 打开浏览器Chrome/Edge
2. 按 F12 打开开发者工具
3. 切换到 Console控制台标签页
### 步骤3访问消息中心页面
访问: `http://localhost:8080/schoolNewsWeb/user/message`
### 步骤4查看控制台输出
查找以下日志:
- `[路由生成] 生成路由:` - 确认消息中心路由是否被生成
- `[路由生成] 尝试加载组件:` - 查看组件路径是否正确
- `[路由生成] 组件加载成功:` - 确认组件是否加载成功
- `[路由生成] 组件加载失败:` - 如果失败,查看详细错误信息
### 步骤5检查网络请求
1. 切换到 Network网络标签页
2. 刷新页面
3. 查看是否有失败的请求
4. 特别关注 `/api/message/my/page` 接口的响应
## 可能的问题和解决方案
### 问题1组件路径错误
**症状**:控制台显示 `[路由生成] 组件加载失败`
**解决方案**
检查数据库中 `tb_sys_menu` 表的 `component` 字段:
```sql
SELECT menu_id, name, url, component
FROM tb_sys_menu
WHERE menu_id = 'menu_user_message_center';
```
应该是:`user/message/MyMessageListView`
### 问题2API接口返回错误
**症状**:网络请求返回 4xx 或 5xx 错误
**解决方案**
1. 检查后端服务是否正常运行
2. 检查用户是否已登录
3. 检查权限配置
### 问题3组件导入错误
**症状**:控制台显示 `Cannot find module` 或类似错误
**解决方案**
检查 `MessagePriorityBadge` 组件是否正确导出:
```typescript
// src/components/message/index.ts
export { default as MessagePriorityBadge } from './MessagePriorityBadge.vue';
```
### 问题4数据为空但没有错误
**症状**:页面显示"暂无消息",但实际数据库中有数据
**解决方案**
1. 检查API返回的数据结构
2.`MyMessageListView.vue``loadMessages()` 函数中添加 console.log
## 常见错误代码对照
### 路径转换示例
数据库配置:`user/message/MyMessageListView`
添加前缀:`@/views/user/message/MyMessageListView`
转换别名:`../views/user/message/MyMessageListView.vue`
最终解析:`src/views/user/message/MyMessageListView.vue`
### 路由配置示例
```javascript
{
path: '/user/message',
name: 'menu_user_message_center',
component: () => import('../views/user/message/MyMessageListView.vue'),
meta: {
title: '消息中心',
requiresAuth: true
}
}
```
## 下一步操作
根据控制台输出的错误信息,确定具体问题后再进行针对性修复。