فهرست منبع

获取锁修改

haogh 1 سال پیش
والد
کامیت
8d2aa5b675

+ 23 - 0
src/main/java/com/qmth/exam/reserve/bean/teaching/TeachingReq.java

@@ -0,0 +1,23 @@
+package com.qmth.exam.reserve.bean.teaching;
+
+import com.qmth.exam.reserve.bean.IModel;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class TeachingReq implements IModel {
+
+    private static final long serialVersionUID = 2203050783813389251L;
+
+    @ApiModelProperty("教学点名称")
+    private String name;
+
+    @ApiModelProperty("教学点名称")
+    private String code;
+
+    @ApiModelProperty("状态")
+    private Boolean enable;
+}

+ 33 - 0
src/main/java/com/qmth/exam/reserve/bean/teaching/TeachingVO.java

@@ -0,0 +1,33 @@
+package com.qmth.exam.reserve.bean.teaching;
+
+import com.qmth.exam.reserve.bean.IModel;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class TeachingVO implements IModel {
+
+    private static final long serialVersionUID = 1426430044029724693L;
+
+    @ApiModelProperty("ID")
+    private Long id;
+
+    @ApiModelProperty("代码")
+    private String code;
+
+    @ApiModelProperty("教学点名称")
+    private String name;
+
+    @ApiModelProperty("城市")
+    private String cityName;
+
+    @ApiModelProperty("总容量")
+    private Integer capacity;
+
+    @ApiModelProperty("状态")
+    private Boolean enable;
+
+}

+ 35 - 0
src/main/java/com/qmth/exam/reserve/controller/admin/TeachingController.java

@@ -0,0 +1,35 @@
+package com.qmth.exam.reserve.controller.admin;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.core.collection.PageResult;
+import com.qmth.exam.reserve.bean.teaching.TeachingReq;
+import com.qmth.exam.reserve.bean.teaching.TeachingVO;
+import com.qmth.exam.reserve.controller.BaseController;
+import com.qmth.exam.reserve.service.CategoryService;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "教学点管理")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/admin/teaching")
+@Aac(strict = false, auth = true)
+public class TeachingController extends BaseController {
+
+    @Autowired
+    private CategoryService categoryService;
+
+    @ApiOperation(value = "教学点管理分页")
+    @PostMapping(value = "/page")
+    public PageResult<TeachingVO> page(@RequestBody TeachingReq req) {
+        return categoryService.page(req);
+    }
+
+}

+ 5 - 0
src/main/java/com/qmth/exam/reserve/dao/StudentDao.java

@@ -1,8 +1,13 @@
 package com.qmth.exam.reserve.dao;
 
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.qmth.exam.reserve.entity.StudentEntity;
 
 public interface StudentDao extends BaseMapper<StudentEntity> {
 
+    List<StudentEntity> listNoFinishStudent(@Param("taskId") Long taskId, @Param("cancel") Boolean cancel);
 }

+ 7 - 2
src/main/java/com/qmth/exam/reserve/service/CategoryService.java

@@ -1,17 +1,22 @@
 package com.qmth.exam.reserve.service;
 
+import java.util.List;
+
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.boot.core.collection.PageResult;
 import com.qmth.exam.reserve.bean.category.CategoryInfo;
 import com.qmth.exam.reserve.bean.login.LoginUser;
 import com.qmth.exam.reserve.bean.stdapply.CategoryVO;
+import com.qmth.exam.reserve.bean.teaching.TeachingReq;
+import com.qmth.exam.reserve.bean.teaching.TeachingVO;
 import com.qmth.exam.reserve.entity.CategoryEntity;
 
-import java.util.List;
-
 public interface CategoryService extends IService<CategoryEntity> {
 
     List<CategoryVO> listTeaching(LoginUser user);
 
     List<CategoryInfo> getCategoryTreeForStudent(LoginUser loginUser);
 
+    PageResult<TeachingVO> page(TeachingReq req);
+
 }

+ 4 - 0
src/main/java/com/qmth/exam/reserve/service/StudentService.java

@@ -1,5 +1,7 @@
 package com.qmth.exam.reserve.service;
 
+import java.util.List;
+
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.qmth.exam.reserve.bean.student.StudentInfo;
 import com.qmth.exam.reserve.bean.student.WechatBindReq;
