Jelajahi Sumber

feat: 创建小组和添加小组成员

mjh 4 tahun lalu
induk
melakukan
606d3f84cd

+ 44 - 0
seecoder-portal-common/src/main/java/cn/seecoder/seecoderportalcommon/vo/group/GroupMemberVO.java

@@ -0,0 +1,44 @@
+package cn.seecoder.seecoderportalcommon.vo.group;
+
+/**
+ * @author wind
+ */
+public class GroupMemberVO {
+    private Long userId;
+    private Long groupId;
+    private Long courseId;
+
+    @Override
+    public String toString() {
+        return "GroupMemberVO{" +
+                "userId=" + userId +
+                ", groupId=" + groupId +
+                ", courseId=" + courseId +
+                '}';
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Long getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(Long groupId) {
+        this.groupId = groupId;
+    }
+
+}

+ 27 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/mapper/group/GroupMemberMapper.java

@@ -0,0 +1,27 @@
+package cn.seecoder.seecoderportalserver.mapper.group;
+
+import cn.seecoder.seecoderportalcommon.vo.group.GroupMemberVO;
+import cn.seecoder.seecoderportalserver.domain.group.GroupMember;
+import org.mapstruct.Mapper;
+
+/**
+ * @author wind
+ */
+@Mapper(componentModel = "spring")
+public interface GroupMemberMapper {
+    /**
+     * po2vo
+     *
+     * @param groupMember po
+     * @return vo
+     */
+    GroupMemberVO toGroupMemberVO(GroupMember groupMember);
+
+    /**
+     * vo2po
+     *
+     * @param groupMemberVO vo
+     * @return po
+     */
+    GroupMember toGroupMember(GroupMemberVO groupMemberVO);
+}

+ 34 - 3
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/repository/group/GroupMemberRepository.java

@@ -4,6 +4,7 @@ import cn.seecoder.seecoderportalserver.domain.group.GroupMember;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import org.springframework.stereotype.Repository;
 
 /**
@@ -14,12 +15,42 @@ public interface GroupMemberRepository extends BaseMapper<GroupMember> {
     /**
      * 新建相关课程下小组成员表
      *
-     * @param name 表名称
+     * @param name      String 表名称
+     * @param groupName String 小组表名称
      */
     @Insert("create table if not exists ${name} (\n" +
             "user_id bigint not null references portal_user(id),\n" +
-            "group_id bigint not null,\n" +
+            "group_id bigint not null references ${groupName}(id),\n" +
             "primary key (user_id)\n" +
             ")")
-    void createGroupMemberTable(@Param("name") String name);
+    void createGroupMemberTable(@Param("name") String name, @Param("groupName") String groupName);
+
+    /**
+     * 根据userId查找小组和用户的关系
+     *
+     * @param name   String 表名称
+     * @param userId Long
+     * @return GroupMember
+     */
+    @Select("select * from ${name} where user_id=${userId}")
+    GroupMember findByUserId(@Param("name") String name, @Param("userId") Long userId);
+
+    /**
+     * 新建小组与用户的关系
+     *
+     * @param name  String
+     * @param group Group
+     */
+    @Insert("insert into ${name} (user_id, group_id) values (${vo.userId}, ${vo.groupId})")
+    void createGroupMember(@Param("name") String name, @Param("vo") GroupMember group);
+
+    /**
+     * 查找小组成员数量
+     *
+     * @param name String 表名称
+     * @param groupId Long
+     * @return int
+     */
+    @Select("select count(*) from ${name} where group_id=${groupId}")
+    int countGroupMembers(@Param("name") String name, @Param("groupId") Long groupId);
 }

+ 13 - 7
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/repository/group/GroupRepository.java

@@ -2,10 +2,7 @@ package cn.seecoder.seecoderportalserver.repository.group;
 
 import cn.seecoder.seecoderportalserver.domain.group.Group;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Insert;
