首页 > 编程语言 >Java全栈项目-校园志愿者服务平台开发实践

Java全栈项目-校园志愿者服务平台开发实践

时间:2025-01-16 09:32:05浏览次数:3  
标签:Java 服务平台 public 全栈 Result activity return NULL id

项目简介

校园志愿者服务平台是一个面向高校的志愿服务管理系统,旨在提供志愿活动发布、报名、签到、时长统计等功能,促进校园志愿服务的规范化管理和高效开展。本文将详细介绍该项目的技术架构、核心功能实现以及开发过程中的经验总结。

技术栈

后端技术

  • Spring Boot 2.7.0
  • Spring Security
  • MyBatis Plus
  • MySQL 8.0
  • Redis
  • JWT

前端技术

  • Vue 3
  • Element Plus
  • Axios
  • Vuex
  • Vue Router

核心功能模块

1. 用户认证与授权

  • 基于JWT的登录认证
  • 基于RBAC的权限控制
  • 角色分为管理员、志愿者管理员、普通志愿者

2. 志愿活动管理

  • 活动发布与审核
  • 活动报名与取消
  • 活动签到打卡
  • 活动评价反馈

3. 志愿时长管理

  • 自动统计志愿时长
  • 时长审核与确认
  • 志愿者排行榜

4. 消息通知

  • 活动提醒
  • 系统公告
  • 站内信

数据库设计

核心表结构

-- 用户表
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  `real_name` varchar(50),
  `phone` varchar(20),
  `email` varchar(100),
  `role_id` bigint,
  `create_time` datetime,
  `update_time` datetime,
  PRIMARY KEY (`id`)
);

-- 活动表
CREATE TABLE `activity` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `description` text,
  `start_time` datetime,
  `end_time` datetime,
  `location` varchar(200),
  `max_participants` int,
  `status` tinyint,
  `create_user_id` bigint,
  `create_time` datetime,
  PRIMARY KEY (`id`)
);

-- 报名表
CREATE TABLE `registration` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `activity_id` bigint NOT NULL,
  `user_id` bigint NOT NULL,
  `status` tinyint,
  `register_time` datetime,
  PRIMARY KEY (`id`)
);

关键功能实现

1. 活动报名流程

@Service
public class ActivityServiceImpl implements ActivityService {
    
    @Autowired
    private ActivityMapper activityMapper;
    
    @Autowired
    private RegistrationMapper registrationMapper;
    
    @Transactional
    public Result register(Long activityId, Long userId) {
        // 检查活动是否存在且开放报名
        Activity activity = activityMapper.selectById(activityId);
        if (activity == null || activity.getStatus() != ActivityStatus.OPEN) {
            return Result.fail("活动不存在或未开放报名");
        }
        
        // 检查是否已报名
        Registration exist = registrationMapper.selectByActivityAndUser(activityId, userId);
        if (exist != null) {
            return Result.fail("已报名该活动");
        }
        
        // 检查人数限制
        int count = registrationMapper.countByActivityId(activityId);
        if (count >= activity.getMaxParticipants()) {
            return Result.fail("活动名额已满");
        }
        
        // 创建报名记录
        Registration registration = new Registration();
        registration.setActivityId(activityId);
        registration.setUserId(userId);
        registration.setStatus(RegistrationStatus.REGISTERED);
        registration.setRegisterTime(new Date());
        
        registrationMapper.insert(registration);
        return Result.ok();
    }
}

2. 签到打卡功能

@Service
public class CheckinServiceImpl implements CheckinService {

    @Autowired
    private RedisTemplate redisTemplate;
    
    public Result checkin(Long activityId, Long userId, String location) {
        // 生成签到key
        String key = String.format("checkin:%d:%d", activityId, userId);
        
        // 防止重复签到
        if (redisTemplate.hasKey(key)) {
            return Result.fail("今日已签到");
        }
        
        // 验证位置信息
        if (!validateLocation(activityId, location)) {
            return Result.fail("不在活动范围内");
        }
        
        // 记录签到
        redisTemplate.opsForValue().set(key, "1", 24, TimeUnit.HOURS);
        
        return Result.ok();
    }
}

性能优化