@@ -19,4 +21,6 @@ public interface StudentService extends IService<StudentEntity> {
 
     void unbindWechatByStudentId(Long studentId);
 
+    List<StudentEntity> listNoFinishStudent(Long taskId, Boolean cancel);
+
 }

+ 18 - 8
src/main/java/com/qmth/exam/reserve/service/impl/CategoryServiceImpl.java

@@ -1,26 +1,30 @@
 package com.qmth.exam.reserve.service.impl;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.boot.core.collection.PageResult;
 import com.qmth.boot.core.exception.StatusException;
 import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
 import com.qmth.exam.reserve.bean.category.CategoryInfo;
 import com.qmth.exam.reserve.bean.login.LoginUser;
 import com.qmth.exam.reserve.bean.stdapply.CategoryVO;
+import com.qmth.exam.reserve.bean.teaching.TeachingReq;
+import com.qmth.exam.reserve.bean.teaching.TeachingVO;
 import com.qmth.exam.reserve.dao.CategoryDao;
 import com.qmth.exam.reserve.entity.CategoryEntity;
 import com.qmth.exam.reserve.enums.CategoryLevel;
 import com.qmth.exam.reserve.enums.Role;
 import com.qmth.exam.reserve.service.ApplyTaskService;
 import com.qmth.exam.reserve.service.CategoryService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
 
 @Service
 public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
@@ -100,7 +104,8 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
         wrapper.eq(CategoryEntity::getEnable, Boolean.TRUE);
         List<CategoryEntity> categoryList = baseMapper.selectList(wrapper);
 
-        Map<Long, List<CategoryEntity>> categoryMaps = categoryList.stream().collect(Collectors.groupingBy(CategoryEntity::getParentId));
+        Map<Long, List<CategoryEntity>> categoryMaps = categoryList.stream()
+                .collect(Collectors.groupingBy(CategoryEntity::getParentId));
         for (CategoryEntity curEntity : categoryList) {
             if (curEntity.getLevel() > 1) {
                 continue;
@@ -168,4 +173,9 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
         return fillParentCategory(info);
     }
 
+    @Override
+    public PageResult<TeachingVO> page(TeachingReq req) {
+        return null;
+    }
+
 }

+ 59 - 39
src/main/java/com/qmth/exam/reserve/service/impl/StudentApplyServiceImpl.java

@@ -12,7 +12,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
@@ -30,12 +30,12 @@ import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.boot.core.collection.PageResult;
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
 import com.qmth.boot.core.exception.StatusException;
 import com.qmth.boot.tools.excel.ExcelReader;
 import com.qmth.boot.tools.excel.enums.ExcelType;
 import com.qmth.boot.tools.excel.model.DataMap;
 import com.qmth.boot.tools.io.ZipWriter;
-import com.qmth.boot.tools.uuid.FastUUID;
 import com.qmth.exam.reserve.bean.login.LoginUser;
 import com.qmth.exam.reserve.bean.stdapply.AgentAndTimeVO;
 import com.qmth.exam.reserve.bean.stdapply.MaterialTitleInfo;
@@ -105,6 +105,9 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
     @Autowired
     private MaterialGenerateService materialService;
 
