# 社团管理系统技术架构设计 ## 1. 技术架构总览 ### 1.1 整体架构 ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 前端Web应用 │ │ 移动端应用 │ │ 管理后台 │ │ Vue 3 + TS │ │ uni-app │ │ Element Plus │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ └───────────────────────┼───────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ API网关 + 认证中心 │ │ Spring Security + Sa-Token │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 社团管理微服务 │ │ Spring Boot 3.4.6 │ │ MyBatis-Plus │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 数据持久层 │ │ MySQL 8.0 + Redis │ │ 文件存储 + 消息队列 │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.2 模块化设计 - **stgl-club-management**: 社团管理核心模块 - **stgl-activity-management**: 活动管理模块 - **stgl-attendance-management**: 考勤签到模块 - **stgl-material-management**: 材料收集模块 - **stgl-integration**: 系统集成模块 ## 2. 数据库设计方案 ### 2.1 数据库架构 - **主数据库**: MySQL 8.0,存储业务数据 - **缓存数据库**: Redis,存储会话、缓存数据 - **分库分表策略**: 按租户分库,大表按时间分表 ### 2.2 核心表结构设计 #### 社团信息表(stgl_club) ```sql CREATE TABLE `stgl_club` ( `id` bigint(20) NOT NULL COMMENT '主键ID', `tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号', `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门', `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)', `club_name` varchar(100) NOT NULL COMMENT '社团名称', `club_type` varchar(50) NOT NULL COMMENT '社团类型', `establish_date` date COMMENT '成立日期', `description` text COMMENT '社团简介', `email` varchar(100) COMMENT '邮箱', `phone` varchar(20) COMMENT '电话', `logo_url` varchar(500) COMMENT 'logo图片路径', `president_name` varchar(50) COMMENT '社长姓名', `president_student_no` varchar(20) COMMENT '社长学号', `president_dept` varchar(100) COMMENT '社长院系', `president_major` varchar(100) COMMENT '社长专业', `president_class` varchar(100) COMMENT '社长班级', `president_phone` varchar(20) COMMENT '社长联系方式', `vice_president_name` varchar(50) COMMENT '副社长姓名', `vice_president_student_no` varchar(20) COMMENT '副社长学号', `advisor_name` varchar(50) COMMENT '指导老师姓名', `advisor_dept` varchar(100) COMMENT '指导老师所属院系', `member_limit` int DEFAULT 0 COMMENT '成员规模上限', `departments` text COMMENT '组织架构', `recruit_cycle` varchar(50) COMMENT '招新周期', `recruit_time` varchar(50) COMMENT '招新时间', `activity_frequency` varchar(50) COMMENT '活动频率', `funding_sources` varchar(200) COMMENT '经费来源', `charter_url` varchar(500) COMMENT '章程文档路径', `honors` text COMMENT '历史荣誉', `status` tinyint DEFAULT 1 COMMENT '状态(1-正常 0-停用)', PRIMARY KEY (`id`), UNIQUE KEY `uk_club_name` (`club_name`, `tenant_id`), KEY `idx_club_type` (`club_type`), KEY `idx_create_time` (`create_time`), KEY `idx_tenant_deleted` (`tenant_id`, `deleted`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='社团信息表'; ``` #### 活动信息表(stgl_activity) ```sql CREATE TABLE `stgl_activity` ( `id` bigint(20) NOT NULL COMMENT '主键ID', `tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号', `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门', `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)', `activity_name` varchar(100) NOT NULL COMMENT '活动名称', `activity_type` varchar(50) NOT NULL COMMENT '活动类型', `activity_theme` varchar(100) COMMENT '活动主题', `start_time` datetime COMMENT '开始时间', `end_time` datetime COMMENT '结束时间', `location` varchar(200) COMMENT '活动地点', `description` text COMMENT '活动简介', `poster_url` varchar(500) COMMENT '活动海报路径', `related_club_id` bigint(20) COMMENT '关联社团ID', `organizer_name` varchar(100) COMMENT '主办方名称', `organizer_type` varchar(50) COMMENT '主办方类型', `organizer_contact` varchar(100) COMMENT '主办方联系人', `organizer_phone` varchar(20) COMMENT '主办方电话', `co_organizers` json COMMENT '承办方信息', `instructors` json COMMENT '指导老师列表', `registration_type` tinyint COMMENT '报名方式(1-个人 2-团队 3-均可)', `registration_start` datetime COMMENT '报名开始时间', `registration_end` datetime COMMENT '报名结束时间', `registration_status` tinyint COMMENT '报名状态', `max_participants` int COMMENT '总人数上限', `max_teams` int COMMENT '团队数量上限', `team_size_min` int COMMENT '每队最小人数', `team_size_max` int COMMENT '每队最大人数', `need_approval` tinyint DEFAULT 0 COMMENT '是否需要审核', `activity_status` varchar(20) COMMENT '活动状态', `is_finished` tinyint DEFAULT 0 COMMENT '是否为已办活动', `summary` text COMMENT '活动总结报告', `achievements` text COMMENT '活动成果', `import_source` varchar(200) COMMENT '导入来源信息', PRIMARY KEY (`id`), KEY `idx_activity_name` (`activity_name`), KEY `idx_activity_type` (`activity_type`), KEY `idx_start_time` (`start_time`), KEY `idx_end_time` (`end_time`), KEY `idx_status` (`activity_status`), KEY `idx_club_id` (`related_club_id`), KEY `idx_tenant_deleted` (`tenant_id`, `deleted`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='活动信息表'; ``` ### 2.3 索引优化策略 - **主键索引**: 所有表使用雪花ID作为主键 - **唯一索引**: 社团名称、活动名称等业务唯一字段 - **复合索引**: tenant_id + deleted(软删除 + 多租户) - **时间索引**: 创建时间、活动时间等时间字段 - **状态索引**: 活动状态、报名状态等枚举字段 ## 3. 后端服务架构 ### 3.1 分层架构设计 ``` Controller层 (REST API) ├── ClubController # 社团管理接口 ├── ActivityController # 活动管理接口 ├── AttendanceController # 考勤管理接口 └── MaterialController # 材料收集接口 Service层 (业务逻辑) ├── ClubService # 社团业务服务 ├── ActivityService # 活动业务服务 ├── AttendanceService # 考勤业务服务 └── MaterialService # 材料收集服务 Mapper层 (数据访问) ├── ClubMapper # 社团数据访问 ├── ActivityMapper # 活动数据访问 ├── AttendanceMapper # 考勤数据访问 └── MaterialMapper # 材料数据访问 Domain层 (实体模型) ├── entity/ # 数据库实体 ├── vo/ # 视图对象 └── dto/ # 数据传输对象 ``` ### 3.2 核心服务设计 #### 社团管理服务 (ClubService) ```java @Service public interface ClubService { // 基础CRUD R getClubById(Long id); R> getClubList(ClubQueryDto queryDto); R createClub(ClubCreateDto createDto); R updateClub(ClubUpdateDto updateDto); R deleteClub(Long id); // 成员管理 R> getClubMembers(Long clubId); R addMember(MemberAddDto addDto); R removeMember(Long clubId, Long userId); R importMembers(Long clubId, MultipartFile file); // 权限管理 R assignRole(Long clubId, Long userId, Integer roleType); R> getClubPermissions(Long clubId); } ``` #### 活动管理服务 (ActivityService) ```java @Service public interface ActivityService { // 活动CRUD R getActivityById(Long id); R getActivityList(ActivityQueryDto queryDto); R createActivity(ActivityCreateDto createDto); R updateActivity(ActivityUpdateDto updateDto); R deleteActivity(Long id); R copyActivity(Long sourceId, ActivityCreateDto createDto); // 报名管理 R> getRegistrations(Long activityId); R approveRegistration(Long registrationId, String remark); R rejectRegistration(Long registrationId, String remark); // 评分管理 R> getActivityRatings(Long activityId); R getActivityStats(Long activityId); } ``` ### 3.3 API接口设计规范 #### 统一响应格式 ```java // 分页查询响应 @Data public class TableDataInfo { private long total; private List rows; private int code; private String msg; } // 普通响应 @Data public class R { private int code; private String msg; private T data; public static R ok() { ... } public static R ok(T data) { ... } public static R fail(String msg) { ... } } ``` #### 接口URL规范 ``` GET /api/stgl/club/list # 社团列表 GET /api/stgl/club/{id} # 社团详情 POST /api/stgl/club # 创建社团 PUT /api/stgl/club/{id} # 更新社团 DELETE /api/stgl/club/{id} # 删除社团 GET /api/stgl/activity/list # 活动列表 POST /api/stgl/activity # 创建活动 PUT /api/stgl/activity/{id} # 更新活动 POST /api/stgl/activity/{id}/copy # 复制活动 GET /api/stgl/attendance/list # 考勤列表 POST /api/stgl/attendance/task # 创建考勤任务 POST /api/stgl/attendance/checkin # 签到接口 GET /api/stgl/material/list # 材料列表 POST /api/stgl/material/task # 创建收集任务 POST /api/stgl/material/submit # 提交材料 ``` ## 4. 前端架构设计 ### 4.1 Vue 3 + TypeScript 架构 ``` src/ ├── api/ # API接口定义 │ ├── club.ts # 社团API │ ├── activity.ts # 活动API │ └── attendance.ts # 考勤API ├── views/ # 页面组件 │ ├── club/ # 社团管理页面 │ ├── activity/ # 活动管理页面 │ └── attendance/ # 考勤管理页面 ├── components/ # 通用组件 │ ├── ClubSelector/ # 社团选择器 │ ├── ActivityCard/ # 活动卡片 │ └── AttendanceMap/ # 签到地图 ├── stores/ # 状态管理 │ ├── club.ts # 社团状态 │ └── activity.ts # 活动状态 ├── types/ # TypeScript类型定义 │ ├── club.ts # 社团类型 │ └── activity.ts # 活动类型 └── utils/ # 工具函数 ├── request.ts # HTTP请求 └── common.ts # 通用工具 ``` ### 4.2 核心组件设计 #### 社团管理组件 ```vue ``` #### 活动管理组件 ```vue ``` ### 4.3 状态管理设计 #### Pinia Store 设计 ```typescript // stores/club.ts export const useClubStore = defineStore('club', { state: () => ({ clubList: [] as Club[], currentClub: null as Club | null, loading: false, clubTypes: [ { label: '学术科技类', value: '学术科技类' }, { label: '文化艺术类', value: '文化艺术类' }, { label: '体育健身类', value: '体育健身类' }, { label: '实践技能类', value: '实践技能类' }, { label: '兴趣爱好类', value: '兴趣爱好类' }, { label: '思想政治类', value: '思想政治类' } ] }), actions: { async fetchClubList(params: ClubQueryParams) { this.loading = true try { const response = await getClubList(params) this.clubList = response.data.rows return response.data } finally { this.loading = false } }, async createClub(club: ClubCreateDto) { const response = await createClub(club) if (response.code === 200) { ElMessage.success('创建成功') await this.fetchClubList({}) } return response } } }) ``` ## 5. 移动端架构设计 ### 5.1 uni-app + uview-plus 架构 ``` pages/ ├── club/ # 社团页面 │ ├── list.vue # 社团列表 │ ├── detail.vue # 社团详情 │ └── join.vue # 加入社团 ├── activity/ # 活动页面 │ ├── list.vue # 活动列表 │ ├── detail.vue # 活动详情 │ └── register.vue # 活动报名 └── attendance/ # 考勤页面 ├── list.vue # 考勤列表 └── checkin.vue # 签到页面 components/ ├── club-card/ # 社团卡片组件 ├── activity-card/ # 活动卡片组件 └── map-checkin/ # 地图签到组件 utils/ ├── request.js # 网络请求 ├── location.js # 地理位置 └── storage.js # 本地存储 ``` ### 5.2 地理位置签到组件 ```vue ``` ## 6. 系统集成方案 ### 6.1 学生信息系统集成 ```java @Component public class StudentInfoIntegration { @Autowired private SysUserMapper userMapper; @Autowired private XgStudentMapper studentMapper; @Autowired private XgClassMapper classMapper; /** * 获取学生信息 */ public StudentVo getStudentInfo(String studentNo) { // 从用户表获取基础信息 SysUser user = userMapper.selectByUserName(studentNo); if (user == null) { throw new ServiceException("用户不存在"); } // 从学生表获取详细信息 XgStudent student = studentMapper.selectByStudentNo(studentNo); if (student == null) { throw new ServiceException("学生信息不存在"); } // 从班级表获取班级信息 XgClass classInfo = classMapper.selectById(student.getClassId()); // 组装返回数据 StudentVo studentVo = new StudentVo(); BeanUtils.copyProperties(user, studentVo); BeanUtils.copyProperties(student, studentVo); if (classInfo != null) { studentVo.setClassName(classInfo.getClassName()); studentVo.setMajorName(classInfo.getMajorName()); } return studentVo; } /** * 批量获取班级学生 */ public List getClassStudents(Long classId) { List students = studentMapper.selectByClassId(classId); return students.stream() .map(student -> getStudentInfo(student.getStudentNo())) .collect(Collectors.toList()); } } ``` ### 6.2 通知系统集成 ```java @Component public class NotificationIntegration { @Autowired private MessageService messageService; /** * 发送活动通知 */ public void sendActivityNotification(Long activityId, List userIds, String type) { Activity activity = activityService.getById(activityId); String title; String content; switch (type) { case "CREATE": title = "新活动通知"; content = String.format("【%s】活动已发布,报名时间:%s - %s", activity.getActivityName(), DateUtils.formatDateTime(activity.getRegistrationStart()), DateUtils.formatDateTime(activity.getRegistrationEnd())); break; case "APPROVAL": title = "报名审核通知"; content = String.format("您报名的【%s】活动审核已通过", activity.getActivityName()); break; case "CHECKIN": title = "签到提醒"; content = String.format("【%s】活动签到已开始,请及时签到", activity.getActivityName()); break; default: return; } // 发送消息 MessageDto messageDto = new MessageDto(); messageDto.setTitle(title); messageDto.setContent(content); messageDto.setType("SYSTEM"); messageDto.setUserIds(userIds); messageService.sendMessage(messageDto); } } ``` ## 7. 性能优化方案 ### 7.1 数据库优化 - **分页查询优化**: 使用覆盖索引避免回表 - **批量操作优化**: 使用MyBatis批量插入/更新 - **慢查询监控**: 启用MySQL慢查询日志 - **连接池优化**: 合理配置HikariCP参数 ### 7.2 缓存策略 ```java @Service public class ClubService { @Cacheable(value = "club", key = "#id") public ClubVo getClubById(Long id) { // 查询数据库 } @CacheEvict(value = "club", key = "#clubDto.id") public void updateClub(ClubUpdateDto clubDto) { // 更新数据库 } @Cacheable(value = "club:list", key = "#queryDto.hashCode()") public List getClubList(ClubQueryDto queryDto) { // 查询数据库 } } ``` ### 7.3 前端性能优化 - **组件懒加载**: 使用Vue 3的defineAsyncComponent - **图片懒加载**: 活动海报、社团logo延迟加载 - **虚拟滚动**: 大列表使用虚拟滚动组件 - **资源预加载**: 关键路由和组件预加载 ## 8. 安全设计方案 ### 8.1 认证授权 ```java @RestController @RequestMapping("/api/stgl/club") public class ClubController { @GetMapping("/list") @SaCheckPermission("stgl:club:list") public TableDataInfo list(ClubQueryDto queryDto) { // 权限检查通过后执行业务逻辑 } @PostMapping @SaCheckPermission("stgl:club:create") public R create(@Valid @RequestBody ClubCreateDto createDto) { // 创建社团 } } ``` ### 8.2 数据权限 ```java @Component public class DataPermissionHandler { /** * 根据用户权限过滤数据 */ public void filterDataByPermission(QueryWrapper queryWrapper) { Long userId = SecurityUtils.getUserId(); String userType = SecurityUtils.getUserType(); if ("student".equals(userType)) { // 学生只能看到自己参与的社团/活动 queryWrapper.eq("create_by", userId) .or() .exists("SELECT 1 FROM stgl_member WHERE relation_id = stgl_club.id AND user_id = " + userId); } else if ("teacher".equals(userType)) { // 老师可以看到自己指导的社团/活动 queryWrapper.like("advisor_name", SecurityUtils.getUsername()); } // 管理员可以看到所有数据,不需要额外过滤 } } ``` ## 9. 部署方案 ### 9.1 Docker容器化 ```dockerfile # Dockerfile for Backend FROM openjdk:17-jdk-alpine VOLUME /tmp COPY target/club-management.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"] # Dockerfile for Frontend FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM nginx:alpine COPY --from=0 /app/dist /usr/share/nginx/html ``` ### 9.2 监控方案 - **应用监控**: Spring Boot Actuator + Micrometer - **日志监控**: ELK Stack (Elasticsearch + Logstash + Kibana) - **性能监控**: Prometheus + Grafana - **错误追踪**: Sentry或自建错误收集系统 这个技术架构设计为社团管理系统的开发提供了完整的技术指导,涵盖了从数据库设计到前后端架构、系统集成、性能优化等各个方面。请根据实际项目需求进行适当调整。