1. Redis缓存优化

  • 使用Redis缓存热点活动数据
  • 实现分布式Session
  • 防止重复提交

2. MySQL优化

  • 合理建立索引
  • 分页查询优化
  • 大数据量批量操作

3. 前端优化

  • 路由懒加载
  • 组件按需加载
  • 图片懒加载

项目部署

1. 环境准备

  • JDK 1.8+
  • MySQL 8.0
  • Redis 6.0
  • Nginx 1.18

2. 部署步骤

# 后端打包
mvn clean package

# 前端打包
npm run build

# 启动服务
nohup java -jar volunteer-service.jar &

# Nginx配置
server {
    listen 80;
    server_name volunteer.example.com;
    
    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }
    
    location /api {
        proxy_pass http://localhost:8080;
    }
}

项目总结

技术亮点

  1. 采用前后端分离架构,提高开发效率
  2. 使用Redis实现高并发场景优化
  3. 实现基于RBAC的权限控制
  4. 统一异常处理和接口规范

遇到的问题及解决方案

  1. 签到并发问题:使用Redis分布式锁解决
  2. 大数据量导出:异步任务+分片处理
  3. 前端性能优化:路由懒加载、组件缓存

项目收获

  1. 加深对Spring Boot全家桶的理解
  2. 提升系统设计和优化能力
  3. 积累项目开发最佳实践

未来展望

  1. 引入微服务架构
  2. 添加数据分析功能
  3. 开发移动端应用
  4. 接入第三方平台

结语

通过这个项目的开发,不仅实现了校园志愿服务的信息化管理,也积累了宝贵的全栈开发经验。项目中的很多技术方案和最佳实践,都可以在其他项目中借鉴使用。

校园志愿者服务平台核心功能详解

一、用户认证与授权模块

1. JWT认证流程实现

@Service
public class AuthServiceImpl implements AuthService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    public Result login(String username, String password) {
        // 1. 验证用户
        User user = userMapper.selectByUsername(username);
        if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
            return Result.fail("用户名或密码错误");
        }
        
        // 2. 生成Token
        String token = jwtTokenUtil.generateToken(user);
        
        // 3. 返回用户信息和Token
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("user", UserVO.fromUser(user));
        
        return Result.ok(result);
    }
}

// JWT工具类
@Component
public class JwtTokenUtil {
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private Long expiration;
    
    public String generateToken(User user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getId());
        claims.put("username", user.getUsername());
        claims.put("role", user.getRole());
        
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    
    public Claims getClaimsFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }
}

2. RBAC权限控制

// 权限注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresRole {
    String value();
}

// 权限拦截器
@Component
public class RoleInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 获取方法上的注解
        RequiresRole requiresRole = ((HandlerMethod) handler).getMethodAnnotation(RequiresRole.class);
        if (requiresRole == null) {
            return true;
        }
        
        // 获取用户角色
        String userRole = SecurityContextHolder.getContext().getAuthentication().getAuthorities().iterator().next().getAuthority();
        
        // 验证权限
        if (!requiresRole.value().equals(userRole)) {
            throw new AccessDeniedException("无权限访问");
        }
        
        return true;
    }
}

3. 角色权限表设计