+    @Autowired
+    private ConcurrentService concurrentService;
+
     @Override
     public PageResult<StudentApplyVO> page(StudentApplyReq req) {
         IPage<StudentApplyVO> iPage = this.baseMapper
@@ -454,16 +457,19 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
     @Override
     public void autoAssign(Long taskId) {
         checkAfterOpenTime();
+        Lock lock = concurrentService.getLock(CacheConstants.LOCK_AUTO_APPLY);
         try {
-            boolean isSuccess = redisClient.tryLock(CacheConstants.LOCK_AUTO_APPLY, FastUUID.get(), TIMEOUT,
-                    TimeUnit.MINUTES);
-            if (!isSuccess) {
+            if (!lock.tryLock()) {
+                log.warn("获取锁失败,不允许同时执行自动分配!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
                 throw new StatusException("其他老师正在执行自动分配,请不要重复执行!");
             }
             // 1、未完成预约的考生
-            LambdaQueryWrapper<StudentEntity> lm = new LambdaQueryWrapper<>();
-            lm.eq(StudentEntity::getApplyFinished, Boolean.FALSE);
-            List<StudentEntity> studentList = studentService.list(lm);
+            /*
+             * LambdaQueryWrapper<StudentEntity> lm = new LambdaQueryWrapper<>();
+             * lm.eq(StudentEntity::getApplyFinished, Boolean.FALSE); List<StudentEntity>
+             * studentList = studentService.list(lm);
+             */
+            List<StudentEntity> studentList = studentService.listNoFinishStudent(taskId, Boolean.FALSE);
             Map<Long, List<StudentEntity>> map = studentList.stream()
                     .collect(Collectors.groupingBy(StudentEntity::getCategoryId));
             // 2、考位是否充足
@@ -490,7 +496,7 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
             log.error(e.getMessage());
             throw new StatusException(e.getMessage());
         } finally {
-            redisClient.delete(CacheConstants.LOCK_AUTO_APPLY);
+            lock.unlock();
         }
 
     }
@@ -500,6 +506,8 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
         int num = 0;
         for (Iterator<StudentEntity> iterator = teachingStudentList.iterator(); iterator.hasNext();) {
             StudentEntity student = iterator.next();
+            String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, student.getId());
+            Lock studentApplyLock = concurrentService.getLock(studentApplyLockKey);
             if (num >= remainNum)
                 break;
             List<StudentApplyEntity> studentApplyList = listStudentApply(student.getId(), Boolean.FALSE);
@@ -510,7 +518,11 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
                 studentApply.setExamSiteId(siteId);
                 studentApply.setCancel(Boolean.FALSE);
                 studentApply.setTimePeriodId(timeId);
-                baseMapper.insert(studentApply);
+                if (studentApplyLock.tryLock()) {
+                    baseMapper.insert(studentApply);
+                } else {
+                    log.warn("获取锁失败,不允许同一个考生同时操作预约!lockKey:{}", studentApplyLockKey);
+                }
                 num++;
                 if (toApplyNum - (studentApplyList.size() + 1) == 0) {
                     iterator.remove();
@@ -616,42 +628,50 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
             log.info("没有开启预约任务");
             return;
         }
-        boolean isSuccess = redisClient.tryLock(
-                CacheConstants.LOCK_ARRANGE_EXAM + DateUtil.formatShortDateString(new Date()), FastUUID.get(), TIMEOUT,
-                TimeUnit.MINUTES);
+        // boolean isSuccess = redisClient.tryLock(
+        // CacheConstants.LOCK_ARRANGE_EXAM + DateUtil.formatShortDateString(new
+        // Date()), FastUUID.get(), TIMEOUT,
+        // TimeUnit.MINUTES);
+        String autoLayoutLockKey = String.format(CacheConstants.LOCK_ARRANGE_EXAM,
+                DateUtil.formatShortDateString(new Date()));
+        Lock autoLayoutLock = concurrentService.getLock(autoLayoutLockKey);
         try {
-            if (isSuccess) {
-                // 1.根据当前日期,查询不能取消的时段
-                List<TimePeriodEntity> timePeriodList = listTimePeroid(applyTask.getId());
-                List<TimePeriodEntity> noCancelTimePeroidList = listNoCancelApplyTimePeroid(timePeriodList,
-                        applyTask.getAllowApplyCancelDays());
-                if (noCancelTimePeroidList.isEmpty()) {
-                    log.info("当前时间不在取消预约范围内");
-                    return;
-                }
-                // 2.查询考试日期的待排考的考生
-                List<StudentApplyEntity> toBeLayoutStudentList = this.baseMapper.listTimePeriod(
-                        noCancelTimePeroidList.stream().map(item -> item.getId()).collect(Collectors.toList()),
-                        Boolean.FALSE);
-                // 3.开始排考
-                Map<Long, List<StudentApplyEntity>> toBeLayoutStudentMap = toBeLayoutStudentList.stream()
-                        .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId));
-                for (Long examSiteId : toBeLayoutStudentMap.keySet()) {
-                    Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap = toBeLayoutStudentMap.get(examSiteId)
-                            .stream().collect(Collectors.groupingBy(StudentApplyEntity::getTimePeriodId));
-                    List<ExamRoomEntity> roomList = listExamRoom(examSiteId);
-                    if (roomList.isEmpty()) {
-                        throw new StatusException(examSiteId + ":未设置考场");
-                    }
-                    ExamSiteEntity examSite = examSiteService.getById(examSiteId);
-                    layoutStudentByTimePeriod(applyTask.getId(), examSite, roomList, timeLayoutStudentMap);
+            if (!autoLayoutLock.tryLock()) {
+                log.warn("获取锁失败,已有线程在执行排考!lockKey:{}", autoLayoutLock);
+                return;
+            }
+            // 1.根据当前日期,查询不能取消的时段
+            List<TimePeriodEntity> timePeriodList = listTimePeroid(applyTask.getId());
+            List<TimePeriodEntity> noCancelTimePeroidList = listNoCancelApplyTimePeroid(timePeriodList,
+                    applyTask.getAllowApplyCancelDays());
+            if (noCancelTimePeroidList.isEmpty()) {
+                log.info("当前时间不在取消预约范围内");
+                return;
+            }
+            // 2.查询考试日期的待排考的考生
+            List<StudentApplyEntity> toBeLayoutStudentList = this.baseMapper.listTimePeriod(
+                    noCancelTimePeroidList.stream().map(item -> item.getId()).collect(Collectors.toList()),
+                    Boolean.FALSE);
+            // 3.开始排考
+            Map<Long, List<StudentApplyEntity>> toBeLayoutStudentMap = toBeLayoutStudentList.stream()
+                    .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId));
+            for (Long examSiteId : toBeLayoutStudentMap.keySet()) {
+                Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap = toBeLayoutStudentMap.get(examSiteId).stream()
+                        .collect(Collectors.groupingBy(StudentApplyEntity::getTimePeriodId));
+                List<ExamRoomEntity> roomList = listExamRoom(examSiteId);
+                if (roomList.isEmpty()) {
+                    throw new StatusException(examSiteId + ":未设置考场");
                 }
+                ExamSiteEntity examSite = examSiteService.getById(examSiteId);
+                layoutStudentByTimePeriod(applyTask.getId(), examSite, roomList, timeLayoutStudentMap);
             }
         } catch (StatusException e) {
             log.error(e.getMessage());
             e.printStackTrace();
         } finally {
-            redisClient.delete(CacheConstants.LOCK_ARRANGE_EXAM + DateUtil.formatShortDateString(new Date()));
+            // redisClient.delete(CacheConstants.LOCK_ARRANGE_EXAM +
+            // DateUtil.formatShortDateString(new Date()));
+            autoLayoutLock.unlock();
         }
     }
 