-import org.apache.ibatis.annotations.Options;
-import org.apache.ibatis.annotations.Param;
-import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.*;
 import org.springframework.stereotype.Repository;
 
 /**
@@ -23,17 +20,26 @@ public interface GroupRepository extends BaseMapper<Group> {
     @Insert("create table if not exists ${name} (\n" +
             "id bigserial primary key,\n" +
             "name varchar(${len}) not null,\n" +
-            "unique (name)"+
+            "unique (name)" +
             ")")
     void createGroupTable(@Param("name") String name, @Param("len") String len);
 
     /**
-     * 创建小组信息
+     * 创建小组
      *
      * @param group 小组信息
      * @param name  表名
      */
     @Insert("insert into ${name} (name) values ('${group.name}')")
     @Options(useGeneratedKeys = true, keyProperty = "group.id")
-    void createGroup(@Param("group") Group group, @Param("name") String name);
+    void createGroup(@Param("name") String name, @Param("group") Group group);
+
+    /**
+     * 删除小组
+     *
+     * @param name String 表名称
+     * @param id   Long
+     */
+    @Delete("delete from ${name} where id=${id}")
+    void deleteGroup(@Param("name") String name, @Param("id") Long id);
 }

+ 7 - 2
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/repository/group/GroupRuleRepository.java

@@ -1,6 +1,5 @@
 package cn.seecoder.seecoderportalserver.repository.group;
 
-import cn.seecoder.seecoderportalserver.domain.Course;
 import cn.seecoder.seecoderportalserver.domain.group.GroupRule;
 import org.springframework.data.jpa.repository.JpaRepository;
 
@@ -10,5 +9,11 @@ import java.util.Optional;
  * @author wind
  */
 public interface GroupRuleRepository extends JpaRepository<GroupRule, Long> {
-
+    /**
+     * 根据课程id查找课程规则
+     *
+     * @param courseId Long 课程id
+     * @return GroupRule
+     */
+    Optional<GroupRule> findByCourse_Id(Long courseId);
 }

+ 9 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/service/GroupService.java

@@ -1,6 +1,7 @@
 package cn.seecoder.seecoderportalserver.service;
 
 
+import cn.seecoder.seecoderportalcommon.vo.group.GroupMemberVO;
 import cn.seecoder.seecoderportalcommon.vo.group.GroupRuleVO;
 import cn.seecoder.seecoderportalcommon.vo.group.GroupVO;
 import cn.seecoder.seecoderportalserver.domain.group.Group;
@@ -41,4 +42,12 @@ public interface GroupService {
      * @return 小组信息(包含Id)
      */
     GroupVO createGroup(GroupVO groupVO);
+
+    /**
+     * 添加小组成员
+     *
+     * @param groupMemberVO GroupMemberVO
+     * @return GroupMemberVO
+     */
+    GroupMemberVO addGroupMember(GroupMemberVO groupMemberVO);
 }

+ 14 - 4
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/serviceImpl/AnnouncementServiceImpl.java