-- 角色表
CREATE TABLE `role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `role_name` varchar(50) NOT NULL,
  `role_code` varchar(50) NOT NULL,
  `description` varchar(200),
  `create_time` datetime,
  PRIMARY KEY (`id`)
);

-- 权限表
CREATE TABLE `permission` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `permission_name` varchar(100) NOT NULL,
  `permission_code` varchar(100) NOT NULL,
  `url` varchar(200),
  `method` varchar(10),
  PRIMARY KEY (`id`)
);

-- 角色权限关联表
CREATE TABLE `role_permission` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `role_id` bigint NOT NULL,
  `permission_id` bigint NOT NULL,
  PRIMARY KEY (`id`)
);

二、志愿活动管理模块

1. 活动发布与审核

@Service
public class ActivityServiceImpl implements ActivityService {
    
    @Autowired
    private ActivityMapper activityMapper;
    
    @Autowired
    private MessageService messageService;
    
    @RequiresRole("ADMIN")
    public Result publishActivity(ActivityDTO activityDTO) {
        // 1. 参数校验
        validateActivityParams(activityDTO);
        
        // 2. 保存活动信息
        Activity activity = new Activity();
        BeanUtils.copyProperties(activityDTO, activity);
        activity.setStatus(ActivityStatus.PENDING);
        activity.setCreateTime(new Date());
        
        activityMapper.insert(activity);
        
        // 3. 发送审核通知
        messageService.sendAuditNotification(activity);
        
        return Result.ok();
    }
    
    @RequiresRole("ADMIN")
    public Result auditActivity(Long activityId, Integer auditStatus, String remark) {
        Activity activity = activityMapper.selectById(activityId);
        if (activity == null) {
            return Result.fail("活动不存在");
        }
        
        // 更新审核状态
        activity.setStatus(auditStatus);
        activity.setAuditRemark(remark);
        activity.setAuditTime(new Date());
        
        activityMapper.updateById(activity);
        
        // 发送审核结果通知
        messageService.sendAuditResultNotification(activity);
        
        return Result.ok();
    }
}

2. 活动报名管理

@Service
public class RegistrationServiceImpl implements RegistrationService {
    
    @Autowired
    private RegistrationMapper registrationMapper;
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Transactional
    public Result register(RegistrationDTO registrationDTO) {
        // 1. 检查活动名额
        String quotaKey = "activity:quota:" + registrationDTO.getActivityId();
        Long remainQuota = redisTemplate.opsForValue().decrement(quotaKey);
        
        if (remainQuota < 0) {
            redisTemplate.opsForValue().increment(quotaKey);
            return Result.fail("活动名额已满");
        }
        
        try {
            // 2. 创建报名记录
            Registration registration = new Registration();
            BeanUtils.copyProperties(registrationDTO, registration);
            registration.setStatus(RegistrationStatus.REGISTERED);
            registration.setRegisterTime(new Date());
            
            registrationMapper.insert(registration);
            
            return Result.ok();
        } catch (Exception e) {
            // 3. 发生异常时恢复名额
            redisTemplate.opsForValue().increment(quotaKey);
            throw e;
        }
    }
    
    public Result cancelRegistration(Long registrationId) {
        Registration registration = registrationMapper.selectById(registrationId);
        if (registration == null) {
            return Result.fail("报名记录不存在");
        }
        
        // 检查是否可以取消
        if (registration.getStatus() != RegistrationStatus.REGISTERED) {
            return Result.fail("当前状态不可取消");
        }
        
        // 更新状态
        registration.setStatus(RegistrationStatus.CANCELED);
        registration.setCancelTime(new Date());
        
        registrationMapper.updateById(registration);
        
        // 恢复活动名额
        String quotaKey = "activity:quota:" + registration.getActivityId();
        redisTemplate.opsForValue().increment(quotaKey);
        
        return Result.ok();
    }
}

3. 活动签到功能

@Service
public class CheckinServiceImpl implements CheckinService {
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Autowired
    private VolunteerHoursService volunteerHoursService;
    
    public Result checkin(CheckinDTO checkinDTO) {
        // 1. 生成签到key
        String checkinKey = String.format("checkin:%d:%d:%s", 
            checkinDTO.getActivityId(), 
            checkinDTO.getUserId(),
            DateUtil.format(new Date(), "yyyy-MM-dd"));
        
        // 2. 防止重复签到
        Boolean success = redisTemplate.opsForValue().setIfAbsent(checkinKey, "1", 24, TimeUnit.HOURS);
        if (!success) {
            return Result.fail("今日已签到");
        }
        
        // 3. 验证位置信息
        if (!validateLocation(checkinDTO)) {
            redisTemplate.delete(checkinKey);
            return Result.fail("不在签到范围内");
        }
        
        // 4. 记录志愿时长
        volunteerHoursService.recordHours(checkinDTO);
        
        return Result.ok();
    }
    
    private boolean validateLocation(CheckinDTO checkinDTO) {
        // 获取活动位置
        ActivityLocation activityLocation = getActivityLocation(checkinDTO.getActivityId());
        
        // 计算距离
        double distance = LocationUtil.calculateDistance(
            checkinDTO.getLatitude(),
            checkinDTO.getLongitude(),
            activityLocation.getLatitude(),
            activityLocation.getLongitude()
        );
        
        // 判断是否在有效范围内(如500米)
        return distance <= 500;
    }
}

4. 活动评价反馈

@Service
public class FeedbackServiceImpl implements FeedbackService {
    
    @Autowired
    private FeedbackMapper feedbackMapper;
    
    @Autowired
    private ActivityMapper activityMapper;
    
    public Result submitFeedback(FeedbackDTO feedbackDTO) {
        // 1. 验证活动状态
        Activity activity = activityMapper.selectById(feedbackDTO.getActivityId());
        if (activity == null || !ActivityStatus.FINISHED.equals(activity.getStatus())) {
            return Result.fail("活动未结束,暂不能评价");
        }
        
        // 2. 检查是否已评价
        if (feedbackMapper.existsByActivityAndUser(
                feedbackDTO.getActivityId(), 
                feedbackDTO.getUserId())) {
            return Result.fail("已提交过评价");
        }
        
        // 3. 保存评价
        Feedback feedback = new Feedback();
        BeanUtils.copyProperties(feedbackDTO, feedback);
        feedback.setCreateTime(new Date());
        
        feedbackMapper.insert(feedback);
        
        // 4. 更新活动评分
        updateActivityRating(feedbackDTO.getActivityId());
        
        return Result.ok();
    }
    
    private void updateActivityRating(Long activityId) {
        // 计算平均分
        Double avgRating = feedbackMapper.calculateAverageRating(activityId);
        
        // 更新活动评分
        Activity activity = new Activity();
        activity.setId(activityId);
        activity.setRating(avgRating);
        
        activityMapper.updateById(activity);
    }
}

5. 相关数据表设计

-- 活动评价表
CREATE TABLE `feedback` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `activity_id` bigint NOT NULL,
  `user_id` bigint NOT NULL,
  `rating` int NOT NULL COMMENT '评分1-5',
  `content` text COMMENT '评价内容',
  `images` varchar(500) COMMENT '图片地址,多个逗号分隔',
  `create_time` datetime,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_activity_user` (`activity_id`,`user_id`)
);

