From 73ffd576bc619562daba43832c55dbcb14411d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=97=AD=E6=98=8E?= Date: Wed, 27 Aug 2025 10:16:15 +0800 Subject: [PATCH] stgl2.0 --- .../controller/StglActivityController.java | 17 +- .../stgl/controller/StglClassController.java | 101 ++++ .../stgl/controller/StglMyController.java | 7 +- .../StglVolunteerMemberController.java | 26 +- .../stgl/domain/StglActivityRating.java | 5 +- .../stgl/domain/StglActivityTeamInfo.java | 38 ++ .../org/dromara/stgl/domain/StglClass.java | 77 +++ .../stgl/domain/StglVolunteerTeam.java | 5 + .../dromara/stgl/domain/bo/StglClassBo.java | 74 +++ .../stgl/domain/bo/StglVolunteerTeamBo.java | 5 + .../stgl/domain/vo/ActivityRatingVo.java | 101 ++++ .../dromara/stgl/domain/vo/MyActivityVo.java | 10 +- .../stgl/domain/vo/MyVolunteerTeamVo.java | 5 + .../dromara/stgl/domain/vo/StglClassVo.java | 104 ++++ .../stgl/domain/vo/StglVolunteerTeamVo.java | 5 + .../listener/StglVolunteerImportListener.java | 2 +- .../stgl/mapper/StglActivityRatingMapper.java | 11 + .../mapper/StglActivityTeamInfoMapper.java | 21 +- .../dromara/stgl/mapper/StglClassMapper.java | 65 +++ .../org/dromara/stgl/mapper/StglMyMapper.java | 7 +- .../stgl/service/IStglActivityService.java | 13 +- .../stgl/service/IStglClassService.java | 68 +++ .../dromara/stgl/service/IStglMyService.java | 5 +- .../StglActivityRegistrationServiceImpl.java | 17 +- .../service/impl/StglActivityServiceImpl.java | 236 ++++++++- .../impl/StglActivityTeamInfoServiceImpl.java | 1 - .../service/impl/StglClassServiceImpl.java | 152 ++++++ .../service/impl/StglClubServiceImpl.java | 126 +++++ .../service/impl/StglMemberServiceImpl.java | 95 +++- .../stgl/service/impl/StglMyServiceImpl.java | 15 +- .../impl/StglVolunteerTeamServiceImpl.java | 291 ++++++++-- .../mapper/StglActivityRatingMapper.xml | 28 + .../resources/mapper/StglMemberMapper.xml | 4 + .../main/resources/mapper/StglMyMapper.xml | 103 +++- .../stgl/StglActivityTeamInfoMapper.xml | 48 +- .../resources/mapper/stgl/StglClassMapper.xml | 242 +++++++++ .../resources/sql/stgl_class_permission.sql | 33 ++ .../database/社团管理/社团管理数据库设计.md | 7 +- .../社团管理需求/社团管理系统需求说明.md | 2 +- web/src/api/stgl/activity/types.ts | 52 +- .../api/stgl/activityregisterhall/types.ts | 5 + web/src/api/stgl/activityteaminfo/index.ts | 62 +++ web/src/api/stgl/activityteaminfo/types.ts | 241 +++++++++ web/src/api/stgl/my/types.ts | 6 +- web/src/api/stgl/stclass/index.ts | 64 +++ web/src/api/stgl/stclass/types.ts | 292 ++++++++++ web/src/components/AdvisorManager/index.vue | 166 ++---- .../components/ActivityDetailView.vue | 60 +-- .../components/ActivityMembersDialog.vue | 24 +- .../components/ActivityRatingView.vue | 231 ++++++-- .../activity/components/PendingMembersTab.vue | 495 ++++++++++++++--- .../components/RegisteredMembersTab.vue | 465 ++++++++++++++-- web/src/views/stgl/activity/hall/index.vue | 121 +++-- web/src/views/stgl/activity/index.vue | 190 ++++--- web/src/views/stgl/club/hall/index.vue | 25 +- .../club/info/components/JoinedMembersTab.vue | 9 +- .../info/components/PendingMembersTab.vue | 9 +- web/src/views/stgl/my/activity/index.vue | 498 ++++++++++++++++-- web/src/views/stgl/stclass/index.vue | 454 ++++++++++++++++ web/src/views/stgl/volunteer/member/index.vue | 16 +- .../team/components/JoinedMembersTab.vue | 13 +- .../team/components/PendingMembersTab.vue | 37 +- web/src/views/stgl/volunteer/team/index.vue | 4 +- 63 files changed, 5064 insertions(+), 647 deletions(-) create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClass.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClassBo.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/ActivityRatingVo.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClassVo.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/resources/sql/stgl_class_permission.sql create mode 100644 web/src/api/stgl/activityteaminfo/index.ts create mode 100644 web/src/api/stgl/activityteaminfo/types.ts create mode 100644 web/src/api/stgl/stclass/index.ts create mode 100644 web/src/api/stgl/stclass/types.ts create mode 100644 web/src/views/stgl/stclass/index.vue diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglActivityController.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglActivityController.java index c76b7a79..abf49ded 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglActivityController.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglActivityController.java @@ -20,6 +20,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.dromara.stgl.domain.bo.StglActivityBo; import org.dromara.stgl.domain.vo.StglActivityVo; +import org.dromara.stgl.domain.vo.ActivityRatingVo; import org.dromara.stgl.service.IStglActivityService; import java.util.List; @@ -106,8 +107,11 @@ public class StglActivityController extends BaseController { @Log(title = "复制活动信息", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping("/copy") - public R copy(@RequestParam("sourceId") Long sourceId, @RequestParam("newActivityName") String newActivityName) { - return toAjax(stglActivityService.copyActivity(sourceId, newActivityName)); + public R copy(@RequestParam("sourceId") Long sourceId, + @RequestParam("newActivityName") String newActivityName, + @RequestParam(value = "activityStatus", defaultValue = "0") String activityStatus, + @RequestParam(value = "registrationStatus", defaultValue = "0") Integer registrationStatus) { + return toAjax(stglActivityService.copyActivity(sourceId, newActivityName, activityStatus, registrationStatus)); } /** @@ -143,4 +147,13 @@ public class StglActivityController extends BaseController { return R.ok(stglActivityService.queryVolunteerTeamOptions()); } + /** + * 查询活动评分列表 + */ + @SaCheckPermission("stgl:activity:query") + @GetMapping("/{id}/ratings") + public R> getActivityRatings(@NotNull(message = "活动ID不能为空") @PathVariable Long id) { + return R.ok(stglActivityService.getActivityRatings(id)); + } + } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java new file mode 100644 index 00000000..5a4593d2 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java @@ -0,0 +1,101 @@ +package org.dromara.stgl.controller; + +import java.util.List; +import java.util.Map; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.stgl.domain.vo.StglClassVo; +import org.dromara.stgl.domain.bo.StglClassBo; +import org.dromara.stgl.service.IStglClassService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 班级学生社团、志愿者队伍、活动参加情况 + * + * @author Cursor AI + * @date 2025-01-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/stgl/stclass") +public class StglClassController extends BaseController { + + private final IStglClassService stglClassService; + + /** + * 查询班主任的班级列表 + */ + @SaCheckPermission("stgl:stclass:list") + @GetMapping("/teacher/classes") + public R>> getTeacherClasses() { + Long userId = LoginHelper.getUserId(); + List> list = stglClassService.getTeacherClasses(userId); + return R.ok(list); + } + + /** + * 查询班级学生社团、志愿者队伍、活动参加情况列表 + */ + @SaCheckPermission("stgl:stclass:list") + @GetMapping("/list") + public TableDataInfo list(StglClassBo bo, PageQuery pageQuery) { + return stglClassService.queryPageList(bo, pageQuery); + } + + /** + * 查询学生社团详情 + */ + @SaCheckPermission("stgl:stclass:query") + @GetMapping("/student/clubs/{studentId}") + public R>> getStudentClubs(@NotNull(message = "学生ID不能为空") @PathVariable Long studentId) { + List> list = stglClassService.getStudentClubs(studentId); + return R.ok(list); + } + + /** + * 查询学生志愿者队伍详情 + */ + @SaCheckPermission("stgl:stclass:query") + @GetMapping("/student/volunteer-teams/{studentId}") + public R>> getStudentVolunteerTeams(@NotNull(message = "学生ID不能为空") @PathVariable Long studentId) { + List> list = stglClassService.getStudentVolunteerTeams(studentId); + return R.ok(list); + } + + /** + * 查询学生活动详情 + */ + @SaCheckPermission("stgl:stclass:query") + @GetMapping("/student/activities/{studentId}") + public R>> getStudentActivities(@NotNull(message = "学生ID不能为空") @PathVariable Long studentId) { + List> list = stglClassService.getStudentActivities(studentId); + return R.ok(list); + } + + /** + * 导出班级学生社团、志愿者队伍、活动参加情况列表 + */ + @SaCheckPermission("stgl:stclass:export") + @Log(title = "班级学生社团、志愿者队伍、活动参加情况", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(StglClassBo bo, HttpServletResponse response) { + List list = stglClassService.queryList(bo); + ExcelUtil.exportExcel(list, "班级学生参与情况", StglClassVo.class, response); + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMyController.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMyController.java index 2ae947e4..6f12bd15 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMyController.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMyController.java @@ -64,8 +64,11 @@ public class StglMyController extends BaseController { @Operation(summary = "查询我报名的活动列表") @SaCheckPermission("stgl:my:list") @GetMapping("/activities") - public R> getMyActivities() { - List list = stglMyService.queryMyActivities(); + public R> getMyActivities( + @Parameter(description = "活动名称") @RequestParam(required = false) String activityName, + @Parameter(description = "活动类型") @RequestParam(required = false) String activityType, + @Parameter(description = "是否评分") @RequestParam(required = false) String isRated) { + List list = stglMyService.queryMyActivities(activityName, activityType, isRated); return R.ok(list); } diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglVolunteerMemberController.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglVolunteerMemberController.java index aef0ef40..46c51509 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglVolunteerMemberController.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglVolunteerMemberController.java @@ -52,7 +52,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 查询志愿者成员列表 */ - @SaCheckPermission("stgl:volunteerMember:list") + @SaCheckPermission("stgl:volunteermember:list") @GetMapping("/list") public TableDataInfo list(StglMemberBo bo, PageQuery pageQuery) { bo.setMemberType(2); // 志愿者成员 @@ -62,7 +62,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 导出志愿者成员列表 */ - @SaCheckPermission("stgl:volunteerMember:export") + @SaCheckPermission("stgl:volunteermember:export") @Log(title = "志愿者成员", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(StglMemberBo bo, HttpServletResponse response) { @@ -74,7 +74,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 获取志愿者成员详细信息 */ - @SaCheckPermission("stgl:volunteerMember:query") + @SaCheckPermission("stgl:volunteermember:query") @GetMapping("/{id}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { @@ -84,15 +84,15 @@ public class StglVolunteerMemberController extends BaseController { /** * 新增志愿者成员 */ - @SaCheckPermission("stgl:volunteerMember:add") + @SaCheckPermission("stgl:volunteermember:add") @Log(title = "志愿者成员", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping public R add(@Validated(AddGroup.class) @RequestBody StglMemberBo bo) { bo.setMemberType(2); // 志愿者成员 - // 如果前端没有传递roleType,设置默认值为1(队员) + // 如果前端没有传递roleType,设置默认值为队员 if (bo.getRoleType() == null) { - bo.setRoleType("1"); + bo.setRoleType("队员"); } return toAjax(stglMemberService.insertByBo(bo)); } @@ -100,7 +100,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 修改志愿者成员 */ - @SaCheckPermission("stgl:volunteerMember:edit") + @SaCheckPermission("stgl:volunteermember:edit") @Log(title = "志愿者成员", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping @@ -112,7 +112,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 删除志愿者成员 */ - @SaCheckPermission("stgl:volunteerMember:remove") + @SaCheckPermission("stgl:volunteermember:remove") @Log(title = "志愿者成员", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") public R remove(@NotEmpty(message = "主键不能为空") @@ -150,7 +150,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 根据队伍ID查询成员 */ - @SaCheckPermission("stgl:volunteerMember:list") + @SaCheckPermission("stgl:volunteermember:list") @GetMapping("/team/{teamId}") public R> getTeamMembers(@NotNull(message = "队伍ID不能为空") @PathVariable Long teamId) { @@ -164,7 +164,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 批量分配队伍 */ - @SaCheckPermission("stgl:volunteerMember:edit") + @SaCheckPermission("stgl:volunteermember:edit") @Log(title = "志愿者成员", businessType = BusinessType.UPDATE) @RepeatSubmit() @PostMapping("/assignTeam") @@ -175,7 +175,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 获取志愿者成员统计信息 */ - @SaCheckPermission("stgl:volunteerMember:query") + @SaCheckPermission("stgl:volunteermember:query") @GetMapping("/stats") public R getStats() { return R.ok(stglMemberService.getVolunteerMemberStats()); @@ -185,7 +185,7 @@ public class StglVolunteerMemberController extends BaseController { * 下载志愿者成员导入模板 */ @PostMapping("/importTemplate") - @SaCheckPermission("stgl:volunteerMember:import") + @SaCheckPermission("stgl:volunteermember:import") public void importTemplate(HttpServletResponse response) { // 创建示例数据 List templateData = new ArrayList<>(); @@ -211,7 +211,7 @@ public class StglVolunteerMemberController extends BaseController { /** * 导入志愿者成员数据 */ - @SaCheckPermission("stgl:volunteerMember:import") + @SaCheckPermission("stgl:volunteermember:import") @Log(title = "志愿者成员", businessType = BusinessType.IMPORT) @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R importData(@RequestPart("file") MultipartFile file, diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityRating.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityRating.java index 641abc24..0b305d7e 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityRating.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityRating.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.dromara.common.tenant.core.TenantEntity; +import java.math.BigDecimal; import java.time.LocalDateTime; /** @@ -48,9 +49,9 @@ public class StglActivityRating extends TenantEntity { private String studentName; /** - * 综合评分(1-5分) + * 综合评分(1-5分,保留1位小数) */ - private Integer ratingScore; + private BigDecimal ratingScore; /** * 评价意见 diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityTeamInfo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityTeamInfo.java index 997f61cf..b172547b 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityTeamInfo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglActivityTeamInfo.java @@ -2,6 +2,7 @@ package org.dromara.stgl.domain; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import lombok.EqualsAndHashCode; import org.dromara.common.tenant.core.TenantEntity; @@ -68,9 +69,46 @@ public class StglActivityTeamInfo extends TenantEntity { */ private String description; + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic(value = "0", delval = "1") + private Integer deleted; + // 扩展字段(不存储到数据库,用于显示) /** * 活动名称 */ private String activityName; + + // 队长详细信息字段(通过关联查询获取) + /** + * 队长性别 + */ + private String leaderGender; + + /** + * 队长联系电话 + */ + private String leaderPhone; + + /** + * 队长邮箱 + */ + private String leaderEmail; + + /** + * 队长院系 + */ + private String leaderDepartment; + + /** + * 队长专业 + */ + private String leaderMajor; + + /** + * 队长班级 + */ + private String leaderClassName; } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClass.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClass.java new file mode 100644 index 00000000..b0e7e1d4 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClass.java @@ -0,0 +1,77 @@ +package org.dromara.stgl.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.dromara.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 班级学生社团、志愿者队伍、活动参加情况对象 stgl_class + * + * @author Cursor AI + * @date 2025-01-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("xg_student_msg") +public class StglClass extends TenantEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String name; + + /** + * 性别 + */ + private String gender; + + /** + * 联系电话 + */ + private String phone; + + /** + * 班级ID + */ + private String classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 年级 + */ + private String grade; + + /** + * 学籍状态 + */ + private String studentStatus; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglVolunteerTeam.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglVolunteerTeam.java index 52004aa2..019bf1af 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglVolunteerTeam.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglVolunteerTeam.java @@ -117,6 +117,11 @@ public class StglVolunteerTeam extends TenantEntity { */ private String advisorEmail; + /** + * 指导老师JSON数据,支持多个指导老师 + */ + private String advisorsJson; + /** * 人员上限 */ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClassBo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClassBo.java new file mode 100644 index 00000000..89df987d --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClassBo.java @@ -0,0 +1,74 @@ +package org.dromara.stgl.domain.bo; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.stgl.domain.StglClass; + +/** + * 班级学生社团、志愿者队伍、活动参加情况业务对象 stgl_class + * + * @author Cursor AI + * @date 2025-01-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = StglClass.class, reverseConvertGenerate = false) +public class StglClassBo extends BaseEntity { + + /** + * 主键ID + */ + private Long id; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String name; + + /** + * 性别 + */ + private String gender; + + /** + * 联系电话 + */ + private String phone; + + /** + * 班级ID + */ + private String classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 年级 + */ + private String grade; + + /** + * 学籍状态 + */ + private String studentStatus; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglVolunteerTeamBo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglVolunteerTeamBo.java index 83d66b1c..5c02aa64 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglVolunteerTeamBo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglVolunteerTeamBo.java @@ -138,6 +138,11 @@ public class StglVolunteerTeamBo extends BaseEntity { @Size(max = 100, message = "指导老师邮箱长度不能超过100个字符") private String advisorEmail; + /** + * 指导老师JSON数据,支持多个指导老师 + */ + private String advisorsJson; + /** * 人员上限 */ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/ActivityRatingVo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/ActivityRatingVo.java new file mode 100644 index 00000000..39a4cf03 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/ActivityRatingVo.java @@ -0,0 +1,101 @@ +package org.dromara.stgl.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.stgl.domain.StglActivityRating; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 活动评分详细信息视图对象 + * + * @author ruoyi + * @date 2025-01-07 + */ +@Data +@AutoMapper(target = StglActivityRating.class) +public class ActivityRatingVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 评分ID + */ + private Long id; + + /** + * 活动ID + */ + private Long activityId; + + /** + * 评分人ID + */ + private Long userId; + + /** + * 评分人姓名 + */ + private String userName; + + /** + * 学号 + */ + private String studentNo; + + /** + * 联系方式 + */ + private String phoneNumber; + + /** + * 院系名称 + */ + private String deptName; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级名称 + */ + private String className; + + /** + * 总评分(1-5分) + */ + private BigDecimal ratingScore; + + /** + * 内容评分(1-5分) + */ + private Integer contentScore; + + /** + * 组织评分(1-5分) + */ + private Integer organizationScore; + + /** + * 体验评分(1-5分) + */ + private Integer experienceScore; + + /** + * 评分备注 + */ + private String ratingComment; + + /** + * 评分时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime ratingTime; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyActivityVo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyActivityVo.java index 25e37e5f..82ac300e 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyActivityVo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyActivityVo.java @@ -7,6 +7,7 @@ import org.dromara.stgl.domain.StglActivity; import java.io.Serial; import java.io.Serializable; +import java.math.BigDecimal; import java.time.LocalDateTime; /** @@ -110,15 +111,20 @@ public class MyActivityVo implements Serializable { */ private String myRegistrationStatus; + /** + * 报名类型(1-个人 2-团队) + */ + private String registrationType; + /** * 我的评分状态(0-未评分 1-已评分) */ private Integer myRatingStatus; /** - * 我的综合评分 + * 我的综合评分(保留1位小数) */ - private Integer myRatingScore; + private BigDecimal myRatingScore; /** * 我的评分时间 diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyVolunteerTeamVo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyVolunteerTeamVo.java index c900f1a6..78a72e92 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyVolunteerTeamVo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/MyVolunteerTeamVo.java @@ -52,6 +52,11 @@ public class MyVolunteerTeamVo implements Serializable { */ private String advisorDept; + /** + * 指导老师JSON数据,支持多个指导老师 + */ + private String advisorsJson; + /** * 当前成员数量 */ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClassVo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClassVo.java new file mode 100644 index 00000000..4ee0a398 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClassVo.java @@ -0,0 +1,104 @@ +package org.dromara.stgl.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.stgl.domain.StglClass; + +import java.io.Serializable; +import java.util.Date; + +/** + * 班级学生社团、志愿者队伍、活动参加情况视图对象 stgl_class + * + * @author Cursor AI + * @date 2025-01-15 + */ +@Data +@AutoMapper(target = StglClass.class) +public class StglClassVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + private Long id; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String studentName; + + /** + * 性别 + */ + private String gender; + + /** + * 联系电话 + */ + private String phone; + + /** + * 班级名称 + */ + private String className; + + /** + * 加入社团数量 + */ + private Integer clubCount; + + /** + * 加入志愿者队伍数量 + */ + private Integer volunteerTeamCount; + + /** + * 参与活动数量 + */ + private Integer activityCount; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + private Long updateBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 版本号 + */ + private Long version; + + /** + * 逻辑删除 + */ + private Long deleted; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglVolunteerTeamVo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglVolunteerTeamVo.java index 77126384..d212a4d5 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglVolunteerTeamVo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglVolunteerTeamVo.java @@ -137,6 +137,11 @@ public class StglVolunteerTeamVo implements Serializable { @ExcelProperty(value = "指导老师邮箱") private String advisorEmail; + /** + * 指导老师JSON数据,支持多个指导老师 + */ + private String advisorsJson; + /** * 人员上限 */ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/listener/StglVolunteerImportListener.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/listener/StglVolunteerImportListener.java index cec1cee1..a2b24a93 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/listener/StglVolunteerImportListener.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/listener/StglVolunteerImportListener.java @@ -238,7 +238,7 @@ public class StglVolunteerImportListener extends AnalysisEventListener selectActivityRatingsWithUserInfo(@Param("activityId") Long activityId); + } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglActivityTeamInfoMapper.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglActivityTeamInfoMapper.java index 0e6314b6..1825d656 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglActivityTeamInfoMapper.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglActivityTeamInfoMapper.java @@ -1,9 +1,13 @@ package org.dromara.stgl.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; import org.dromara.stgl.domain.StglActivityTeamInfo; import org.dromara.stgl.domain.vo.StglActivityTeamInfoVo; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; /** * 活动团队信息Mapper接口 @@ -13,4 +17,19 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; */ public interface StglActivityTeamInfoMapper extends BaseMapperPlus { + /** + * 根据条件查询团队信息列表(VO对象) + * + * @param queryWrapper 查询条件 + * @return 团队信息列表 + */ + List selectVoList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据活动ID查询团队列表 + * + * @param activityId 活动ID + * @return 团队列表 + */ + List selectVoListByActivityId(@Param("activityId") Long activityId); } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java new file mode 100644 index 00000000..7e96245a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java @@ -0,0 +1,65 @@ +package org.dromara.stgl.mapper; + +import org.dromara.stgl.domain.StglClass; +import org.dromara.stgl.domain.vo.StglClassVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; +import java.util.Map; + +/** + * 班级学生社团、志愿者队伍、活动参加情况Mapper接口 + * + * @author Cursor AI + * @date 2025-01-15 + */ +public interface StglClassMapper extends BaseMapperPlus { + + /** + * 查询班主任的班级列表 + * + * @param userId 用户ID + * @return 班级列表 + */ + List> selectTeacherClasses(Long userId); + + /** + * 查询班级学生列表(包含社团、志愿者队伍、活动统计) + * + * @param params 查询参数 + * @return 学生列表 + */ + List selectClassStudents(Map params); + + /** + * 查询班级学生总数 + * + * @param params 查询参数 + * @return 学生总数 + */ + Integer selectClassStudentsCount(Map params); + + /** + * 查询学生社团详情 + * + * @param studentId 学生ID + * @return 社团详情列表 + */ + List> selectStudentClubs(Long studentId); + + /** + * 查询学生志愿者队伍详情 + * + * @param studentId 学生ID + * @return 志愿者队伍详情列表 + */ + List> selectStudentVolunteerTeams(Long studentId); + + /** + * 查询学生活动详情 + * + * @param studentId 学生ID + * @return 活动详情列表 + */ + List> selectStudentActivities(Long studentId); +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMyMapper.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMyMapper.java index 1a1a9e4c..1ae29933 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMyMapper.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMyMapper.java @@ -40,9 +40,14 @@ public interface StglMyMapper { * * @param userId 用户ID * @param tenantId 租户ID + * @param activityName 活动名称 + * @param activityType 活动类型 + * @param isRated 是否评分 * @return 活动列表 */ - List selectMyActivities(@Param("userId") Long userId, @Param("tenantId") String tenantId); + List selectMyActivities(@Param("userId") Long userId, @Param("tenantId") String tenantId, + @Param("activityName") String activityName, @Param("activityType") String activityType, + @Param("isRated") String isRated); /** * 分页查询我的报名记录(社团+志愿团队+活动) diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglActivityService.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglActivityService.java index 10f4ba44..1424d59d 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglActivityService.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglActivityService.java @@ -6,6 +6,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.stgl.domain.StglActivity; import org.dromara.stgl.domain.bo.StglActivityBo; import org.dromara.stgl.domain.vo.StglActivityVo; +import org.dromara.stgl.domain.vo.ActivityRatingVo; import java.util.Collection; import java.util.List; @@ -82,9 +83,11 @@ public interface IStglActivityService extends IService { * * @param sourceId 源活动ID * @param newActivityName 新活动名称 + * @param activityStatus 新活动状态 + * @param registrationStatus 新活动报名状态 * @return 是否成功 */ - Boolean copyActivity(Long sourceId, String newActivityName); + Boolean copyActivity(Long sourceId, String newActivityName, String activityStatus, Integer registrationStatus); /** * 查询社团选择器选项 @@ -112,4 +115,12 @@ public interface IStglActivityService extends IService { */ Boolean summarizeActivity(Long id, String summary, String achievements, String summaryAttachments, String achievementsAttachments); + /** + * 查询活动评分列表(包含评分人详细信息) + * + * @param activityId 活动ID + * @return 活动评分详细信息列表 + */ + List getActivityRatings(Long activityId); + } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java new file mode 100644 index 00000000..8b33c1f7 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java @@ -0,0 +1,68 @@ +package org.dromara.stgl.service; + +import org.dromara.stgl.domain.vo.StglClassVo; +import org.dromara.stgl.domain.bo.StglClassBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 班级学生社团、志愿者队伍、活动参加情况Service接口 + * + * @author Cursor AI + * @date 2025-01-15 + */ +public interface IStglClassService { + + /** + * 查询班主任的班级列表 + * + * @param userId 用户ID + * @return 班级列表 + */ + List> getTeacherClasses(Long userId); + + /** + * 查询班级学生社团、志愿者队伍、活动参加情况分页列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 分页结果 + */ + TableDataInfo queryPageList(StglClassBo bo, PageQuery pageQuery); + + /** + * 查询班级学生社团、志愿者队伍、活动参加情况列表 + * + * @param bo 查询条件 + * @return 列表结果 + */ + List queryList(StglClassBo bo); + + /** + * 查询学生社团详情 + * + * @param studentId 学生ID + * @return 社团详情列表 + */ + List> getStudentClubs(Long studentId); + + /** + * 查询学生志愿者队伍详情 + * + * @param studentId 学生ID + * @return 志愿者队伍详情列表 + */ + List> getStudentVolunteerTeams(Long studentId); + + /** + * 查询学生活动详情 + * + * @param studentId 学生ID + * @return 活动详情列表 + */ + List> getStudentActivities(Long studentId); +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMyService.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMyService.java index a87b11a9..c04bf3a2 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMyService.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMyService.java @@ -35,9 +35,12 @@ public interface IStglMyService { /** * 查询我报名的活动列表(带评分状态) * + * @param activityName 活动名称 + * @param activityType 活动类型 + * @param isRated 是否评分 * @return 活动列表 */ - List queryMyActivities(); + List queryMyActivities(String activityName, String activityType, String isRated); /** * 分页查询我的报名记录 diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityRegistrationServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityRegistrationServiceImpl.java index b206c161..c74ffaa5 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityRegistrationServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityRegistrationServiceImpl.java @@ -16,8 +16,10 @@ import org.dromara.stgl.domain.StglActivityTeamInfo; import org.dromara.stgl.domain.bo.StglActivityRegistrationBo; import org.dromara.stgl.domain.bo.StglActivityTeamInfoBo; import org.dromara.stgl.domain.vo.StglActivityRegistrationVo; +import org.dromara.stgl.domain.vo.StglActivityVo; import org.dromara.stgl.mapper.StglActivityRegistrationMapper; import org.dromara.stgl.service.IStglActivityRegistrationService; +import org.dromara.stgl.service.IStglActivityService; import org.dromara.stgl.service.IStglActivityTeamInfoService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -38,6 +40,7 @@ import java.util.List; public class StglActivityRegistrationServiceImpl extends ServiceImpl implements IStglActivityRegistrationService { private final StglActivityRegistrationMapper baseMapper; + private final IStglActivityService stglActivityService; private final IStglActivityTeamInfoService teamInfoService; /** @@ -192,9 +195,19 @@ public class StglActivityRegistrationServiceImpl extends ServiceImpl userAdvisorClubIds = getUserAdvisorClubIds(currentUserId); - if (userAdvisorClubIds.isEmpty()) { - // 如果不是任何社团的指导老师,设置一个不存在的ID,返回空结果 - if (bo.getParams() == null) { - bo.setParams(new HashMap<>()); - } - bo.getParams().put("dataScope", "AND sa.related_club_id = -1"); - log.info("应用数据权限:用户 {} 为指导老师角色,但不是任何社团的指导老师,返回空结果", currentUserId); - } else { - // 设置数据权限过滤条件 - 只能查看自己作为指导老师的社团的活动 - if (bo.getParams() == null) { - bo.setParams(new HashMap<>()); - } - String clubIdsStr = userAdvisorClubIds.stream() - .map(String::valueOf) - .collect(Collectors.joining(",")); - bo.getParams().put("dataScope", "AND sa.related_club_id IN (" + clubIdsStr + ")"); - log.info("应用数据权限:用户 {} 为指导老师角色,只能查看社团ID为 {} 的活动", currentUserId, clubIdsStr); + // 获取用户作为指导老师的志愿者队伍ID列表 + List userAdvisorTeamIds = getUserAdvisorTeamIds(currentUserId); + + // 获取用户作为指导老师的活动ID列表 + List userAdvisorActivityIds = getUserAdvisorActivityIds(currentUserId); + + // 构建数据权限过滤条件 + String dataScope = buildInstructorDataScope(userAdvisorClubIds, userAdvisorTeamIds, userAdvisorActivityIds); + + if (bo.getParams() == null) { + bo.setParams(new HashMap<>()); } + bo.getParams().put("dataScope", dataScope); + log.info("应用数据权限:用户 {} 为指导老师角色,数据权限条件: {}", currentUserId, dataScope); } } catch (Exception e) { @@ -634,4 +637,195 @@ public class StglActivityServiceImpl extends ServiceImpl getUserAdvisorTeamIds(Long userId) { + List teamIds = new ArrayList<>(); + + try { + // 从stgl_volunteer_team表的advisor_name字段查询 + List nameTeamIds = getNameAdvisorTeamIds(userId); + teamIds.addAll(nameTeamIds); + + // 从stgl_volunteer_team表的advisors_json字段查询 + List jsonTeamIds = getJsonAdvisorTeamIds(userId); + teamIds.addAll(jsonTeamIds); + + log.debug("用户 {} 作为指导老师的志愿者队伍ID列表: {}", userId, teamIds); + + } catch (Exception e) { + log.error("获取用户 {} 作为指导老师的志愿者队伍ID列表时发生异常: {}", userId, e.getMessage(), e); + } + + return teamIds; + } + + /** + * 从志愿者队伍表的advisor_name字段获取用户作为指导老师的志愿者队伍ID列表 + * + * @param userId 用户ID + * @return 志愿者队伍ID列表 + */ + private List getNameAdvisorTeamIds(Long userId) { + List teamIds = new ArrayList<>(); + + try { + // 先通过用户ID查询用户信息,获取用户姓名 + SysUserVo user = sysUserService.selectUserById(userId); + if (user != null && StringUtils.isNotBlank(user.getNickName())) { + // 查询志愿者队伍表中advisor_name字段匹配该用户姓名的记录 + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.select(StglVolunteerTeam::getId); + lqw.eq(StglVolunteerTeam::getAdvisorName, user.getNickName()); + + List teams = stglVolunteerTeamMapper.selectList(lqw); + teamIds = teams.stream() + .map(StglVolunteerTeam::getId) + .collect(Collectors.toList()); + } + + } catch (Exception e) { + log.error("从志愿者队伍表获取用户 {} 作为指导老师的志愿者队伍ID列表时发生异常: {}", userId, e.getMessage(), e); + } + + return teamIds; + } + + /** + * 从志愿者队伍表的advisors_json字段获取用户作为指导老师的志愿者队伍ID列表 + * + * @param userId 用户ID + * @return 志愿者队伍ID列表 + */ + private List getJsonAdvisorTeamIds(Long userId) { + List teamIds = new ArrayList<>(); + + try { + // 查询志愿者队伍表中advisors_json字段包含该用户的记录 + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.select(StglVolunteerTeam::getId); + lqw.isNotNull(StglVolunteerTeam::getAdvisorsJson); + lqw.ne(StglVolunteerTeam::getAdvisorsJson, ""); + lqw.like(StglVolunteerTeam::getAdvisorsJson, userId.toString()); + + List teams = stglVolunteerTeamMapper.selectList(lqw); + teamIds = teams.stream() + .map(StglVolunteerTeam::getId) + .collect(Collectors.toList()); + + } catch (Exception e) { + log.error("从志愿者队伍表获取用户 {} 作为指导老师的志愿者队伍ID列表时发生异常: {}", userId, e.getMessage(), e); + } + + return teamIds; + } + + /** + * 获取用户作为指导老师的活动ID列表 + * + * @param userId 用户ID + * @return 活动ID列表 + */ + private List getUserAdvisorActivityIds(Long userId) { + List activityIds = new ArrayList<>(); + + try { + // 从stgl_activity表的instructors字段查询 + List jsonActivityIds = getJsonAdvisorActivityIds(userId); + activityIds.addAll(jsonActivityIds); + + log.debug("用户 {} 作为指导老师的活动ID列表: {}", userId, activityIds); + + } catch (Exception e) { + log.error("获取用户 {} 作为指导老师的活动ID列表时发生异常: {}", userId, e.getMessage(), e); + } + + return activityIds; + } + + /** + * 从活动表的instructors字段获取用户作为指导老师的活动ID列表 + * + * @param userId 用户ID + * @return 活动ID列表 + */ + private List getJsonAdvisorActivityIds(Long userId) { + List activityIds = new ArrayList<>(); + + try { + // 查询活动表中instructors字段包含该用户的记录 + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.select(StglActivity::getId); + lqw.like(StglActivity::getInstructors, userId.toString()); + + List activities = baseMapper.selectList(lqw); + activityIds = activities.stream() + .map(StglActivity::getId) + .collect(Collectors.toList()); + + } catch (Exception e) { + log.error("从活动表获取用户 {} 作为指导老师的活动ID列表时发生异常: {}", userId, e.getMessage(), e); + } + + return activityIds; + } + + /** + * 构建指导老师数据权限过滤条件 + * + * @param userAdvisorClubIds 用户作为指导老师的社团ID列表 + * @param userAdvisorTeamIds 用户作为指导老师的志愿者队伍ID列表 + * @param userAdvisorActivityIds 用户作为指导老师的活动ID列表 + * @return 数据权限过滤条件 + */ + private String buildInstructorDataScope(List userAdvisorClubIds, List userAdvisorTeamIds, List userAdvisorActivityIds) { + List conditions = new ArrayList<>(); + + // 1. 是活动关联社团的指导老师 + if (!userAdvisorClubIds.isEmpty()) { + String clubIdsStr = userAdvisorClubIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + conditions.add("(sa.related_type = 1 AND sa.related_club_id IN (" + clubIdsStr + "))"); + } + + // 2. 是活动关联志愿者队伍的指导老师 + if (!userAdvisorTeamIds.isEmpty()) { + String teamIdsStr = userAdvisorTeamIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + conditions.add("(sa.related_type = 2 AND sa.related_club_id IN (" + teamIdsStr + "))"); + } + + // 3. 是活动本身的指导老师 + if (!userAdvisorActivityIds.isEmpty()) { + String activityIdsStr = userAdvisorActivityIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + conditions.add("sa.id IN (" + activityIdsStr + ")"); + } + + // 如果没有任何权限,返回一个永远为false的条件 + if (conditions.isEmpty()) { + return "AND 1 = 0"; + } + + // 组合所有条件,使用OR连接 + return "AND (" + String.join(" OR ", conditions) + ")"; + } + + @Override + public List getActivityRatings(Long activityId) { + if (activityId == null) { + return new ArrayList<>(); + } + + // 使用自定义SQL查询评分详情,关联用户表获取完整信息 + return stglActivityRatingMapper.selectActivityRatingsWithUserInfo(activityId); + } } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityTeamInfoServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityTeamInfoServiceImpl.java index 1fbd4b5e..d8521195 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityTeamInfoServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityTeamInfoServiceImpl.java @@ -118,7 +118,6 @@ public class StglActivityTeamInfoServiceImpl extends ServiceImpl queryByActivityId(Long activityId) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(StglActivityTeamInfo::getActivityId, activityId); - lqw.orderByDesc(StglActivityTeamInfo::getCreateTime); return baseMapper.selectVoList(lqw); } diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java new file mode 100644 index 00000000..34867143 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java @@ -0,0 +1,152 @@ +package org.dromara.stgl.service.impl; + +import org.dromara.stgl.domain.StglClass; +import org.dromara.stgl.domain.vo.StglClassVo; +import org.dromara.stgl.domain.bo.StglClassBo; +import org.dromara.stgl.mapper.StglClassMapper; +import org.dromara.stgl.service.IStglClassService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 班级学生社团、志愿者队伍、活动参加情况Service业务层处理 + * + * @author Cursor AI + * @date 2025-01-15 + */ +@RequiredArgsConstructor +@Service +public class StglClassServiceImpl implements IStglClassService { + + private final StglClassMapper baseMapper; + + /** + * 查询班主任的班级列表 + * + * @param userId 用户ID + * @return 班级列表 + */ + @Override + public List> getTeacherClasses(Long userId) { + return baseMapper.selectTeacherClasses(userId); + } + + /** + * 查询班级学生社团、志愿者队伍、活动参加情况分页列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 分页结果 + */ + @Override + public TableDataInfo queryPageList(StglClassBo bo, PageQuery pageQuery) { + // 构建查询参数 + Map params = buildQueryParams(bo); + params.put("offset", (pageQuery.getPageNum() - 1) * pageQuery.getPageSize()); + params.put("pageSize", pageQuery.getPageSize()); + + // 使用自定义SQL查询 + List list = baseMapper.selectClassStudents(params); + + // 获取总数 + Map countParams = buildQueryParams(bo); + Integer total = baseMapper.selectClassStudentsCount(countParams); + + TableDataInfo result = new TableDataInfo<>(); + result.setRows(list); + result.setTotal(total != null ? total : 0); + return result; + } + + /** + * 查询班级学生社团、志愿者队伍、活动参加情况列表 + * + * @param bo 查询条件 + * @return 列表结果 + */ + @Override + public List queryList(StglClassBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 查询学生社团详情 + * + * @param studentId 学生ID + * @return 社团详情列表 + */ + @Override + public List> getStudentClubs(Long studentId) { + return baseMapper.selectStudentClubs(studentId); + } + + /** + * 查询学生志愿者队伍详情 + * + * @param studentId 学生ID + * @return 志愿者队伍详情列表 + */ + @Override + public List> getStudentVolunteerTeams(Long studentId) { + return baseMapper.selectStudentVolunteerTeams(studentId); + } + + /** + * 查询学生活动详情 + * + * @param studentId 学生ID + * @return 活动详情列表 + */ + @Override + public List> getStudentActivities(Long studentId) { + return baseMapper.selectStudentActivities(studentId); + } + + /** + * 构建查询参数 + * + * @param bo 查询条件 + * @return 查询参数 + */ + private Map buildQueryParams(StglClassBo bo) { + Map params = bo.getParams(); + if (StringUtils.isNotBlank(bo.getClassId())) { + params.put("classId", bo.getClassId()); + } + if (StringUtils.isNotBlank(bo.getName())) { + params.put("studentName", bo.getName()); + } + if (StringUtils.isNotBlank(bo.getStudentNo())) { + params.put("studentNo", bo.getStudentNo()); + } + return params; + } + + /** + * 构建查询条件 + * + * @param bo 查询条件 + * @return 查询条件 + */ + private LambdaQueryWrapper buildQueryWrapper(StglClassBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getClassId()), StglClass::getClassId, bo.getClassId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), StglClass::getName, bo.getName()); + lqw.like(StringUtils.isNotBlank(bo.getStudentNo()), StglClass::getStudentNo, bo.getStudentNo()); + return lqw; + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java index 51c81645..58078359 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java @@ -25,6 +25,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; // 添加学生信息服务依赖 import org.dromara.xg.domain.XgStudentMsg; import org.dromara.xg.mapper.XgStudentMsgMapper; +// 添加社长角色分配相关依赖 +import org.dromara.system.domain.SysUserRole; +import org.dromara.system.mapper.SysUserRoleMapper; import java.util.stream.Collectors; import java.util.Collections; import org.dromara.common.satoken.utils.LoginHelper; @@ -56,6 +59,9 @@ import java.util.Set; public class StglClubServiceImpl implements IStglClubService { private static final Logger log = LoggerFactory.getLogger(StglClubServiceImpl.class); + + // 社长角色ID常量 + private static final Long CLUB_PRESIDENT_ROLE_ID = 1957336212742184962L; private final StglClubMapper baseMapper; private final StglMemberMapper memberMapper; @@ -63,6 +69,8 @@ public class StglClubServiceImpl implements IStglClubService { private final XgStudentMsgMapper studentMsgMapper; // 添加指导老师角色分配工具 private final AdvisorRoleAssigner advisorRoleAssigner; + // 添加用户角色Mapper + private final SysUserRoleMapper sysUserRoleMapper; /** * 查询社团信息 @@ -332,6 +340,8 @@ public class StglClubServiceImpl implements IStglClubService { createLeaderMemberRecords(add); // 为指导老师分配角色 assignAdvisorRoles(add); + // 为社长分配角色 + assignPresidentRole(add); } return flag; } @@ -1095,6 +1105,23 @@ public class StglClubServiceImpl implements IStglClubService { memberMapper.insert(newMember); } + // 8. 处理社长角色分配:移除原社长角色,为新社长分配角色 + try { + // 8.1 移除原社长的角色(根据社团信息获取原社长的用户ID) + Long oldPresidentUserId = findUserIdByStudentNo(club.getPresidentStudentNo()); + if (oldPresidentUserId != null) { + removePresidentRole(oldPresidentUserId); + log.info("社团转让:成功移除原社长角色,用户ID: {}, 社团: {}", oldPresidentUserId, club.getClubName()); + } + + // 8.2 为新社长分配角色 + assignPresidentRoleByUserId(newPresidentUserId, club.getClubName()); + log.info("社团转让:成功为新社长分配角色,用户ID: {}, 社团: {}", newPresidentUserId, club.getClubName()); + } catch (Exception e) { + log.error("社团转让时角色分配出现异常,社团: {}", club.getClubName(), e); + // 注意:这里不抛出异常,避免影响转让主流程 + } + return true; } @@ -1207,5 +1234,104 @@ public class StglClubServiceImpl implements IStglClubService { return advisorUserIds; } + + /** + * 为社长分配角色 + * + * @param club 社团信息 + */ + private void assignPresidentRole(StglClub club) { + try { + if (StringUtils.isNotBlank(club.getPresidentStudentNo())) { + // 根据学号查找用户ID + Long presidentUserId = findUserIdByStudentNo(club.getPresidentStudentNo()); + if (presidentUserId != null) { + assignPresidentRoleByUserId(presidentUserId, club.getClubName()); + } else { + log.warn("社团 {} 的社长学号 {} 对应的用户不存在,跳过角色分配", + club.getClubName(), club.getPresidentStudentNo()); + } + } else { + log.warn("社团 {} 的社长学号为空,跳过角色分配", club.getClubName()); + } + } catch (Exception e) { + log.error("为社团 {} 的社长分配角色时发生异常", club.getClubName(), e); + // 不抛出异常,避免影响主业务流程 + } + } + + /** + * 根据用户ID为社长分配角色 + * + * @param presidentUserId 社长用户ID + * @param clubName 社团名称 + */ + private void assignPresidentRoleByUserId(Long presidentUserId, String clubName) { + try { + // 检查用户是否已有该角色 + if (!hasUserRole(presidentUserId, CLUB_PRESIDENT_ROLE_ID)) { + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(presidentUserId); + userRole.setRoleId(CLUB_PRESIDENT_ROLE_ID); + + int result = sysUserRoleMapper.insert(userRole); + if (result > 0) { + log.info("成功为社团 {} 的社长分配角色,社长用户ID: {}, 角色ID: {}", + clubName, presidentUserId, CLUB_PRESIDENT_ROLE_ID); + } else { + log.error("为社团 {} 的社长分配角色失败,社长用户ID: {}", clubName, presidentUserId); + } + } else { + log.debug("社团 {} 的社长已有该角色,跳过分配", clubName); + } + } catch (Exception e) { + log.error("为社团 {} 的社长分配角色时发生异常,社长用户ID: {}", clubName, presidentUserId, e); + throw new RuntimeException("分配社长角色失败", e); + } + } + + /** + * 移除社长角色 + * + * @param presidentUserId 社长用户ID + */ + private void removePresidentRole(Long presidentUserId) { + try { + int result = sysUserRoleMapper.delete( + Wrappers.lambdaQuery() + .eq(SysUserRole::getUserId, presidentUserId) + .eq(SysUserRole::getRoleId, CLUB_PRESIDENT_ROLE_ID) + ); + + if (result > 0) { + log.info("成功移除用户 {} 的社长角色", presidentUserId); + } else { + log.debug("用户 {} 没有社长角色需要移除", presidentUserId); + } + } catch (Exception e) { + log.error("移除用户 {} 的社长角色时发生异常", presidentUserId, e); + throw new RuntimeException("移除社长角色失败", e); + } + } + + /** + * 检查用户是否已有指定角色 + * + * @param userId 用户ID + * @param roleId 角色ID + * @return 是否已有该角色 + */ + private boolean hasUserRole(Long userId, Long roleId) { + try { + LambdaQueryWrapper query = Wrappers.lambdaQuery(); + query.eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getRoleId, roleId); + return sysUserRoleMapper.selectCount(query) > 0; + } catch (Exception e) { + log.error("检查用户角色关系时发生异常,用户ID: {}, 角色ID: {}", userId, roleId, e); + // 如果查询失败,假设用户没有该角色,继续分配 + return false; + } + } } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java index 3ef76506..1be2392c 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java @@ -28,6 +28,7 @@ import java.util.Date; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; +import java.util.HashSet; import java.util.Set; /** @@ -150,13 +151,16 @@ public class StglMemberServiceImpl implements IStglMemberService { // 处理状态查询:优先使用statusList多状态查询,如果没有则使用单个status if (params != null && params.get("statusList") != null) { Object statusList = params.get("statusList"); + log.info("StglMemberServiceImpl - statusList参数: {}, 类型: {}", statusList, statusList.getClass()); if (statusList instanceof List) { List statuses = (List) statusList; if (!statuses.isEmpty()) { + log.info("StglMemberServiceImpl - 使用List类型statusList: {}", statuses); lqw.in(StglMember::getStatus, statuses); } } else if (statusList instanceof String) { String statusStr = (String) statusList; + log.info("StglMemberServiceImpl - 使用String类型statusList: {}", statusStr); if (statusStr.contains(",")) { // 处理 "0,3" 格式的字符串 String[] statuses = statusStr.split(","); @@ -164,15 +168,20 @@ public class StglMemberServiceImpl implements IStglMemberService { for (String status : statuses) { statusList2.add(status.trim()); } + log.info("StglMemberServiceImpl - 解析后的statusList: {}", statusList2); lqw.in(StglMember::getStatus, statusList2); } else { // 单个状态值 + log.info("StglMemberServiceImpl - 单个状态值: {}", statusStr); lqw.eq(StglMember::getStatus, statusStr); } } } else if (bo.getStatus() != null) { // 如果没有statusList,则使用单个status字段 + log.info("StglMemberServiceImpl - 使用单个status: {}", bo.getStatus()); lqw.eq(StglMember::getStatus, bo.getStatus()); + } else { + log.info("StglMemberServiceImpl - 无状态过滤条件"); } lqw.between(params.get("beginJoinTime") != null && params.get("endJoinTime") != null, @@ -238,7 +247,14 @@ public class StglMemberServiceImpl implements IStglMemberService { entity.setStatus(2); // 默认为已加入状态(stgl_member_status字典) } if (entity.getRoleType() == null) { - entity.setRoleType("3"); // 默认为普通成员 + // 根据成员类型设置不同的默认角色 + if (entity.getMemberType() != null && entity.getMemberType() == 2) { + // 志愿者成员默认角色为"队员" + entity.setRoleType("队员"); + } else { + // 社团成员默认角色为"3"(普通成员) + entity.setRoleType("3"); + } } } @@ -319,7 +335,16 @@ public class StglMemberServiceImpl implements IStglMemberService { member.setMemberType(memberType); member.setRelationId(relationId); member.setUserId(userId); - member.setRoleType(roleType != null ? roleType.toString() : "3"); + // 根据成员类型设置不同的默认角色 + if (roleType != null) { + member.setRoleType(roleType.toString()); + } else if (memberType == 2) { + // 志愿者成员默认角色为"队员" + member.setRoleType("队员"); + } else { + // 社团成员默认角色为"3" + member.setRoleType("3"); + } member.setJoinTime(now); member.setStatus(2); // 已加入状态(stgl_member_status字典) return member; @@ -425,7 +450,16 @@ public class StglMemberServiceImpl implements IStglMemberService { memberBo.setStudentNo(memberVo.getStudentNo()); memberBo.setStudentName(memberVo.getStudentName()); memberBo.setRelationId(memberVo.getRelationId()); - memberBo.setRoleType(memberVo.getRoleType() != null ? memberVo.getRoleType() : "1"); // 默认普通成员 + // 根据成员类型设置不同的默认角色 + if (memberVo.getRoleType() != null) { + memberBo.setRoleType(memberVo.getRoleType()); + } else if (memberType == 2) { + // 志愿者成员默认角色为"队员" + memberBo.setRoleType("队员"); + } else { + // 社团成员默认角色为"1" + memberBo.setRoleType("1"); + } memberBo.setStatus(memberVo.getStatus() != null ? memberVo.getStatus() : "1"); // 默认正常状态 memberBo.setJoinTime(memberVo.getJoinTime() != null ? memberVo.getJoinTime() : new Date()); @@ -584,22 +618,30 @@ public class StglMemberServiceImpl implements IStglMemberService { return bo; // 不做任何过滤 } - // 如果是志愿者队长,只能查看自己担任队长的队伍成员 + // 如果是志愿者队长,可以查看自己担任队长的队伍成员,或者自己所在的队伍成员 if (roles.contains("stgl_volunteer_captain")) { // 查询用户担任队长的队伍ID列表 List captainTeamIds = getCaptainTeamIds(currentUserId); - if (captainTeamIds.isEmpty()) { - // 如果用户不是任何队伍的队长,返回空结果 + // 查询用户作为成员所在的志愿者队伍ID列表 + List memberTeamIds = getMemberTeamIds(currentUserId); + + // 合并队长和成员的队伍ID列表 + Set accessibleTeamIds = new HashSet<>(); + accessibleTeamIds.addAll(captainTeamIds); + accessibleTeamIds.addAll(memberTeamIds); + + if (accessibleTeamIds.isEmpty()) { + // 如果用户不在任何志愿者队伍中,返回空结果 bo.setRelationId(-1L); // 设置一个不存在的关联ID } else if (bo.getRelationId() == null) { - // 如果没有指定特定队伍,限制查询范围为该用户担任队长的队伍 + // 如果没有指定特定队伍,限制查询范围为该用户有权限访问的队伍 // 由于查询接口限制,我们只能返回第一个队伍的数据 // 在实际应用中,前端应该明确指定要查询的队伍ID - bo.setRelationId(captainTeamIds.get(0)); + bo.setRelationId(accessibleTeamIds.iterator().next()); } else { // 如果指定了队伍ID,验证用户是否有权限访问该队伍 - if (!captainTeamIds.contains(bo.getRelationId())) { + if (!accessibleTeamIds.contains(bo.getRelationId())) { bo.setRelationId(-1L); // 设置一个不存在的关联ID } } @@ -628,7 +670,7 @@ public class StglMemberServiceImpl implements IStglMemberService { // 查询用户作为队长的志愿者队伍 StglMemberBo captainBo = new StglMemberBo(); captainBo.setMemberType(2); // 志愿者成员 - captainBo.setRoleType("0"); // 队长角色 + captainBo.setRoleType("队长"); // 队长角色 List captainRecords = baseMapper.selectMemberWithUserInfo( captainBo.getMemberType(), @@ -654,4 +696,37 @@ public class StglMemberServiceImpl implements IStglMemberService { } } + /** + * 获取用户作为成员所在的队伍ID列表 + */ + private List getMemberTeamIds(Long userId) { + try { + // 查询用户作为成员的志愿者队伍 + StglMemberBo memberBo = new StglMemberBo(); + memberBo.setMemberType(2); // 志愿者成员 + + List memberRecords = baseMapper.selectMemberWithUserInfo( + memberBo.getMemberType(), + null, // relationId 不限制 + null, // studentNo + null, // studentName + null, // roleType 不限制角色 + null, // status + null, // clubName + null, // teamName + userId, // userId 权限过滤 + null // params + ); + + return memberRecords.stream() + .map(StglMemberVo::getRelationId) + .filter(relationId -> relationId != null) + .distinct() + .collect(Collectors.toList()); + + } catch (Exception e) { + return new ArrayList<>(); + } + } + } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMyServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMyServiceImpl.java index 8d114479..592c1bae 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMyServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMyServiceImpl.java @@ -20,6 +20,8 @@ import org.dromara.system.service.ISysUserService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDateTime; import java.util.List; @@ -60,10 +62,10 @@ public class StglMyServiceImpl implements IStglMyService { * 查询我报名的活动列表(带评分状态) */ @Override - public List queryMyActivities() { + public List queryMyActivities(String activityName, String activityType, String isRated) { Long userId = LoginHelper.getUserId(); String tenantId = LoginHelper.getTenantId(); - return stglMyMapper.selectMyActivities(userId, tenantId); + return stglMyMapper.selectMyActivities(userId, tenantId, activityName, activityType, isRated); } /** @@ -106,9 +108,10 @@ public class StglMyServiceImpl implements IStglMyService { rating.setStudentNo(userInfo.getUserName()); // 使用用户名作为学号 rating.setStudentName(userInfo.getNickName()); - // 计算综合评分(三项评分平均值) - double avgScore = (ratingBo.getContentScore() + ratingBo.getOrganizationScore() + ratingBo.getExperienceScore()) / 3.0; - rating.setRatingScore((int) Math.round(avgScore)); + // 计算综合评分(三项评分平均值,保留1位小数) + BigDecimal avgScore = BigDecimal.valueOf(ratingBo.getContentScore() + ratingBo.getOrganizationScore() + ratingBo.getExperienceScore()) + .divide(BigDecimal.valueOf(3), 1, RoundingMode.HALF_UP); + rating.setRatingScore(avgScore); rating.setRatingTime(LocalDateTime.now()); return stglActivityRatingMapper.insert(rating) > 0; @@ -123,7 +126,7 @@ public class StglMyServiceImpl implements IStglMyService { String tenantId = LoginHelper.getTenantId(); // 查询用户的活动信息 - List activities = stglMyMapper.selectMyActivities(userId, tenantId); + List activities = stglMyMapper.selectMyActivities(userId, tenantId, null, null, null); // 查找指定的活动 return activities.stream() diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglVolunteerTeamServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglVolunteerTeamServiceImpl.java index abe764c5..70fe91c0 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglVolunteerTeamServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglVolunteerTeamServiceImpl.java @@ -22,8 +22,10 @@ import org.dromara.xg.mapper.XgStudentMsgMapper; import org.dromara.xg.domain.vo.XgStudentMsgVo; import org.dromara.xg.domain.XgStudentMsg; import org.dromara.system.mapper.SysUserMapper; +import org.dromara.system.mapper.SysUserRoleMapper; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.SysUserRole; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.stgl.utils.AdvisorRoleAssigner; import cn.dev33.satoken.stp.StpUtil; @@ -35,6 +37,11 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Objects; +import java.util.Map; +import java.util.ArrayList; +import java.util.stream.Collectors; +import com.fasterxml.jackson.core.type.TypeReference; +import org.dromara.common.json.utils.JsonUtils; /** * 志愿者队伍Service业务层处理 @@ -51,8 +58,19 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { private final StglMemberMapper memberMapper; private final XgStudentMsgMapper studentMsgMapper; private final SysUserMapper userMapper; + private final SysUserRoleMapper sysUserRoleMapper; private final AdvisorRoleAssigner advisorRoleAssigner; + /** + * 志愿者队长角色ID + */ + private static final Long VOLUNTEER_CAPTAIN_ROLE_ID = 1957353206166118402L; + + /** + * 指导老师角色ID + */ + private static final Long ADVISOR_ROLE_ID = 1959880215953952769L; + /** * 查询志愿者队伍 */ @@ -153,6 +171,8 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { bo.setId(add.getId()); // 自动创建队长的成员记录 createLeaderMemberRecord(add); + // 为队长分配角色 + assignLeaderRole(add); // 为指导老师分配角色 assignAdvisorRole(add); } @@ -168,7 +188,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { StglVolunteerTeam update = MapstructUtils.convert(bo, StglVolunteerTeam.class); validEntityBeforeSave(update); - // 获取更新前的队伍信息,用于比较指导老师变化 + // 获取更新前的队伍信息,用于比较变化 StglVolunteerTeam oldTeam = null; if (update.getId() != null) { oldTeam = baseMapper.selectById(update.getId()); @@ -180,6 +200,8 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { if (updateResult) { // 同步更新队长的成员记录 syncLeaderInMembers(update); + // 处理队长角色分配 + handleLeaderRoleAssignment(oldTeam, update); // 处理指导老师角色分配 handleAdvisorRoleAssignment(oldTeam, update); } @@ -318,7 +340,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { query.eq(StglMember::getMemberType, 2); // 志愿者成员 query.eq(StglMember::getRelationId, teamId); // 指定队伍 query.eq(StglMember::getStudentNo, studentNo); // 指定学号 - query.eq(StglMember::getRoleType, "0"); // 队长角色 + query.eq(StglMember::getRoleType, "队长"); // 队长角色 query.eq(StglMember::getStatus, 2); // 已加入状态 return memberMapper.selectCount(query) > 0; @@ -448,7 +470,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { member.setUserId(LoginHelper.getUserId()); // 设置当前登录用户ID member.setStudentName(applicationBo.getStudentName()); member.setStudentNo(applicationBo.getStudentNo()); - member.setRoleType("1"); // 1-队员(志愿者角色) + member.setRoleType("队员"); // 队员角色 - 直接写入字符"队员" member.setStatus(0); // 0-待审核 member.setJoinTime(new Date()); @@ -555,7 +577,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { leader.setUserId(team.getLeaderUserId()); leader.setStudentNo(team.getLeaderStudentNo()); leader.setStudentName(team.getLeaderName()); - leader.setRoleType("0"); // 队长 (stgl_volunteer_role=0) + leader.setRoleType("队长"); // 队长角色 - 直接写入字符"队长" leader.setJoinTime(team.getCreateTime() != null ? team.getCreateTime() : new Date()); leader.setStatus(2); // 已加入状态 leader.setRemark("补充缺失的队长成员记录"); @@ -599,6 +621,9 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { throw new RuntimeException("志愿者队伍不存在"); } + // 保存原队长用户ID,用于角色分配处理 + Long oldLeaderUserId = team.getLeaderUserId(); + // 2. 获取新队长的学生信息 XgStudentMsg newLeaderStudent = studentMsgMapper.selectOne( Wrappers.lambdaQuery() @@ -633,23 +658,43 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { throw new RuntimeException("更新队伍信息失败"); } - // 5. 更新成员表 + // 5. 处理角色分配:移除原队长角色,为新队长分配角色 + try { + // 5.1 移除原队长的角色 + if (oldLeaderUserId != null) { + removeLeaderRole(oldLeaderUserId); + log.info("志愿者队伍转让:成功移除原队长角色,用户ID: {}, 队伍: {}", oldLeaderUserId, team.getTeamName()); + } + + // 5.2 为新队长分配角色 + StglVolunteerTeam updatedTeam = new StglVolunteerTeam(); + updatedTeam.setId(teamId); + updatedTeam.setTeamName(team.getTeamName()); + updatedTeam.setLeaderUserId(newLeaderUserId); + assignLeaderRole(updatedTeam); + log.info("志愿者队伍转让:成功为新队长分配角色,用户ID: {}, 队伍: {}", newLeaderUserId, team.getTeamName()); + } catch (Exception e) { + log.error("志愿者队伍转让时角色分配出现异常,队伍: {}", team.getTeamName(), e); + // 注意:这里不抛出异常,避免影响转让主流程 + } + + // 6. 更新成员表 Long currentUserId = LoginHelper.getUserId(); - // 5.1 查询原队长在成员表中的记录 + // 6.1 查询原队长在成员表中的记录 StglMember oldLeaderMember = null; if (currentUserId != null) { oldLeaderMember = memberMapper.selectOne( Wrappers.lambdaQuery() .eq(StglMember::getMemberType, 2) // 志愿者队伍成员 .eq(StglMember::getRelationId, teamId) - .eq(StglMember::getRoleType, 0) // 队长角色 + .eq(StglMember::getRoleType, "队长") // 队长角色 - 直接使用字符"队长" .eq(StglMember::getUserId, currentUserId) // 添加当前用户ID条件 .eq(StglMember::getStatus, 2) // 已加入状态 ); } - // 5.2 查询新队长在成员表中的记录 + // 6.2 查询新队长在成员表中的记录 StglMember newLeaderMember = memberMapper.selectOne( Wrappers.lambdaQuery() .eq(StglMember::getMemberType, 2) // 志愿者队伍成员 @@ -657,7 +702,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { .eq(StglMember::getUserId, newLeaderUserId) ); - // 5.3 更新原队长的成员状态为已退出 + // 6.3 更新原队长的成员状态为已退出 if (oldLeaderMember != null) { StglMember updateOldLeader = new StglMember(); updateOldLeader.setId(oldLeaderMember.getId()); @@ -667,12 +712,12 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { memberMapper.updateById(updateOldLeader); } - // 5.4 添加或更新新队长的成员记录 + // 6.4 添加或更新新队长的成员记录 if (newLeaderMember != null) { // 如果新队长已经是成员,更新角色和状态 StglMember updateNewLeader = new StglMember(); updateNewLeader.setId(newLeaderMember.getId()); - updateNewLeader.setRoleType("0"); // 队长角色 + updateNewLeader.setRoleType("队长"); // 队长角色 - 直接写入字符"队长" updateNewLeader.setStatus(2); // 已加入状态 updateNewLeader.setJoinTime(new Date()); updateNewLeader.setUpdateBy(LoginHelper.getUserId()); @@ -690,7 +735,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { newMember.setUserId(newLeaderUserId); newMember.setStudentNo(newLeaderStudent.getStudentNo()); newMember.setStudentName(newLeaderStudent.getName()); - newMember.setRoleType("0"); // 队长角色 + newMember.setRoleType("队长"); // 队长角色 - 直接写入字符"队长" newMember.setJoinTime(new Date()); newMember.setStatus(2); // 已加入状态 newMember.setCreateBy(LoginHelper.getUserId()); @@ -854,10 +899,12 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { */ private void assignAdvisorRole(StglVolunteerTeam team) { try { - Long advisorUserId = extractAdvisorUserId(team); - if (advisorUserId != null) { - advisorRoleAssigner.assignAdvisorRole(advisorUserId); - log.info("成功为志愿者队伍 {} 的指导老师分配角色,指导老师用户ID: {}", team.getTeamName(), advisorUserId); + List advisorUserIds = extractAdvisorUserIds(team); + for (Long advisorUserId : advisorUserIds) { + if (advisorUserId != null) { + advisorRoleAssigner.assignAdvisorRole(advisorUserId); + log.info("成功为志愿者队伍 {} 的指导老师分配角色,指导老师用户ID: {}", team.getTeamName(), advisorUserId); + } } } catch (Exception e) { log.error("为志愿者队伍 {} 的指导老师分配角色时发生异常", team.getTeamName(), e); @@ -873,23 +920,30 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { */ private void handleAdvisorRoleAssignment(StglVolunteerTeam oldTeam, StglVolunteerTeam newTeam) { try { - // 获取更新前后的指导老师用户ID - Long oldAdvisorUserId = oldTeam != null ? extractAdvisorUserId(oldTeam) : null; - Long newAdvisorUserId = extractAdvisorUserId(newTeam); + // 获取更新前后的指导老师用户ID列表 + List oldAdvisorUserIds = oldTeam != null ? extractAdvisorUserIds(oldTeam) : new ArrayList<>(); + List newAdvisorUserIds = extractAdvisorUserIds(newTeam); - // 如果指导老师发生变化 - if (!Objects.equals(oldAdvisorUserId, newAdvisorUserId)) { - // 移除旧指导老师的角色 - if (oldAdvisorUserId != null) { - advisorRoleAssigner.removeAdvisorRole(oldAdvisorUserId); - log.info("志愿者队伍 {} 移除指导老师,成功移除角色,用户ID: {}", newTeam.getTeamName(), oldAdvisorUserId); - } + // 找出需要移除角色的指导老师 + List removedAdvisorUserIds = oldAdvisorUserIds.stream() + .filter(oldId -> !newAdvisorUserIds.contains(oldId)) + .collect(Collectors.toList()); - // 为新指导老师分配角色 - if (newAdvisorUserId != null) { - advisorRoleAssigner.assignAdvisorRole(newAdvisorUserId); - log.info("志愿者队伍 {} 新增指导老师,成功分配角色,用户ID: {}", newTeam.getTeamName(), newAdvisorUserId); - } + // 找出需要分配角色的指导老师 + List addedAdvisorUserIds = newAdvisorUserIds.stream() + .filter(newId -> !oldAdvisorUserIds.contains(newId)) + .collect(Collectors.toList()); + + // 移除旧指导老师的角色 + for (Long advisorUserId : removedAdvisorUserIds) { + advisorRoleAssigner.removeAdvisorRole(advisorUserId); + log.info("志愿者队伍 {} 移除指导老师,成功移除角色,用户ID: {}", newTeam.getTeamName(), advisorUserId); + } + + // 为新指导老师分配角色 + for (Long advisorUserId : addedAdvisorUserIds) { + advisorRoleAssigner.assignAdvisorRole(advisorUserId); + log.info("志愿者队伍 {} 新增指导老师,成功分配角色,用户ID: {}", newTeam.getTeamName(), advisorUserId); } } catch (Exception e) { log.error("处理志愿者队伍 {} 的指导老师角色分配时发生异常", newTeam.getTeamName(), e); @@ -897,26 +951,175 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService { } } + /** + * 为队长分配角色 + * + * @param team 志愿者队伍信息 + */ + private void assignLeaderRole(StglVolunteerTeam team) { + try { + if (team.getLeaderUserId() != null) { + // 检查用户是否已有该角色 + if (!hasUserRole(team.getLeaderUserId(), VOLUNTEER_CAPTAIN_ROLE_ID)) { + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(team.getLeaderUserId()); + userRole.setRoleId(VOLUNTEER_CAPTAIN_ROLE_ID); + + int result = sysUserRoleMapper.insert(userRole); + if (result > 0) { + log.info("成功为志愿者队伍 {} 的队长分配角色,队长用户ID: {}, 角色ID: {}", + team.getTeamName(), team.getLeaderUserId(), VOLUNTEER_CAPTAIN_ROLE_ID); + } else { + log.error("为志愿者队伍 {} 的队长分配角色失败,队长用户ID: {}", team.getTeamName(), team.getLeaderUserId()); + } + } else { + log.debug("志愿者队伍 {} 的队长已有该角色,跳过分配", team.getTeamName()); + } + } else { + log.warn("志愿者队伍 {} 的队长用户ID为空,跳过角色分配", team.getTeamName()); + } + } catch (Exception e) { + log.error("为志愿者队伍 {} 的队长分配角色时发生异常", team.getTeamName(), e); + // 不抛出异常,避免影响主业务流程 + } + } + + /** + * 处理队长角色分配(更新时使用) + * + * @param oldTeam 更新前的队伍信息 + * @param newTeam 更新后的队伍信息 + */ + private void handleLeaderRoleAssignment(StglVolunteerTeam oldTeam, StglVolunteerTeam newTeam) { + try { + // 获取更新前后的队长用户ID + Long oldLeaderUserId = oldTeam != null ? oldTeam.getLeaderUserId() : null; + Long newLeaderUserId = newTeam.getLeaderUserId(); + + // 如果队长发生变化 + if (!Objects.equals(oldLeaderUserId, newLeaderUserId)) { + // 移除旧队长的角色 + if (oldLeaderUserId != null) { + removeLeaderRole(oldLeaderUserId); + log.info("志愿者队伍 {} 更换队长,成功移除原队长角色,用户ID: {}", newTeam.getTeamName(), oldLeaderUserId); + } + + // 为新队长分配角色 + if (newLeaderUserId != null) { + assignLeaderRole(newTeam); + } + } + } catch (Exception e) { + log.error("处理志愿者队伍 {} 的队长角色分配时发生异常", newTeam.getTeamName(), e); + // 不抛出异常,避免影响主业务流程 + } + } + + /** + * 移除队长角色 + * + * @param leaderUserId 队长用户ID + */ + private void removeLeaderRole(Long leaderUserId) { + try { + int result = sysUserRoleMapper.delete( + Wrappers.lambdaQuery() + .eq(SysUserRole::getUserId, leaderUserId) + .eq(SysUserRole::getRoleId, VOLUNTEER_CAPTAIN_ROLE_ID) + ); + + if (result > 0) { + log.info("成功移除用户 {} 的志愿者队长角色", leaderUserId); + } else { + log.debug("用户 {} 没有志愿者队长角色需要移除", leaderUserId); + } + } catch (Exception e) { + log.error("移除用户 {} 的志愿者队长角色时发生异常", leaderUserId, e); + throw new RuntimeException("移除志愿者队长角色失败", e); + } + } + + /** + * 检查用户是否已有指定角色 + * + * @param userId 用户ID + * @param roleId 角色ID + * @return 是否已有该角色 + */ + private boolean hasUserRole(Long userId, Long roleId) { + try { + LambdaQueryWrapper query = Wrappers.lambdaQuery(); + query.eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getRoleId, roleId); + return sysUserRoleMapper.selectCount(query) > 0; + } catch (Exception e) { + log.error("检查用户角色关系时发生异常,用户ID: {}, 角色ID: {}", userId, roleId, e); + // 如果查询失败,假设用户没有该角色,继续分配 + return false; + } + } + /** * 从志愿者队伍信息中提取指导老师用户ID * * @param team 志愿者队伍信息 + * @return 指导老师用户ID列表 + */ + private List extractAdvisorUserIds(StglVolunteerTeam team) { + List advisorUserIds = new ArrayList<>(); + + // 1. 从advisorsJson字段提取指导老师用户ID + if (StringUtils.isNotBlank(team.getAdvisorsJson())) { + try { + List> advisors = JsonUtils.parseObject(team.getAdvisorsJson(), new TypeReference>>() {}); + if (advisors != null) { + for (Map advisor : advisors) { + Object userIdObj = advisor.get("userId"); + if (userIdObj != null) { + Long userId = Long.valueOf(userIdObj.toString()); + advisorUserIds.add(userId); + } + } + } + } catch (Exception e) { + log.error("解析志愿者队伍 {} 的指导老师JSON数据时发生异常", team.getTeamName(), e); + } + } + + // 2. 从advisorName字段提取指导老师用户ID(兼容旧数据) + if (advisorUserIds.isEmpty() && StringUtils.isNotBlank(team.getAdvisorName())) { + try { + // 根据指导老师姓名查找用户ID + LambdaQueryWrapper query = Wrappers.lambdaQuery(); + query.eq(SysUser::getNickName, team.getAdvisorName()) + .eq(SysUser::getDelFlag, "0") + .eq(SysUser::getStatus, "0") + .last("LIMIT 1"); + + SysUser advisor = userMapper.selectOne(query); + if (advisor != null) { + log.debug("找到志愿者队伍 {} 的指导老师用户ID: {} ({})", team.getTeamName(), advisor.getUserId(), team.getAdvisorName()); + advisorUserIds.add(advisor.getUserId()); + } else { + log.debug("未找到志愿者队伍 {} 指导老师的用户信息: {}", team.getTeamName(), team.getAdvisorName()); + } + } catch (Exception e) { + log.error("查找志愿者队伍 {} 指导老师用户ID时发生异常", team.getTeamName(), e); + } + } + + return advisorUserIds; + } + + /** + * 从志愿者队伍信息中提取指导老师用户ID(兼容旧方法) + * + * @param team 志愿者队伍信息 * @return 指导老师用户ID */ private Long extractAdvisorUserId(StglVolunteerTeam team) { - // 志愿者队伍目前使用单一指导老师字段,需要根据指导老师姓名查找用户ID - if (StringUtils.isNotBlank(team.getAdvisorName())) { - try { - // 这里需要根据指导老师姓名查找用户ID - // 由于志愿者队伍表没有直接存储指导老师用户ID,需要额外的查询逻辑 - // 暂时返回null,后续可以根据需要实现 - log.debug("志愿者队伍 {} 的指导老师 {} 需要根据姓名查找用户ID", team.getTeamName(), team.getAdvisorName()); - return null; - } catch (Exception e) { - log.error("查找志愿者队伍 {} 指导老师用户ID时发生异常", team.getTeamName(), e); - } - } - return null; + List advisorUserIds = extractAdvisorUserIds(team); + return advisorUserIds.isEmpty() ? null : advisorUserIds.get(0); } } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglActivityRatingMapper.xml b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglActivityRatingMapper.xml index 1c6574e7..b73d5a41 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglActivityRatingMapper.xml +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglActivityRatingMapper.xml @@ -64,4 +64,32 @@ + + + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMemberMapper.xml b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMemberMapper.xml index 9c625935..a8a9ee41 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMemberMapper.xml +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMemberMapper.xml @@ -200,6 +200,10 @@ AND m.user_id = #{userId} + + + AND u.phonenumber LIKE CONCAT('%', #{params.phone}, '%') + ORDER BY m.role_type ASC, m.join_time DESC diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMyMapper.xml b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMyMapper.xml index 557e0707..6188531c 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMyMapper.xml +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglMyMapper.xml @@ -132,6 +132,7 @@ a.update_time, ar.registration_time as my_registration_time, CAST(ar.approval_status AS CHAR) as my_registration_status, + CAST(ar.registration_type AS CHAR) as registration_type, CASE WHEN rat.id IS NOT NULL THEN 1 ELSE 0 END as my_rating_status, rat.rating_score as my_rating_score, rat.rating_time as my_rating_time, @@ -149,15 +150,107 @@ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglActivityTeamInfoMapper.xml b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglActivityTeamInfoMapper.xml index 8b8ed7d1..4a29cf52 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglActivityTeamInfoMapper.xml +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglActivityTeamInfoMapper.xml @@ -14,12 +14,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - - - - - - @@ -28,16 +22,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + + + + + select t.id, t.team_name, t.activity_id, t.leader_user_id, t.leader_name, t.leader_student_no, - t.team_members, t.team_size, t.description, t.leader_gender, t.leader_phone, t.leader_email, - t.leader_department, t.leader_major, t.leader_class_name, + t.team_members, t.team_size, t.description, t.tenant_id, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time, t.deleted, - a.activity_name + a.activity_name, + + s.gender as leader_gender, + s.phone as leader_phone, + s.email as leader_email, + s.college_name as leader_department, + s.major_name as leader_major, + s.class_name as leader_class_name from stgl_activity_team_info t left join stgl_activity a on t.activity_id = a.id + left join xg_student_msg s on t.leader_student_no = s.student_no and s.deleted = 0 + + + + + + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml new file mode 100644 index 00000000..2311497a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/sql/stgl_class_permission.sql b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/sql/stgl_class_permission.sql new file mode 100644 index 00000000..525c7daa --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/sql/stgl_class_permission.sql @@ -0,0 +1,33 @@ +-- 班主任查看本班学生社团、志愿者队伍、活动参加情况权限配置 +-- 执行时间:2025-01-15 + +-- 1. 添加菜单权限 +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +VALUES (2000001, '班级学生参与情况', 2000, 1, 'stclass', 'stgl/stclass/index', NULL, 1, 0, 'C', '0', '0', 'stgl:stclass:list', 'education', 103, 1, NOW(), 1, NOW(), '班主任查看本班学生社团、志愿者队伍、活动参加情况'); + +-- 2. 添加按钮权限 +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +VALUES (2000002, '班级学生参与情况查询', 2000001, 1, '', '', NULL, 1, 0, 'F', '0', '0', 'stgl:stclass:query', '#', 103, 1, NOW(), 1, NOW(), '班级学生参与情况查询'); + +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +VALUES (2000003, '班级学生参与情况导出', 2000001, 2, '', '', NULL, 1, 0, 'F', '0', '0', 'stgl:stclass:export', '#', 103, 1, NOW(), 1, NOW(), '班级学生参与情况导出'); + +-- 3. 为班主任角色(head_teacher)分配权限 +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1947639680473493505, 2000001); +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1947639680473493505, 2000002); +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1947639680473493505, 2000003); + +-- 4. 为管理员角色分配权限 +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 2000001); +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 2000002); +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 2000003); + +-- 5. 为超级管理员角色分配权限 +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 2000001); +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 2000002); +INSERT INTO sys_role_menu (role_id, menu_id) VALUES (2, 2000003); + +-- 权限字符说明: +-- stgl:stclass:list - 班级学生参与情况列表查询权限 +-- stgl:stclass:query - 班级学生参与情况详情查询权限 +-- stgl:stclass:export - 班级学生参与情况导出权限 \ No newline at end of file diff --git a/docs/设计文档/database/社团管理/社团管理数据库设计.md b/docs/设计文档/database/社团管理/社团管理数据库设计.md index 26fcc0ec..aae58e51 100644 --- a/docs/设计文档/database/社团管理/社团管理数据库设计.md +++ b/docs/设计文档/database/社团管理/社团管理数据库设计.md @@ -545,7 +545,7 @@ CREATE TABLE `stgl_activity_rating` ( `user_id` bigint(20) NOT NULL COMMENT '评分人ID', `student_no` varchar(20) COMMENT '学号', `student_name` varchar(50) COMMENT '学生姓名', - `rating_score` tinyint NOT NULL COMMENT '评分(1-5分)', + `rating_score` decimal(3,1) NOT NULL COMMENT '综合评分(1-5分,保留1位小数)', `rating_comment` varchar(500) COMMENT '评分备注', `rating_time` datetime COMMENT '评分时间', @@ -566,8 +566,9 @@ CREATE TABLE `stgl_activity_rating` ( ``` #### 字段说明 -- `rating_score`: 总评分,1-5分,不支持0.5分 -- 扩展评分维度为预留字段,后续可扩展使用 +- `rating_score`: 综合评分,1-5分,保留1位小数,由三项分评分平均值计算得出 +- `content_score`、`organization_score`、`experience_score`: 三项分评分维度(内容、组织、体验),1-5分整数 +- 综合评分计算公式:`rating_score = ROUND((content_score + organization_score + experience_score) / 3, 1)` ## 3. 码表数据设计 diff --git a/docs/需求文档/社团管理需求/社团管理系统需求说明.md b/docs/需求文档/社团管理需求/社团管理系统需求说明.md index 0a8ed046..436cf3de 100644 --- a/docs/需求文档/社团管理需求/社团管理系统需求说明.md +++ b/docs/需求文档/社团管理需求/社团管理系统需求说明.md @@ -309,7 +309,7 @@ - `activity_id` bigint(20) COMMENT '活动ID' - `user_id` bigint(20) COMMENT '评分人ID' -- `rating_score` tinyint COMMENT '评分(1-5分)' +- `rating_score` decimal(3,1) COMMENT '综合评分(1-5分,保留1位小数)' - `rating_comment` varchar(500) COMMENT '评分备注' - `rating_time` datetime COMMENT '评分时间' diff --git a/web/src/api/stgl/activity/types.ts b/web/src/api/stgl/activity/types.ts index 2d52e350..0aba8f9c 100644 --- a/web/src/api/stgl/activity/types.ts +++ b/web/src/api/stgl/activity/types.ts @@ -582,6 +582,16 @@ export interface ActivityCopyParams { * 新活动名称 */ newActivityName: string; + + /** + * 新活动状态(复制后默认为草稿状态) + */ + activityStatus?: string; + + /** + * 新活动报名状态(复制后默认为未开始状态) + */ + registrationStatus?: string; } /** @@ -639,10 +649,50 @@ export interface ActivityRatingVO { userName?: string; /** - * 评分(1-5分) + * 评分人学号 + */ + studentNo?: string; + + /** + * 评分人联系方式 + */ + phoneNumber?: string; + + /** + * 评分人院系 + */ + deptName?: string; + + /** + * 评分人专业 + */ + majorName?: string; + + /** + * 评分人班级 + */ + className?: string; + + /** + * 总评分(1-5分) */ ratingScore: number; + /** + * 内容评分(1-5分) + */ + contentScore?: number; + + /** + * 组织评分(1-5分) + */ + organizationScore?: number; + + /** + * 体验评分(1-5分) + */ + experienceScore?: number; + /** * 评分备注 */ diff --git a/web/src/api/stgl/activityregisterhall/types.ts b/web/src/api/stgl/activityregisterhall/types.ts index db298dc9..e8935f0e 100644 --- a/web/src/api/stgl/activityregisterhall/types.ts +++ b/web/src/api/stgl/activityregisterhall/types.ts @@ -369,6 +369,11 @@ export interface StglActivityRegistrationForm { * 班级 */ className?: string; + + /** + * 审核状态(0-待审核 1-通过 2-驳回) + */ + approvalStatus?: number; } // 团队成员信息 diff --git a/web/src/api/stgl/activityteaminfo/index.ts b/web/src/api/stgl/activityteaminfo/index.ts new file mode 100644 index 00000000..8cd32b24 --- /dev/null +++ b/web/src/api/stgl/activityteaminfo/index.ts @@ -0,0 +1,62 @@ +import request from '@/utils/request'; +import { StglActivityTeamInfoVo } from './types'; + +// 查询活动团队信息列表 +export function listActivityTeamInfo(query?: any) { + return request({ + url: '/stgl/activityteaminfo/list', + method: 'get', + params: query + }); +} + +// 查询活动团队信息详细 +export function getActivityTeamInfo(id: string | number) { + return request({ + url: '/stgl/activityteaminfo/' + id, + method: 'get' + }); +} + +// 新增活动团队信息 +export function addActivityTeamInfo(data: any) { + return request({ + url: '/stgl/activityteaminfo', + method: 'post', + data: data + }); +} + +// 修改活动团队信息 +export function updateActivityTeamInfo(data: any) { + return request({ + url: '/stgl/activityteaminfo', + method: 'put', + data: data + }); +} + +// 删除活动团队信息 +export function delActivityTeamInfo(ids: string | number | Array) { + return request({ + url: '/stgl/activityteaminfo/' + ids, + method: 'delete' + }); +} + +// 导出活动团队信息 +export function exportActivityTeamInfo(query?: any) { + return request({ + url: '/stgl/activityteaminfo/export', + method: 'post', + data: query + }); +} + +// 根据活动ID查询团队列表 +export function getTeamsByActivityId(activityId: string | number) { + return request({ + url: '/stgl/activityteaminfo/activity/' + activityId, + method: 'get' + }); +} \ No newline at end of file diff --git a/web/src/api/stgl/activityteaminfo/types.ts b/web/src/api/stgl/activityteaminfo/types.ts new file mode 100644 index 00000000..39dad5a4 --- /dev/null +++ b/web/src/api/stgl/activityteaminfo/types.ts @@ -0,0 +1,241 @@ +// 团队成员信息 +export interface TeamMemberInfo { + /** + * 用户ID + */ + userId: string | number; + + /** + * 学号 + */ + studentNo: string; + + /** + * 姓名 + */ + studentName: string; + + /** + * 性别 + */ + gender?: string; + + /** + * 联系电话 + */ + phone?: string; + + /** + * 邮箱 + */ + email?: string; + + /** + * 院系 + */ + department?: string; + + /** + * 专业 + */ + major?: string; + + /** + * 班级 + */ + className?: string; + + /** + * 是否为队长(0-否 1-是) + */ + isLeader?: number; +} + +// 活动团队信息表单对象 +export interface StglActivityTeamInfoForm { + /** + * 主键ID + */ + id?: string | number; + + /** + * 团队名称 + */ + teamName: string; + + /** + * 关联活动ID + */ + activityId: string | number; + + /** + * 队长用户ID + */ + leaderUserId: string | number; + + /** + * 队长姓名 + */ + leaderName: string; + + /** + * 队长学号 + */ + leaderStudentNo: string; + + /** + * 团队成员信息(JSON格式) + */ + teamMembers?: string; + + /** + * 团队人数 + */ + teamSize?: number; + + /** + * 团队简介 + */ + description?: string; +} + +// 活动团队信息查询对象 +export interface StglActivityTeamInfoQuery { + /** + * 页码 + */ + pageNum?: number; + + /** + * 每页数量 + */ + pageSize?: number; + + /** + * 团队名称 + */ + teamName?: string; + + /** + * 关联活动ID + */ + activityId?: string | number; + + /** + * 队长用户ID + */ + leaderUserId?: string | number; + + /** + * 队长姓名 + */ + leaderName?: string; + + /** + * 队长学号 + */ + leaderStudentNo?: string; +} + +// 活动团队信息视图对象 +export interface StglActivityTeamInfoVo { + /** + * 主键ID + */ + id: string | number; + + /** + * 团队名称 + */ + teamName: string; + + /** + * 关联活动ID + */ + activityId: string | number; + + /** + * 队长用户ID + */ + leaderUserId: string | number; + + /** + * 队长姓名 + */ + leaderName: string; + + /** + * 队长学号 + */ + leaderStudentNo: string; + + /** + * 团队成员信息(JSON格式) + */ + teamMembers?: string; + + /** + * 团队人数 + */ + teamSize?: number; + + /** + * 团队简介 + */ + description?: string; + + /** + * 队长性别 + */ + leaderGender?: string; + + /** + * 队长联系电话 + */ + leaderPhone?: string; + + /** + * 队长邮箱 + */ + leaderEmail?: string; + + /** + * 队长院系 + */ + leaderDepartment?: string; + + /** + * 队长专业 + */ + leaderMajor?: string; + + /** + * 队长班级 + */ + leaderClassName?: string; + + /** + * 活动名称 + */ + activityName?: string; + + /** + * 创建者 + */ + createBy?: string | number; + + /** + * 创建时间 + */ + createTime?: string; + + /** + * 更新者 + */ + updateBy?: string | number; + + /** + * 更新时间 + */ + updateTime?: string; +} \ No newline at end of file diff --git a/web/src/api/stgl/my/types.ts b/web/src/api/stgl/my/types.ts index c2ab4c6d..50c92546 100644 --- a/web/src/api/stgl/my/types.ts +++ b/web/src/api/stgl/my/types.ts @@ -93,12 +93,14 @@ export interface MyActivityVO { location?: string; /** 主办方名称 */ organizerName?: string; + /** 承办方名称 */ + undertakerName?: string; /** 关联类型(1-社团 2-志愿者队伍) */ relatedType?: number; /** 关联组织名称(社团名称或志愿者团队名称) */ organizationName?: string; /** 指导老师 */ - instructors?: string; + instructors?: string | any[]; /** 活动状态 */ activityStatus: string; /** 报名状态 */ @@ -111,6 +113,8 @@ export interface MyActivityVO { myRegistrationTime: string; /** 我的报名状态 */ myRegistrationStatus: string; + /** 报名类型(1-个人 2-团队) */ + registrationType: string; /** 我的评分状态(0-未评分 1-已评分) */ myRatingStatus: number; /** 我的综合评分 */ diff --git a/web/src/api/stgl/stclass/index.ts b/web/src/api/stgl/stclass/index.ts new file mode 100644 index 00000000..6dd6a53f --- /dev/null +++ b/web/src/api/stgl/stclass/index.ts @@ -0,0 +1,64 @@ +import request from '@/utils/request'; +import { StglClassQuery, StglClassVO, StglClassDetailVO } from './types'; + +/** + * 查询班主任的班级列表 + */ +export function getTeacherClasses() { + return request({ + url: '/stgl/stclass/teacher/classes', + method: 'get' + }); +} + +/** + * 查询本班学生社团、志愿者队伍、活动参加情况列表 + */ +export function listStglClass(query: StglClassQuery) { + return request({ + url: '/stgl/stclass/list', + method: 'get', + params: query + }); +} + +/** + * 查询学生社团详情 + */ +export function getStudentClubs(studentId: string | number) { + return request({ + url: `/stgl/stclass/student/clubs/${studentId}`, + method: 'get' + }); +} + +/** + * 查询学生志愿者队伍详情 + */ +export function getStudentVolunteerTeams(studentId: string | number) { + return request({ + url: `/stgl/stclass/student/volunteer-teams/${studentId}`, + method: 'get' + }); +} + +/** + * 查询学生活动参加详情 + */ +export function getStudentActivities(studentId: string | number) { + return request({ + url: `/stgl/stclass/student/activities/${studentId}`, + method: 'get' + }); +} + +/** + * 导出本班学生社团、志愿者队伍、活动参加情况 + */ +export function exportStglClass(query: StglClassQuery) { + return request({ + url: '/stgl/stclass/export', + method: 'get', + params: query + }); +} \ No newline at end of file diff --git a/web/src/api/stgl/stclass/types.ts b/web/src/api/stgl/stclass/types.ts new file mode 100644 index 00000000..56633584 --- /dev/null +++ b/web/src/api/stgl/stclass/types.ts @@ -0,0 +1,292 @@ +/** + * 分页查询基础接口 + */ +export interface PageQuery { + pageNum?: number; + pageSize?: number; +} + +/** + * 班级学生社团、志愿者队伍、活动参加情况查询对象 + */ +export interface StglClassQuery extends PageQuery { + /** + * 班级ID + */ + classId?: string | number; + + /** + * 学生姓名 + */ + studentName?: string; + + /** + * 学号 + */ + studentNo?: string; +} + +/** + * 班级学生社团、志愿者队伍、活动参加情况视图对象 + */ +export interface StglClassVO { + /** + * 学生ID + */ + id: string | number; + + /** + * 学号 + */ + studentNo: string; + + /** + * 学生姓名 + */ + studentName: string; + + /** + * 性别 + */ + gender: string; + + /** + * 联系电话 + */ + phone: string; + + /** + * 班级名称 + */ + className: string; + + /** + * 加入社团数量 + */ + clubCount: number; + + /** + * 加入志愿者队伍数量 + */ + volunteerTeamCount: number; + + /** + * 参与活动数量 + */ + activityCount: number; +} + +/** + * 社团详情对象 + */ +export interface ClubDetailVO { + /** + * 社团ID + */ + id: string | number; + + /** + * 社团名称 + */ + clubName: string; + + /** + * 社团类型 + */ + clubType: string; + + /** + * 成立日期 + */ + establishDate: string; + + /** + * 成员规模 + */ + memberLimit: number; + + /** + * 社长姓名 + */ + presidentName: string; + + /** + * 社长联系方式 + */ + presidentPhone: string; + + /** + * 社长院系 + */ + presidentDept: string; + + /** + * 社长专业 + */ + presidentMajor: string; + + /** + * 社长班级 + */ + presidentClass: string; + + /** + * 指导老师姓名 + */ + advisorName: string; + + /** + * 指导老师部门 + */ + advisorDept: string; + + /** + * 指导老师电话 + */ + advisorPhone: string; +} + +/** + * 志愿者队伍详情对象 + */ +export interface VolunteerTeamDetailVO { + /** + * 队伍ID + */ + id: string | number; + + /** + * 队伍名称 + */ + teamName: string; + + /** + * 成立日期 + */ + establishDate: string; + + /** + * 队长姓名 + */ + leaderName: string; + + /** + * 队长联系方式 + */ + leaderPhone: string; + + /** + * 队长院系 + */ + leaderDept: string; + + /** + * 队长专业 + */ + leaderMajor: string; + + /** + * 队长班级 + */ + leaderClass: string; + + /** + * 指导老师姓名 + */ + advisorName: string; + + /** + * 指导老师部门 + */ + advisorDept: string; + + /** + * 指导老师电话 + */ + advisorPhone: string; +} + +/** + * 活动详情对象 + */ +export interface ActivityDetailVO { + /** + * 活动ID + */ + id: string | number; + + /** + * 活动名称 + */ + activityName: string; + + /** + * 活动类型 + */ + activityType: string; + + /** + * 活动主题 + */ + activityTheme: string; + + /** + * 活动地点 + */ + location: string; + + /** + * 活动时间 + */ + activityTime: string; + + /** + * 关联社团/队伍 + */ + relatedOrganization: string; + + /** + * 指导老师姓名 + */ + advisorName: string; + + /** + * 指导老师部门 + */ + advisorDept: string; + + /** + * 指导老师电话 + */ + advisorPhone: string; +} + +/** + * 班级详情对象 + */ +export interface StglClassDetailVO { + /** + * 班级ID + */ + id: string | number; + + /** + * 班级名称 + */ + className: string; + + /** + * 学院名称 + */ + collegeName: string; + + /** + * 专业名称 + */ + majorName: string; + + /** + * 年级 + */ + grade: string; +} \ No newline at end of file diff --git a/web/src/components/AdvisorManager/index.vue b/web/src/components/AdvisorManager/index.vue index 4fda36db..62105d79 100644 --- a/web/src/components/AdvisorManager/index.vue +++ b/web/src/components/AdvisorManager/index.vue @@ -15,7 +15,11 @@ - + @@ -48,7 +52,7 @@ diff --git a/web/src/views/stgl/activity/components/PendingMembersTab.vue b/web/src/views/stgl/activity/components/PendingMembersTab.vue index c5b2020d..ffbf0088 100644 --- a/web/src/views/stgl/activity/components/PendingMembersTab.vue +++ b/web/src/views/stgl/activity/components/PendingMembersTab.vue @@ -6,21 +6,16 @@ - + - + - - - - - 搜索 @@ -38,27 +33,12 @@ @@ -100,19 +80,16 @@ - + @@ -122,8 +99,9 @@ - - + + + {{ viewForm.studentNo }} {{ viewForm.studentName }} @@ -136,11 +114,53 @@ - {{ viewForm.teamName || '-' }} {{ viewForm.remark || '-' }} {{ parseTime(viewForm.registrationTime, '{y}-{m}-{d} {h}:{i}:{s}') }} + +
+ + 团队信息 + + + + {{ teamInfo.teamName }} + {{ teamInfo.teamSize || 0 }}人 + {{ teamInfo.leaderName }} + {{ teamInfo.leaderStudentNo }} + {{ teamInfo.leaderPhone || '-' }} + {{ teamInfo.leaderDepartment || '-' }} + {{ teamInfo.description || '暂无描述' }} + + + +
+

队员列表

+ + + + + + + + + + + + + + + +
+
+