@@ -20,6 +20,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
+/**
+ * @author wind
+ */
 @Service
 public class AnnouncementServiceImpl implements AnnouncementService {
     private final AnnouncementRepository announcementRepository;
@@ -36,6 +39,7 @@ public class AnnouncementServiceImpl implements AnnouncementService {
         this.userService = userService;
     }
 
+    @Override
     public AnnouncementVO create(AnnouncementVO announcementVO, Long userId) {
         announcementVO.getSender().setId(userId);
         Announcement announcement = announcementMapper.toAnnouncement(announcementVO);
@@ -44,6 +48,7 @@ public class AnnouncementServiceImpl implements AnnouncementService {
         return announcementMapper.toAnnouncementVO(result);
     }
 
+    @Override
     public String delete(Long id, Long userId) {
         AnnouncementVO announcement = this.findAnnouncementById(id);
         if (!announcement.getSender().getId().equals(userId)) {
@@ -54,6 +59,7 @@ public class AnnouncementServiceImpl implements AnnouncementService {
         return "公告删除完成。";
     }
 
+    @Override
     public AnnouncementVO update(AnnouncementVO announcementVO, Long userId) {
         AnnouncementVO announcement = this.findAnnouncementById(announcementVO.getId());
         if (!userId.equals(announcement.getSender().getId())) {
@@ -66,6 +72,7 @@ public class AnnouncementServiceImpl implements AnnouncementService {
         return announcementMapper.toAnnouncementVO(result);
     }
 
+    @Override
     public AnnouncementVO findAnnouncementById(Long id, Long userId) {
         Announcement announcement = announcementRepository.findById(id).orElseThrow(CustomErrorException::announcementNotFound);
         UserVO user = userService.findUserById(userId);
@@ -74,6 +81,7 @@ public class AnnouncementServiceImpl implements AnnouncementService {
         return result;
     }
 
+    @Override
     public AnnouncementVO findAnnouncementById(Long id) {
         Announcement announcement = announcementRepository.findById(id).orElseThrow(CustomErrorException::announcementNotFound);
         AnnouncementVO result = announcementMapper.toAnnouncementVO(announcement);
@@ -81,6 +89,7 @@ public class AnnouncementServiceImpl implements AnnouncementService {
         return result;
     }
 
+    @Override
     public List<AnnouncementVO> findAnnouncementsByCourseId(Long courseId) {
         courseService.findCourseById(courseId);
         DomainUserDetails user = SecurityUtils.getCurrentUser();
@@ -89,12 +98,11 @@ public class AnnouncementServiceImpl implements AnnouncementService {
                 .filter(announcement -> announcement.getState() != ObjectState.DELETED)
                 .map(announcementMapper::toAnnouncementVO)
                 .collect(Collectors.toList());
-        result.forEach(announcementVO -> {
-            announcementVO.setRead(userService.isTeacher(user.getRole()) || this.getReadState(announcementVO.getId(), user.getId()));
-        });
+        result.forEach(announcementVO -> announcementVO.setRead(userService.isTeacher(user.getRole()) || this.getReadState(announcementVO.getId(), user.getId())));
         return result;
     }
 
+    @Override
     public List<AnnouncementVO> findUnreadAnnouncementsByUserId(Long userId) {
         UserVO user = userService.findUserById(userId);
         if (userService.isStudent(user.getRole())) {
@@ -135,7 +143,7 @@ public class AnnouncementServiceImpl implements AnnouncementService {
             }
             announcementList.addAll(recordButUnread);
             return announcementList.stream()
-                    .map(id -> {return this.findAnnouncementById(id, userId);})
+                    .map(id -> this.findAnnouncementById(id, userId))
                     .collect(Collectors.toList());
         } else {
             return new ArrayList<>();
@@ -151,12 +159,14 @@ public class AnnouncementServiceImpl implements AnnouncementService {
         }
     }
 
+    @Override
     public List<AnnouncementVO> findAnnouncementsByUserId(Long userId) {
         return announcementRepository.findAnnouncementsBySenderIdAndStateNot(userId, ObjectState.DELETED).stream()
                 .map(announcementMapper::toAnnouncementVO)
                 .collect(Collectors.toList());
     }
 
+    @Override
     public String readAnnouncement(Long id, Long userId) {
         AnnouncementRecord record = announcementRecordRepository.findById(new AnnouncementRecordKey(id, userId)).orElseThrow(CustomErrorException::announcementRecordNotFound);
         record.setState(true);

+ 62 - 8
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/serviceImpl/GroupServiceImpl.java

@@ -1,14 +1,18 @@
 package cn.seecoder.seecoderportalserver.serviceImpl;
 
+import cn.seecoder.seecoderportalcommon.vo.group.GroupMemberVO;
 import cn.seecoder.seecoderportalcommon.vo.group.GroupRuleVO;
 import cn.seecoder.seecoderportalcommon.vo.group.GroupVO;
 import cn.seecoder.seecoderportalserver.domain.group.Group;
+import cn.seecoder.seecoderportalserver.domain.group.GroupMember;
 import cn.seecoder.seecoderportalserver.domain.group.GroupRule;
 import cn.seecoder.seecoderportalserver.mapper.group.GroupMapper;
+import cn.seecoder.seecoderportalserver.mapper.group.GroupMemberMapper;
 import cn.seecoder.seecoderportalserver.mapper.group.GroupRuleMapper;
 import cn.seecoder.seecoderportalserver.repository.group.GroupMemberRepository;
 import cn.seecoder.seecoderportalserver.repository.group.GroupRepository;
 import cn.seecoder.seecoderportalserver.repository.group.GroupRuleRepository;
+import cn.seecoder.seecoderportalserver.security.DomainUserDetails;
 import cn.seecoder.seecoderportalserver.security.SecurityUtils;
 import cn.seecoder.seecoderportalserver.service.CourseService;
 import cn.seecoder.seecoderportalserver.service.GroupService;
@@ -28,14 +32,16 @@ public class GroupServiceImpl implements GroupService {
     private final GroupRuleRepository groupRuleRepository;
     private final GroupRuleMapper groupRuleMapper;
     private final GroupMemberRepository groupMemberRepository;
+    private final GroupMemberMapper groupMemberMapper;
     private final CourseService courseService;
 
-    public GroupServiceImpl(GroupRepository groupRepository, GroupMapper groupMapper, GroupRuleRepository groupRuleRepository, GroupRuleMapper groupRuleMapper, GroupMemberRepository groupMemberRepository, CourseService courseService) {
+    public GroupServiceImpl(GroupRepository groupRepository, GroupMapper groupMapper, GroupRuleRepository groupRuleRepository, GroupRuleMapper groupRuleMapper, GroupMemberRepository groupMemberRepository, GroupMemberMapper groupMemberMapper, CourseService courseService) {
         this.groupRepository = groupRepository;
         this.groupMapper = groupMapper;
         this.groupRuleRepository = groupRuleRepository;
         this.groupRuleMapper = groupRuleMapper;
         this.groupMemberRepository = groupMemberRepository;
+        this.groupMemberMapper = groupMemberMapper;
         this.courseService = courseService;
     }
 
@@ -43,24 +49,74 @@ public class GroupServiceImpl implements GroupService {
     public String createGroupTable(GroupRuleVO groupRuleVO) {
         String tableName = getGroupTableName(groupRuleVO.getCourseVO().getId());
         groupRepository.createGroupTable(tableName, groupRuleVO.getMaxNameLen().toString());
+        log.info("Create Group Table " + tableName);
         return tableName;
     }
 
     @Override
     public String createGroupMemberTable(Long courseId) {
         String tableName = getGroupMemberTableName(courseId);
-        groupMemberRepository.createGroupMemberTable(tableName);
+        groupMemberRepository.createGroupMemberTable(tableName, getGroupTableName(courseId));
+        log.info("Create Group Member Table " + tableName);
         return tableName;
     }
 
     @Override
     public GroupVO createGroup(GroupVO groupVO) {
-        // todo
+        DomainUserDetails user = SecurityUtils.getCurrentUser();
+        // 验证选课
+        if (!courseService.checkSelection(user.getId(), groupVO.getCourseId())) {
+            throw CustomErrorException.courseSelectionNotFound();
+        }
+        // 验证尚未加入小组
+        if (groupMemberRepository.findByUserId(getGroupMemberTableName(groupVO.getCourseId()), user.getId()) != null) {
+            throw CustomErrorException.groupMemberAlreadyExists("当前用户无法创建新的小组!");
+        }
+        // 创建小组
         Group group = groupMapper.toGroup(groupVO);
-        groupRepository.createGroup(group, getGroupTableName(groupVO.getCourseId()));
+        groupRepository.createGroup(getGroupTableName(groupVO.getCourseId()), group);
+        log.info("Create Group: {}", group);
+        // 添加小组成员(当前用户)
+        try {
+            GroupMemberVO groupMemberVO = new GroupMemberVO();
+            groupMemberVO.setUserId(user.getId());
+            groupMemberVO.setGroupId(group.getId());
+            groupMemberVO.setCourseId(groupVO.getCourseId());
+            addGroupMember(groupMemberVO);
+        } catch (RuntimeException e) {
+            log.error(e.getMessage());
+            groupRepository.deleteGroup(getGroupTableName(groupVO.getCourseId()), group.getId());
+            log.info("Delete Group: {}", group);
+        }
         return groupMapper.toGroupVO(group);
     }
 
+    @Override
+    public GroupMemberVO addGroupMember(GroupMemberVO groupMemberVO) {
+        String tableName = getGroupMemberTableName(groupMemberVO.getCourseId());
+        DomainUserDetails user = SecurityUtils.getCurrentUser();
+        // 验证选课
+        if (!courseService.checkSelection(user.getId(), groupMemberVO.getCourseId())) {
+            throw CustomErrorException.courseSelectionNotFound();
+        }
+        // 验证尚未加入小组
+        if (groupMemberRepository.findByUserId(tableName, user.getId()) != null) {
+            throw CustomErrorException.groupMemberAlreadyExists("当前用户无法加入其他小组!");
+        }
+        // 验证小组未满员
+        GroupRule groupRule = groupRuleRepository.findByCourse_Id(groupMemberVO.getCourseId())
+                .orElseThrow(() -> CustomErrorException.groupRuleNotFound(null));
+        int currentSize = groupMemberRepository.countGroupMembers(tableName, groupMemberVO.getGroupId());
+        if(currentSize == groupRule.getMaxSize()){
+            throw CustomErrorException.groupAlreadyMaxSize(null);
+        }
+        // 添加小组成员
+        GroupMember groupMember = groupMemberMapper.toGroupMember(groupMemberVO);
+        groupMemberRepository.createGroupMember(tableName, groupMember);
+        log.info("Create GroupMember: {}", groupMember);
+        return groupMemberMapper.toGroupMemberVO(groupMember);
+    }
+
     @Override
     public GroupRuleVO editGroupRule(GroupRuleVO ruleVO) {
         Long teacherId = courseService
@@ -72,10 +128,8 @@ public class GroupServiceImpl implements GroupService {
         GroupRule groupRule;
         groupRule = groupRuleRepository.save(groupRuleMapper.toGroupRule(ruleVO));
         log.info("Edit Course Group Rule: {}", groupRule);
-        String table1 = createGroupTable(ruleVO);
-        log.info("Create Group Table " + table1);
-        String table2 = createGroupMemberTable(ruleVO.getCourseVO().getId());
-        log.info("Create Group Member Table " + table2);
+        createGroupTable(ruleVO);
+        createGroupMemberTable(ruleVO.getCourseVO().getId());
         return groupRuleMapper.toGroupRuleVO(groupRule);
     }
 

+ 6 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/web/rest/GroupController.java

@@ -1,5 +1,6 @@
 package cn.seecoder.seecoderportalserver.web.rest;
 
+import cn.seecoder.seecoderportalcommon.vo.group.GroupMemberVO;
 import cn.seecoder.seecoderportalcommon.vo.group.GroupRuleVO;
 import cn.seecoder.seecoderportalcommon.vo.group.GroupVO;
 import cn.seecoder.seecoderportalserver.service.GroupService;
@@ -30,4 +31,9 @@ public class GroupController {
     public OkResponse<GroupVO> createGroup(@RequestBody GroupVO groupVO){
         return OkResponse.of(groupService.createGroup(groupVO));
     }
+
+    @PostMapping("/member/add")
+    public OkResponse<GroupMemberVO> addGroupMember(@RequestBody GroupMemberVO groupMemberVO){
+        return OkResponse.of(groupService.addGroupMember(groupMemberVO));
+    }
 }

+ 14 - 11
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/web/rest/errors/CustomErrorException.java

@@ -58,20 +58,23 @@ public class CustomErrorException extends RuntimeException {
         return new CustomErrorException("devcloud: " + info);
     }
 
-    /**
-     * 手机号不存在触发报错
-     * @param phone 手机号
-     * @return 异常
-     */
-    public static CustomErrorException userPhoneNotFound(String phone){
+    public static CustomErrorException userPhoneNotFound(String phone) {
         return new CustomErrorException("手机号 \"" + phone + "\" 不存在!");
     }
 
-
-    /**
-     * @return 异常
-     */
-    public static CustomErrorException unauthorizedUser(){
+    public static CustomErrorException unauthorizedUser() {
         return new CustomErrorException("未授权用户!");
     }
+
+    public static CustomErrorException groupMemberAlreadyExists(String additional) {
+        return new CustomErrorException("小组成员记录已存在!" + (additional == null ? "" : additional));
+    }
+
+    public static CustomErrorException groupRuleNotFound(String additional){
+        return new CustomErrorException("小组规则不存在!" + (additional == null ? "" : additional));
+    }
+
+    public static CustomErrorException groupAlreadyMaxSize(String additional){
+        return new CustomErrorException("当前小组已经满员!" + (additional == null ? "" : additional));
+    }
 }