-- 签到记录表
CREATE TABLE `checkin` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `activity_id` bigint NOT NULL,
  `user_id` bigint NOT NULL,
  `checkin_time` datetime NOT NULL,
  `latitude` decimal(10,6),
  `longitude` decimal(10,6),
  `location_description` varchar(200),
  PRIMARY KEY (`id`),
  KEY `idx_activity_user` (`activity_id`,`user_id`)
);

-- 志愿时长记录表
CREATE TABLE `volunteer_hours` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint NOT NULL,
  `activity_id` bigint NOT NULL,
  `hours` decimal(5,2) NOT NULL,
  `record_date` date NOT NULL,
  `status` tinyint NOT NULL COMMENT '0-待审核 1-已审核 2-已驳回',
  `create_time` datetime,
  `audit_time` datetime,
  `audit_user_id` bigint,
  `audit_remark` varchar(200),
  PRIMARY KEY (`id`),
  KEY `idx_user_date` (`user_id`,`record_date`)
);

校园志愿者服务平台功能详解(二)

一、志愿时长管理模块

1. 时长统计服务

@Service
public class VolunteerHoursServiceImpl implements VolunteerHoursService {

    @Autowired
    private VolunteerHoursMapper hoursMapper;
    
    @Autowired
    private CheckinMapper checkinMapper;
    
    @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
    public void calculateDailyHours() {
        // 获取昨天的日期
        LocalDate yesterday = LocalDate.now().minusDays(1);
        
        // 获取昨天的所有签到记录
        List<Checkin> checkins = checkinMapper.selectByDate(yesterday);
        
        for (Checkin checkin : checkins) {
            // 计算志愿时长
            BigDecimal hours = calculateHours(checkin);
            
            // 创建时长记录
            VolunteerHours record = new VolunteerHours();
            record.setUserId(checkin.getUserId());
            record.setActivityId(checkin.getActivityId());
            record.setHours(hours);
            record.setRecordDate(yesterday);
            record.setStatus(HoursStatus.PENDING);
            record.setCreateTime(new Date());
            
            hoursMapper.insert(record);
        }
    }
    
