Java志愿者平台案例

wen java案例 2

本文目录导读:

Java志愿者平台案例

  1. 数据库设计
  2. 核心代码实现
  3. 运行与测试
  4. 扩展与优化建议

这是一个基于 Spring Boot + Thymeleaf + MySQL 的志愿者平台案例,该案例模拟了志愿者注册、查看活动、报名活动以及后台管理的基本流程。

1 技术栈

  • 后端: Spring Boot 2.x, Spring Data JPA, Spring Security
  • 前端: Thymeleaf, Bootstrap 5, HTML5, CSS3
  • 数据库: MySQL 8.0
  • 构建工具: Maven

2 功能模块

模块名称 功能描述
用户模块 志愿者注册、登录、个人信息管理、服务时长统计
活动模块 查看活动列表、活动详情、报名/取消报名
组织模块 查看组织信息、申请加入组织
后台管理 活动管理(CRUD)、用户管理、数据统计
通知模块 活动提醒、报名确认通知

数据库设计

SQL建表语句

-- 用户表
CREATE TABLE `volunteer_user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL UNIQUE,
  `password` varchar(100) NOT NULL,
  `real_name` varchar(50),
  `phone` varchar(20),
  `email` varchar(100),
  `avatar` varchar(255),
  `role` enum('ADMIN','VOLUNTEER','ORGANIZATION') DEFAULT 'VOLUNTEER',
  `total_hours` decimal(10,2) DEFAULT 0.00,
  `status` tinyint DEFAULT 1,
  `created_at` datetime,
  `updated_at` datetime,
  PRIMARY KEY (`id`)
);
-- 活动表
CREATE TABLE `activity` (
  `id` bigint NOT NULL AUTO_INCREMENT, varchar(200) NOT NULL,
  `description` text,
  `location` varchar(255),
  `start_time` datetime,
  `end_time` datetime,
  `max_volunteers` int DEFAULT 0,
  `registered_volunteers` int DEFAULT 0,
  `status` enum('PENDING','ACTIVE','COMPLETED','CANCELLED') DEFAULT 'PENDING',
  `category` varchar(50),
  `hours_awarded` decimal(10,2) DEFAULT 0.00,
  `organization_id` bigint,
  `created_at` datetime,
  `updated_at` datetime,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`organization_id`) REFERENCES `volunteer_user`(`id`)
);
-- 报名表
CREATE TABLE `registration` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `volunteer_id` bigint NOT NULL,
  `activity_id` bigint NOT NULL,
  `status` enum('REGISTERED','CONFIRMED','COMPLETED','CANCELLED') DEFAULT 'REGISTERED',
  `registration_time` datetime,
  `checkin_time` datetime,
  `checkout_time` datetime,
  `actual_hours` decimal(10,2),
  `created_at` datetime,
  `updated_at` datetime,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`volunteer_id`) REFERENCES `volunteer_user`(`id`),
  FOREIGN KEY (`activity_id`) REFERENCES `activity`(`id`),
  UNIQUE KEY `uk_vol_act` (`volunteer_id`, `activity_id`)
);

核心代码实现

1 项目结构

src/main/java/com/volunteer
├── config
│   ├── SecurityConfig.java          # Spring Security配置
│   └── WebMvcConfig.java            # MVC配置
├── controller
│   ├── HomeController.java          # 首页控制器
│   ├── ActivityController.java      # 活动控制器
│   ├── UserController.java          # 用户控制器
│   └── AdminController.java         # 管理员控制器
├── entity
│   ├── User.java
│   ├── Activity.java
│   └── Registration.java
├── repository
│   ├── UserRepository.java
│   ├── ActivityRepository.java
│   └── RegistrationRepository.java
├── service
│   ├── UserService.java
│   ├── ActivityService.java
│   └── RegistrationService.java
├── dto
│   └── ActivityDTO.java
└── VolunteerApplication.java

2 核心实体类

User.java

@Entity
@Table(name = "volunteer_user")
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(unique = true, nullable = false)
    private String username;
    @Column(nullable = false)
    private String password;
    private String realName;
    private String phone;
    private String email;
    private String avatar;
    @Enumerated(EnumType.STRING)
    private UserRole role = UserRole.VOLUNTEER;
    @Column(precision = 10, scale = 2)
    private BigDecimal totalHours = BigDecimal.ZERO;
    private Integer status = 1;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
    }
    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }
}
enum UserRole {
    ADMIN, VOLUNTEER, ORGANIZATION
}

3 服务层关键实现

ActivityService.java (活动报名核心逻辑)

@Service
@Transactional
public class ActivityService {
    @Autowired
    private ActivityRepository activityRepository;
    @Autowired
    private RegistrationRepository registrationRepository;
    public String registerForActivity(Long volunteerId, Long activityId) {
        // 1. 检查活动是否存在
        Activity activity = activityRepository.findById(activityId)
            .orElseThrow(() -> new RuntimeException("活动不存在"));
        // 2. 检查活动状态
        if (activity.getStatus() != ActivityStatus.ACTIVE) {
            return "活动未开放报名";
        }
        // 3. 检查是否已报名
        if (registrationRepository.existsByVolunteerIdAndActivityId(volunteerId, activityId)) {
            return "您已报名此活动";
        }
        // 4. 检查名额是否已满
        if (activity.getRegisteredVolunteers() >= activity.getMaxVolunteers()) {
            return "活动名额已满";
        }
        // 5. 创建报名记录
        Registration registration = new Registration();
        registration.setVolunteerId(volunteerId);
        registration.setActivityId(activityId);
        registration.setStatus(RegistrationStatus.REGISTERED);
        registration.setRegistrationTime(LocalDateTime.now());
        registrationRepository.save(registration);
        // 6. 更新活动已报名人数
        activity.setRegisteredVolunteers(activity.getRegisteredVolunteers() + 1);
        activityRepository.save(activity);
        return "报名成功";
    }
    public String cancelRegistration(Long volunteerId, Long activityId) {
        // 取消报名逻辑,释放名额
        Registration registration = registrationRepository
            .findByVolunteerIdAndActivityId(volunteerId, activityId)
            .orElseThrow(() -> new RuntimeException("报名记录不存在"));
        if (registration.getStatus() != RegistrationStatus.REGISTERED) {
            return "无法取消,报名状态已改变";
        }
        registration.setStatus(RegistrationStatus.CANCELLED);
        registrationRepository.save(registration);
        Activity activity = activityRepository.findById(activityId).get();
        activity.setRegisteredVolunteers(activity.getRegisteredVolunteers() - 1);
        activityRepository.save(activity);
        return "取消成功";
    }
}

4 配置类

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/register", "/login", "/css/**", "/js/**", "/images/**").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/activity/**").authenticated()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .permitAll()
            .and()
            .logout()
                .logoutSuccessUrl("/login?logout")
                .permitAll();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

5 前端页面示例

活动列表页面 (activity-list.html)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">志愿活动列表</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <h2>志愿活动列表</h2>
        <div class="row mt-4">
            <div class="col-md-4 mb-4" th:each="activity : ${activities}">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title" th:text="${activity.title}">活动标题</h5>
                        <p class="card-text" th:text="${activity.description}">活动描述</p>
                        <p class="card-text">
                            <small class="text-muted">
                                📍 <span th:text="${activity.location}">地点</span><br>
                                🕐 <span th:text="${#temporals.format(activity.startTime, 'yyyy-MM-dd HH:mm')}">开始时间</span><br>
                                👥 已报名: <span th:text="${activity.registeredVolunteers}">0</span> / <span th:text="${activity.maxVolunteers}">50</span>
                            </small>
                        </p>
                        <div th:if="${activity.status.name() == 'ACTIVE'}">
                            <button class="btn btn-primary" 
                                    th:onclick="'registerForActivity(' + ${activity.id} + ')'
                                    th:disabled="${activity.registeredVolunteers >= activity.maxVolunteers}">
                                立即报名
                            </button>
                        </div>
                        <div th:unless="${activity.status.name() == 'ACTIVE'}" class="text-muted">
                            <span th:if="${activity.status.name() == 'PENDING'}">即将开始</span>
                            <span th:if="${activity.status.name() == 'COMPLETED'}">已结束</span>
                            <span th:if="${activity.status.name() == 'CANCELLED'}">已取消</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script th:inline="javascript">
        function registerForActivity(activityId) {
            fetch(`/activity/register/${activityId}`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                }
            })
            .then(response => response.text())
            .then(data => {
                alert(data);
                location.reload();
            });
        }
    </script>
</body>
</html>

运行与测试

1 配置文件

application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/volunteer_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: root
    password: yourpassword
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
server:
  port: 8080

2 启动步骤

  1. 创建数据库

    CREATE DATABASE volunteer_db;
  2. 修改配置 修改 application.yml 中的数据库连接信息

  3. 运行项目

    mvn spring-boot:run
  4. 访问系统

    • 首页: http://localhost:8080
    • 默认管理员账号: admin / admin123

扩展与优化建议

1 功能扩展

  • 积分系统: 根据服务时长和活动评价给予积分
  • 地图服务: 集成百度/高德地图,显示活动位置
  • 即时通讯: 使用WebSocket实现志愿者即时通讯
  • 智能推荐: 基于志愿者兴趣推荐活动

2 性能优化

  • 使用Redis缓存热门活动数据
  • 异步处理报名通知邮件/短信
  • 数据库读写分离

3 安全性增强

  • 增加验证码功能防止恶意注册
  • 限制单个IP请求频率
  • 审计日志记录关键操作

这个案例提供了一个完整的志愿者平台基础框架,可以直接运行并作为实际项目的基础,你可以根据具体需求进行功能扩展和定制。

抱歉,评论功能暂时关闭!