+ 14 - 6
src/main/java/com/qmth/exam/reserve/service/impl/StudentServiceImpl.java

@@ -1,5 +1,14 @@
 package com.qmth.exam.reserve.service.impl;
 
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -11,12 +20,6 @@ import com.qmth.exam.reserve.entity.CategoryEntity;
 import com.qmth.exam.reserve.entity.StudentEntity;
 import com.qmth.exam.reserve.service.CategoryService;
 import com.qmth.exam.reserve.service.StudentService;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
 @Service
 public class StudentServiceImpl extends ServiceImpl<StudentDao, StudentEntity> implements StudentService {
@@ -142,4 +145,9 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, StudentEntity> i
         log.info("[WECHAT_UNBIND] studentId:{} openId:{} uid:{}", stu.getId(), stu.getOpenId(), stu.getUid());
     }
 
+    @Override
+    public List<StudentEntity> listNoFinishStudent(Long taskId, Boolean cancel) {
+        return this.baseMapper.listNoFinishStudent(taskId, cancel);
+    }
+
 }

+ 10 - 0
src/main/resources/mapper/StudentMapper.xml

@@ -1,5 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.qmth.exam.reserve.dao.StudentDao">
+	<select id="listNoFinishStudent" resultType="com.qmth.exam.reserve.entity.StudentEntity">
+		SELECT
+		 	s.* 
+		FROM
+			t_student s
+			LEFT JOIN ( SELECT student_id, count( 1 ) nums FROM t_student_apply a WHERE a.cancel =#{cancel}  GROUP BY student_id ) ts ON ts.student_id = s.id 
+		WHERE
+			apply_task_id =#{taskId} 
+			AND s.apply_number - ifnull( ts.nums, 0 )> 0
+	</select>
 
 </mapper>