    private BigDecimal calculateHours(Checkin checkin) {
        // 获取活动信息
        Activity activity = activityMapper.selectById(checkin.getActivityId());
        
        // 计算实际参与时长
        Duration duration = Duration.between(
            checkin.getCheckinTime().toInstant(),
            activity.getEndTime().toInstant()
        );
        
        // 转换为小时,保留一位小数
        return BigDecimal.valueOf(duration.toMinutes())
            .divide(BigDecimal.valueOf(60), 1, RoundingMode.HALF_UP);
    }
}

2. 时长审核功能

@Service
public class HoursAuditServiceImpl implements HoursAuditService {

    @Autowired
    private VolunteerHoursMapper hoursMapper;
    
    @Autowired
    private MessageService messageService;
    
    @Transactional
    public Result auditHours(HoursAuditDTO auditDTO) {
        // 批量获取时长记录
        List<VolunteerHours> hoursList = hoursMapper.selectBatchIds(auditDTO.getHoursIds());
        
        // 验证所有记录是否都是待审核状态
        if (!validateStatus(hoursList)) {
            return Result.fail("存在已审核的记录");
        }
        
        // 批量更新审核状态
        Date now = new Date();
        for (VolunteerHours hours : hoursList) {
            hours.setStatus(auditDTO.getStatus());
            hours.setAuditUserId(auditDTO.getAuditorId());
            hours.setAuditTime(now);
            hours.setAuditRemark(auditDTO.getRemark());
            
            hoursMapper.updateById(hours);
            
            // 发送审核结果通知
            messageService.sendHoursAuditNotification(hours);
        }
        
        return Result.ok();
    }
    
    private boolean validateStatus(List<VolunteerHours> hoursList) {
        return hoursList.stream()
            .allMatch(hours -> HoursStatus.PENDING.equals(hours.getStatus()));
    }
}

3. 志愿者排行榜

@Service
public class VolunteerRankServiceImpl implements VolunteerRankService {

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Autowired
    private VolunteerHoursMapper hoursMapper;
    
    private static final String RANK_KEY = "volunteer:rank";
    
    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点更新排行榜
    public void updateRankList() {
        // 清除旧的排行榜数据
        redisTemplate.delete(RANK_KEY);
        
        // 获取志愿者总时长
        List<VolunteerRankVO> rankList = hoursMapper.selectTotalHoursByUser();
        
        // 更新Redis排行榜
        for (VolunteerRankVO rank : rankList) {
            redisTemplate.opsForZSet().add(RANK_KEY, 
                rank.getUserId().toString(), 
                rank.getTotalHours().doubleValue());
        }
    }
    
    public List<VolunteerRankVO> getTopRanks(int limit) {
        // 获取排名前N的志愿者
        Set<ZSetOperations.TypedTuple<String>> topSet = 
            redisTemplate.opsForZSet().reverseRangeWithScores(RANK_KEY, 0, limit - 1);
            
        // 转换为VO对象
        List<VolunteerRankVO> result = new ArrayList<>();
        int rank = 1;
        for (ZSetOperations.TypedTuple<String> item : topSet) {
            VolunteerRankVO vo = new VolunteerRankVO();
            vo.setUserId(Long.valueOf(item.getValue()));
            vo.setTotalHours(BigDecimal.valueOf(item.getScore()));
            vo.setRank(rank++);
            
            // 获取用户信息
            User user = userMapper.selectById(vo.getUserId());
            vo.setUsername(user.getUsername());
            vo.setRealName(user.getRealName());
            
            result.add(vo);
        }
        
        return result;
    }
}

二、消息通知模块

1. 消息服务实现

@Service
public class MessageServiceImpl implements MessageService {

    @Autowired
    private MessageMapper messageMapper;
    
    @Autowired
    private WebSocketService webSocketService;
    
    @Async
    public void sendActivityReminder(Activity activity) {
        // 获取所有报名用户
        List<Long> userIds = registrationMapper.selectUserIdsByActivity(activity.getId());
        
        // 创建消息内容
        Message message = new Message();
        message.setType(MessageType.ACTIVITY_REMINDER);
        message.setTitle("活动提醒");
        message.setContent(String.format("活动【%s】将在%s开始,请准时参加!", 
            activity.getTitle(),
            DateUtil.format(activity.getStartTime(), "MM-dd HH:mm")));
        
        // 批量保存消息
        List<Message> messages = userIds.stream()
            .map(userId -> {
                Message userMessage = new Message();
                BeanUtils.copyProperties(message, userMessage);
                userMessage.setUserId(userId);
                userMessage.setCreateTime(new Date());
                return userMessage;
            })
            .collect(Collectors.toList());
            
        messageMapper.insertBatch(messages);
        
        // 实时推送消息
        messages.forEach(msg -> 
            webSocketService.sendMessage(msg.getUserId(), msg));
    }
}

2. WebSocket消息推送

@ServerEndpoint("/websocket/{userId}")
@Component
public class WebSocketServer {

    private static final Map<Long, Session> SESSION_MAP = new ConcurrentHashMap<>();
    
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Long userId) {
        SESSION_MAP.put(userId, session);
    }
    
    @OnClose
    public void onClose(@PathParam("userId") Long userId) {
        SESSION_MAP.remove(userId);
    }
    
    public void sendMessage(Long userId, Object message) {
        Session session = SESSION_MAP.get(userId);
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(
                    JsonUtil.toJsonString(message));
            } catch (IOException e) {
                log.error("发送消息失败", e);
            }
        }
    }
}

3. 系统公告管理

@Service
public class AnnouncementServiceImpl implements AnnouncementService {

    @Autowired
    private AnnouncementMapper announcementMapper;
    
    @Autowired
    private MessageService messageService;
    
    @RequiresRole("ADMIN")
    public Result publishAnnouncement(AnnouncementDTO dto) {
        // 创建公告
        Announcement announcement = new Announcement();
        BeanUtils.copyProperties(dto, announcement);
        announcement.setStatus(AnnouncementStatus.PUBLISHED);
        announcement.setCreateTime(new Date());
        
        announcementMapper.insert(announcement);
        
        // 如果需要推送通知
        if (dto.getNeedNotify()) {
            Message message = new Message();
            message.setType(MessageType.SYSTEM_ANNOUNCEMENT);
            message.setTitle("系统公告");
            message.setContent(dto.getContent());
            message.setRelatedId(announcement.getId());
            
            // 获取所有活跃用户
            List<Long> activeUserIds = userMapper.selectActiveUserIds();
            
            // 批量发送通知
            messageService.batchSendMessage(activeUserIds, message);
        }
        
        return Result.ok();
    }
}

4. 站内信管理

@Service
public class InboxServiceImpl implements InboxService {

    @Autowired
    private MessageMapper messageMapper;
    
    public Result getUnreadMessages(Long userId) {
        // 获取未读消息
        List<Message> messages = messageMapper.selectUnreadByUser(userId);
        
        // 转换为VO对象
        List<MessageVO> voList = messages.stream()
            .map(message -> {
                MessageVO vo = new MessageVO();
                BeanUtils.copyProperties(message, vo);
                // 根据消息类型处理额外信息
                enrichMessageInfo(vo);
                return vo;
            })
            .collect(Collectors.toList());
            
        return Result.ok(voList);
    }
    
    @Transactional
    public Result markAsRead(List<Long> messageIds, Long userId) {
        // 验证消息归属
        if (!validateMessageOwnership(messageIds, userId)) {
            return Result.fail("无效的消息ID");
        }
        
        // 批量更新已读状态
        messageMapper.updateReadStatus(messageIds, MessageStatus.READ);
        
        return Result.ok();
    }
    
    private void enrichMessageInfo(MessageVO vo) {
        switch (vo.getType()) {
            case MessageType.ACTIVITY_REMINDER:
                Activity activity = activityMapper.selectById(vo.getRelatedId());
                vo.setExtra(ActivityVO.from(activity));
                break;
            case MessageType.SYSTEM_ANNOUNCEMENT:
                Announcement announcement = announcementMapper.selectById(vo.getRelatedId());
                vo.setExtra(AnnouncementVO.from(announcement));
                break;
            // 其他消息类型处理...
        }
    }
}

5. 相关数据表设计

-- 消息表
CREATE TABLE `message` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint NOT NULL,
  `type` tinyint NOT NULL COMMENT '消息类型:1-活动提醒 2-系统公告 3-志愿时长审核',
  `title` varchar(100) NOT NULL,
  `content` text NOT NULL,
  `status` tinyint NOT NULL COMMENT '状态:0-未读 1-已读',
  `related_id` bigint COMMENT '关联ID',
  `create_time` datetime NOT NULL,
  `read_time` datetime,
  PRIMARY KEY (`id`),
  KEY `idx_user_status` (`user_id`, `status`)
);

-- 系统公告表
CREATE TABLE `announcement` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `content` text NOT NULL,
  `status` tinyint NOT NULL COMMENT '状态:0-草稿 1-已发布',
  `publisher_id` bigint NOT NULL,
  `publish_time` datetime,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`id`)
);

-- 消息模板表
CREATE TABLE `message_template` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `template_code` varchar(50) NOT NULL,
  `template_name` varchar(100) NOT NULL,
  `content` text NOT NULL,
  `params` varchar(500) COMMENT '参数说明',
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_code` (`template_code`)
);

标签:Java,服务平台,public,全栈,Result,activity,return,NULL,id
From: https://blog.csdn.net/exlink2012/article/details/145105951

相关文章

  • Java中的高效集合操作:Stream API实战指南
    Java中的高效集合操作:StreamAPI实战指南1.引言:集合操作的痛点在日常开发中,我们经常需要对集合进行各种操作,比如过滤、映射、排序、聚合等。传统的做法是使用for循环或Iterator,代码冗长且容易出错。比如:List<String>names=newArrayList<>();for(Useruser:users......
  • Java异常
    什么是异常异常的捕获与抛出trycatchfinally的使用publicclassTest{publicstaticvoidmain(String[]args){inta=1;intb=0;try{//try监控区域System.out.println(a/b);}catch(ArithmeticExce......
  • 【Java开发】实现 License 认证(只校验有效期)
    一、License介绍License也就是版权许可证书,一般用于收费软件给付费用户提供的访问许可证明1.1应用场景应用部署在客户的内网环境这种情况开发者无法控制客户的网络环境,也不能保证应用所在服务器可以访问外网因此通常的做法是使用服务器许可文件,在应用启动的时候加载证书然......
  • 【Java安全】浅谈内存马
    一、内存马概述1.1内存马产生的背景1.2Java内存马的基本原理1.3Java内存马的类型1.4Java内存马的使用场景二、内存马注入实战演示2.1JSP注入Filter内存马2.2Fastjson反序列化注入内存马2.3注入Agent内存马三、内存马的检测与防御......
  • java学习第二天
    Hello~Hello,World!(代码入门第一个代码)随便新建一个文件夹,存放代码新建一个Java文件文件后缀名为.javaHello.java【注意点】系统可能没有显示文件后缀名,需要手动打开,首先点击查看然后再在里面的显示给文件扩展名打钩就可以看到文件的后缀名了编写程序在文件夹地......
  • JAVA开源毕业设计 中药实验管理系统 Vue.JS+SpringBoot+MySQL
    本文项目编号T130,文末自助获取源码\color{red}{T130,文末自助获取源码}......
  • JAVA开源毕业设计 网上商城系统 Vue.JS+SpringBoot+MySQL
    本文项目编号T129,文末自助获取源码\color{red}{T129,文末自助获取源码}......
  • JAVA开源毕业设计 编程训练系统 Vue.JS+SpringBoot+MySQL
    本文项目编号T128,文末自助获取源码\color{red}{T128,文末自助获取源码}......
  • 【JavaSecLab靶场】Java综合漏洞平台
    免责声明:请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。在我们平时的网络安全工作中,经常会面对各种各样的挑战。比如,进行定期的漏洞扫描、代码审计,甚至是参与红蓝对抗演练时,发现漏洞后往往需要及时将其修复。最近,我接触到了一款开源的网络安全工......
  • Java-exp5
    目录(1) 实验流程图(软件设计简易流程图+每个按钮添加监听Event,画出监听程序设计流程图)(2)页面布局设计图 (3)实验源代码(粘贴源代码):SimpleCalculator.java ScienceCalculator.javaBaseConverter.javaDrawFn.java(4)实验代码、过程、相应结果(截图)并对实验进行说明和分......