wangliang 3 年 前
コミット
a365dda5a1

+ 10 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/mapper/TCStatisticsMapper.java

@@ -51,4 +51,14 @@ public interface TCStatisticsMapper extends BaseMapper<TCStatistics> {
      * @return
      */
     public List<TCStatisticsDto> findByBatchNoCount(@Param("schoolId") Long schoolId, @Param("batchNo") String batchNo, @Param("orgIds") Set<Long> orgIds);
+
+    /**
+     * 根据batchNo统计信息
+     *
+     * @param schoolId
+     * @param batchNo
+     * @param orgIds
+     * @return
+     */
+    public List<TCStatisticsDto> findByBatchNoCountJoin(@Param("schoolId") Long schoolId, @Param("batchNo") String batchNo, @Param("orgIds") Set<Long> orgIds);
 }

+ 8 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/mapper/TCStatisticsTempMapper.java

@@ -26,4 +26,12 @@ public interface TCStatisticsTempMapper extends BaseMapper<TCStatisticsTemp> {
      */
     public List<TCStatisticsTemp> findByBatchNo(@Param("schoolId") Long schoolId, @Param("batchNo") String batchNo);
 
+    /**
+     * 根据批次号查找印刷计划信息
+     *
+     * @param schoolId
+     * @param batchNo
+     * @return
+     */
+    public List<TCStatisticsTemp> findByBatchNoJoin(@Param("schoolId") Long schoolId, @Param("batchNo") String batchNo);
 }

+ 42 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/TCStatisticsService.java

@@ -6,7 +6,9 @@ import com.qmth.distributed.print.business.bean.dto.TCStatisticsDto;
 import com.qmth.distributed.print.business.entity.TCStatistics;
 import com.qmth.distributed.print.business.enums.StatisticsStatusEnum;
 import com.qmth.teachcloud.common.entity.SysUser;
+import com.qmth.teachcloud.common.util.Result;
 
+import java.sql.ResultSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -53,6 +55,16 @@ public interface TCStatisticsService extends IService<TCStatistics> {
      */
     public List<TCStatisticsDto> findByBatchNoCount(Long schoolId, String batchNo, Set<Long> orgIds);
 
+    /**
+     * 根据batchNo统计信息
+     *
+     * @param schoolId
+     * @param batchNo
+     * @param orgIds
+     * @return
+     */
+    public List<TCStatisticsDto> findByBatchNoCountJoin(Long schoolId, String batchNo, Set<Long> orgIds);
+
     /**
      * 导入关联数据
      *
@@ -68,4 +80,34 @@ public interface TCStatisticsService extends IService<TCStatistics> {
      * @param setCollections
      */
     public void removeImportData(Set<String> courseSet, Set<Long>... setCollections);
+
+    /**
+     * 刷新关联数据
+     *
+     * @param sysUser
+     * @param batchNoSet
+     */
+    public void freshenJoinData(SysUser sysUser, Set<String> batchNoSet);
+
+    /**
+     * 删除旧的关联数据
+     *
+     * @param batchNoSet
+     */
+    public void removeOldJoinData(Set<String> batchNoSet);
+
+    /**
+     * 刷新数据
+     *
+     * @return
+     */
+    public Result freshenData();
+
+    /**
+     * 删除数据
+     *
+     * @param ids
+     * @return
+     */
+    public Result deleteData(List<Long> ids);
 }

+ 9 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/TCStatisticsTempService.java

@@ -28,4 +28,13 @@ public interface TCStatisticsTempService extends IService<TCStatisticsTemp> {
      * @return
      */
     public List<TCStatisticsTemp> findByBatchNo(Long schoolId, String batchNo);
+
+    /**
+     * 根据批次号查找印刷计划信息
+     *
+     * @param schoolId
+     * @param batchNo
+     * @return
+     */
+    public List<TCStatisticsTemp> findByBatchNoJoin(Long schoolId, String batchNo);
 }

+ 134 - 3
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/TCStatisticsServiceImpl.java

@@ -5,21 +5,21 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
+import com.qmth.boot.api.exception.ApiException;
 import com.qmth.distributed.print.business.bean.dto.TCStatisticsDto;
 import com.qmth.distributed.print.business.entity.TCStatistics;
 import com.qmth.distributed.print.business.entity.TCStatisticsTemp;
 import com.qmth.distributed.print.business.enums.StatisticsStatusEnum;
 import com.qmth.distributed.print.business.mapper.TCStatisticsMapper;
-import com.qmth.distributed.print.business.mapper.TCStatisticsTempMapper;
 import com.qmth.distributed.print.business.service.TCStatisticsService;
 import com.qmth.distributed.print.business.service.TCStatisticsTempService;
 import com.qmth.teachcloud.common.contant.SpringContextHolder;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.entity.SysOrg;
 import com.qmth.teachcloud.common.entity.SysUser;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.service.SysOrgService;
-import com.qmth.teachcloud.common.util.JacksonUtil;
-import org.springframework.boot.SpringApplication;
+import com.qmth.teachcloud.common.util.*;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -47,6 +47,9 @@ public class TCStatisticsServiceImpl extends ServiceImpl<TCStatisticsMapper, TCS
     @Resource
     SysOrgService sysOrgService;
 
+    @Resource
+    RedisUtil redisUtil;
+
     /**
      * 查找印刷计划信息
      *
@@ -78,6 +81,19 @@ public class TCStatisticsServiceImpl extends ServiceImpl<TCStatisticsMapper, TCS
         return tcStatisticsMapper.findByBatchNoCount(schoolId, batchNo, orgIds);
     }
 
+    /**
+     * 根据batchNo统计信息
+     *
+     * @param schoolId
+     * @param batchNo
+     * @param orgIds
+     * @return
+     */
+    @Override
+    public List<TCStatisticsDto> findByBatchNoCountJoin(Long schoolId, String batchNo, Set<Long> orgIds) {
+        return tcStatisticsMapper.findByBatchNoCountJoin(schoolId, batchNo, orgIds);
+    }
+
     /**
      * 导入关联数据
      *
@@ -142,4 +158,119 @@ public class TCStatisticsServiceImpl extends ServiceImpl<TCStatisticsMapper, TCS
                 .in(TCStatisticsTemp::getClazzId, setCollections[2]);
         tcStatisticsTempService.remove(tcStatisticsTempQueryWrapper);
     }
+
+    /**
+     * 刷新关联数据
+     *
+     * @param sysUser
+     * @param batchNoSet
+     */
+    @Override
+    @Transactional
+    public void freshenJoinData(SysUser sysUser, Set<String> batchNoSet) {
+        for (String batchNo : batchNoSet) {
+            List<TCStatisticsTemp> tcStatisticsTempList = tcStatisticsTempService.findByBatchNoJoin(sysUser.getSchoolId(), batchNo);
+            if (Objects.nonNull(tcStatisticsTempList) && tcStatisticsTempList.size() > 0) {
+                String batchNoNew = SystemConstant.getUuid();
+                Set<Long> collegeIdSet = new HashSet<>();
+                for (TCStatisticsTemp t : tcStatisticsTempList) {
+                    collegeIdSet.add(t.getCollegeId());
+                    t.insertInfo(sysUser.getId());
+                    t.setBatchNo(batchNoNew);
+                }
+                tcStatisticsTempService.saveBatch(tcStatisticsTempList);
+
+                Set<Long> orgIds = new HashSet<>();
+                for (Long l : collegeIdSet) {
+                    List<SysOrg> sysOrgList = sysOrgService.findByConnectByRootOrgId(l);
+                    Set<Long> orgTempIds = sysOrgList.stream().map(s -> s.getId()).collect(Collectors.toSet());
+                    orgIds.addAll(orgTempIds);
+                }
+                List<TCStatisticsDto> tcStatisticsDtoList = this.findByBatchNoCountJoin(sysUser.getSchoolId(), batchNoNew, orgIds);
+                if (Objects.nonNull(tcStatisticsDtoList) && tcStatisticsDtoList.size() > 0) {
+                    TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
+                    Gson gson = new Gson();
+                    List<TCStatistics> tcStatisticsList = gson.fromJson(JacksonUtil.parseJson(tcStatisticsDtoList), new TypeToken<List<TCStatistics>>() {
+                    }.getType());
+                    for (TCStatistics t : tcStatisticsList) {
+                        t.insertInfo(sysUser.getId());
+                    }
+                    tcStatisticsService.saveBatch(tcStatisticsList);
+                }
+            }
+        }
+    }
+
+    /**
+     * 删除旧的关联数据
+     *
+     * @param batchNoSet
+     */
+    @Override
+    @Transactional
+    public void removeOldJoinData(Set<String> batchNoSet) {
+        QueryWrapper<TCStatisticsTemp> tcStatisticsTempQueryWrapper = new QueryWrapper<>();
+        tcStatisticsTempQueryWrapper.lambda().in(TCStatisticsTemp::getBatchNo, batchNoSet);
+        tcStatisticsTempService.remove(tcStatisticsTempQueryWrapper);
+
+        TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
+        QueryWrapper<TCStatistics> tcStatisticsQueryWrapper = new QueryWrapper<>();
+        tcStatisticsQueryWrapper.lambda().in(TCStatistics::getBatchNo, batchNoSet);
+        tcStatisticsService.remove(tcStatisticsQueryWrapper);
+    }
+
+    /**
+     * 刷新数据
+     *
+     * @return
+     */
+    @Override
+    @Transactional
+    public Result freshenData() {
+        TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        QueryWrapper<TCStatistics> tcStatisticsQueryWrapper = new QueryWrapper<>();
+        tcStatisticsQueryWrapper.select(" DISTINCT batch_no ");
+        List<TCStatistics> tcStatisticsList = tcStatisticsService.list(tcStatisticsQueryWrapper);
+        if (Objects.nonNull(tcStatisticsList) && tcStatisticsList.size() > 0) {
+            Set<String> batchNoSet = tcStatisticsList.stream().map(s -> s.getBatchNo()).collect(Collectors.toSet());
+            try {
+                if (redisUtil.lock(SystemConstant.REDIS_LOCK_BATCH_NO_PREFIX + Math.abs(batchNoSet.toString().hashCode()),
+                        SystemConstant.REDIS_LOCK_BATCH_NO_TIME_OUT)) {
+                    tcStatisticsService.freshenJoinData(sysUser, batchNoSet);
+                    tcStatisticsService.removeOldJoinData(batchNoSet);
+                } else {
+                    throw ExceptionResultEnum.ERROR.exception("正在刷新数据,请稍候再试!");
+                }
+            } catch (Exception e) {
+                log.error("请求出错", e);
+                if (e instanceof ApiException) {
+                    return ResultUtil.error((ApiException) e, e.getMessage());
+                } else {
+                    return ResultUtil.error(e.getMessage());
+                }
+            } finally {
+                redisUtil.releaseLock(SystemConstant.REDIS_LOCK_BATCH_NO_PREFIX + Math.abs(batchNoSet.toString().hashCode()));
+            }
+        }
+        return ResultUtil.ok(true);
+    }
+
+    /**
+     * 删除数据
+     *
+     * @param ids
+     * @return
+     */
+    @Override
+    @Transactional
+    public Result deleteData(List<Long> ids) {
+        TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
+        List<TCStatistics> tcStatisticsList = tcStatisticsService.listByIds(ids);
+        if (Objects.nonNull(tcStatisticsList) && tcStatisticsList.size() > 0) {
+            Set<String> batchNoSet = tcStatisticsList.stream().map(s -> s.getBatchNo()).collect(Collectors.toSet());
+            tcStatisticsService.removeOldJoinData(batchNoSet);
+        }
+        return ResultUtil.ok(true);
+    }
 }

+ 12 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/TCStatisticsServiceTempImpl.java

@@ -34,4 +34,16 @@ public class TCStatisticsServiceTempImpl extends ServiceImpl<TCStatisticsTempMap
     public List<TCStatisticsTemp> findByBatchNo(Long schoolId, String batchNo) {
         return tcStatisticsTempMapper.findByBatchNo(schoolId, batchNo);
     }
+
+    /**
+     * 根据批次号查找印刷计划信息
+     *
+     * @param schoolId
+     * @param batchNo
+     * @return
+     */
+    @Override
+    public List<TCStatisticsTemp> findByBatchNoJoin(Long schoolId, String batchNo) {
+        return tcStatisticsTempMapper.findByBatchNoJoin(schoolId, batchNo);
+    }
 }

+ 1 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/templete/service/impl/TaskLogicServiceImpl.java

@@ -1387,6 +1387,7 @@ public class TaskLogicServiceImpl implements TaskLogicService {
             tcStatisticsTempService.saveBatch(tcStatisticsImportTempList);
             map.put("dataCount", tcStatisticsImportTempList.size());
             tcStatisticsService.importJoinData(sysUser, batchNo);
+            tcStatisticsTempService.removeByIds(tcStatisticsImportTempList.stream().map(s -> s.getId()).collect(Collectors.toSet()));
             return finalExcelList;
         });
         return map;

+ 114 - 1
distributed-print-business/src/main/resources/mapper/TCStatisticsMapper.xml

@@ -20,7 +20,8 @@
         IF(t.examStudentCount = 0,null,t.printPlanName) as printPlanName,
         t.examStudentCount,
         IF(t.examStudentCount = 0,null,t.examStudentCount + (t.drawCount * t.backupCount)) as printSum,
-        IF(t.examStudentCount = 0,null,t.status) as status
+        IF(t.examStudentCount = 0,null,t.status) as status,
+        t.batchNo
         from
         (
         select
@@ -91,6 +92,7 @@
                 and t.teacherName like concat('%',#{teacherName},'%')
             </if>
         </where>
+        order by t.id
     </select>
 
     <select id="findByBatchNoCount" resultType="com.qmth.distributed.print.business.bean.dto.TCStatisticsDto">
@@ -203,4 +205,115 @@
             t.printPlanId,
             t.clazzId
     </select>
+
+    <select id="findByBatchNoCountJoin" resultType="com.qmth.distributed.print.business.bean.dto.TCStatisticsDto">
+        select
+        t.collegeId,
+        t.collegeName,
+        t.courseName,
+        t.courseCode,
+        t.teachingRoomId,
+        t.teachingRoomName,
+        t.teacherName,
+        t.clazzId,
+        t.clazzName,
+        t.paperNumber,
+        t.printPlanId,
+        t.printPlanName,
+        t.batchNo,
+        t.status,
+        case
+        when sum(t.examStudentCount) = 0 then 0
+        else sum(t.examStudentCount) + (t.drawCount * t.backupCount)
+        end printSum,
+        GROUP_CONCAT(distinct t.examDetailCourseId) as examDetailCourseIds,
+        GROUP_CONCAT(distinct t.examTaskDetailId) as examTaskDetailIds
+        from
+        (
+        select
+        distinct
+        tcst.college_id as collegeId,
+        tcst.college_name as collegeName,
+        tcst.course_name as courseName,
+        tcst.course_code as courseCode,
+        tcst.teaching_room_id as teachingRoomId,
+        tcst.teaching_room_name as teachingRoomName,
+        tcst.teacher_name as teacherName,
+        tcst.clazz_id as clazzId,
+        tcst.clazz_name as clazzName,
+        tcst.batch_no as batchNo,
+        et.paper_number as paperNumber,
+        edc.id as examDetailCourseId,
+        etd.id as examTaskDetailId,
+        epp.id as printPlanId,
+        epp.name as printPlanName,
+        IFNULL(etd.draw_count, 0) as drawCount,
+        IFNULL((
+        select
+        count(1)
+        from
+        exam_student es
+        where
+        es.exam_detail_course_id = edc.id
+        and es.clazz_id = tcst.clazz_id
+        and es.school_id = #{schoolId}),0) as examStudentCount,
+        IFNULL(epp.backup_count, 0) as backupCount,
+        case
+        when epp.status is null then 'UN_JOIN'
+        when epp.status = 'PRINT_FINISH'
+        or epp.status = 'END' then 'FINISH'
+        else 'UN_FINISH'
+        end status
+        from
+        t_c_statistics_temp tcst
+        join exam_task et on
+        et.course_code = tcst.course_code
+        and et.teaching_room_id = tcst.teaching_room_id
+        join exam_task_detail etd on
+        etd.exam_task_id = et.id
+        join exam_detail_course edc on
+        edc.course_code = tcst.course_code
+        and edc.paper_number = et.paper_number
+        and edc.school_id = et.school_id
+        join exam_detail ed on
+        ed.id = edc.exam_detail_id
+        join exam_print_plan epp on
+        epp.id = ed.print_plan_id
+        <where>
+            <if test="batchNo != null and batchNo != ''">
+                and tcst.batch_no = #{batchNo}
+            </if>
+            and IF(epp.id is null, true,
+            et.enable = true
+            and etd.enable = true
+            <if test="schoolId != null and schoolId != ''">
+                and et.school_id = #{schoolId}
+            </if>
+            <if test="orgIds != null and orgIds.size() > 0">
+                and epp.org_id IN
+                <foreach collection="orgIds" item="item" index="index" open="(" separator="," close=")">
+                    #{item}
+                </foreach>
+            </if>
+        </where>)) t
+        group by
+        t.collegeId,
+        t.collegeName,
+        t.courseName,
+        t.courseCode,
+        t.teachingRoomId,
+        t.teachingRoomName,
+        t.teacherName,
+        t.clazzId,
+        t.clazzName,
+        t.paperNumber,
+        t.printPlanId,
+        t.printPlanName,
+        t.status,
+        t.drawCount,
+        t.batchNo
+        order by
+        t.printPlanId,
+        t.clazzId
+    </select>
 </mapper>

+ 44 - 0
distributed-print-business/src/main/resources/mapper/TCStatisticsTempMapper.xml

@@ -45,4 +45,48 @@
             </if>)
         </where>
     </select>
+
+    <select id="findByBatchNoJoin" resultType="com.qmth.distributed.print.business.entity.TCStatisticsTemp">
+        select distinct
+        tcst.college_id as collegeId,
+        tcst.college_name as collegeName,
+        tcst.course_name as courseName,
+        tcst.course_code as courseCode,
+        tcst.teaching_room_id as teachingRoomId,
+        tcst.teaching_room_name as teachingRoomName,
+        tcst.teacher_name as teacherName,
+        tcst.clazz_id as clazzId,
+        tcst.clazz_name as clazzName,
+        et.paper_number as paperNumber,
+        epp.id as printPlanId,
+        edc.id as examDetailCourseId,
+        etd.id as examTaskDetailId,
+        'JOIN' as dataSource
+        from
+        t_c_statistics_temp tcst
+        join exam_task et on
+        et.course_code = tcst.course_code
+        and et.teaching_room_id = tcst.teaching_room_id
+        join exam_task_detail etd on
+        etd.exam_task_id = et.id
+        join exam_detail_course edc on
+        edc.course_code = tcst.course_code
+        and edc.paper_number = et.paper_number
+        and edc.school_id = et.school_id
+        join exam_detail ed on
+        ed.id = edc.exam_detail_id
+        join exam_print_plan epp on
+        epp.id = ed.print_plan_id
+        <where>
+            <if test="batchNo != null and batchNo != ''">
+                and tcst.batch_no = #{batchNo}
+            </if>
+            and IF(epp.id is null, true,
+            et.enable = true
+            and etd.enable = true
+            <if test="schoolId != null and schoolId != ''">
+                and et.school_id = #{schoolId}
+            </if>)
+        </where>
+    </select>
 </mapper>

+ 22 - 4
distributed-print/src/main/java/com/qmth/distributed/print/api/TCStatisticsController.java

@@ -8,6 +8,7 @@ import com.qmth.distributed.print.business.enums.StatisticsStatusEnum;
 import com.qmth.distributed.print.business.service.PrintCommonService;
 import com.qmth.distributed.print.business.service.TCStatisticsService;
 import com.qmth.distributed.print.business.templete.execute.AsyncStatisticsDataImportService;
+import com.qmth.teachcloud.common.bean.params.ArraysParams;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.entity.TBTask;
@@ -18,16 +19,16 @@ import com.qmth.teachcloud.common.util.ServletUtil;
 import io.swagger.annotations.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.validation.BindingResult;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.validation.Valid;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
+import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
 
@@ -79,4 +80,21 @@ public class TCStatisticsController {
         SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
         return ResultUtil.ok(tcStatisticsService.list(new Page<>(pageNumber, pageSize), SystemConstant.convertIdToLong(collegeId), SystemConstant.convertIdToLong(teachingRoomId), status, courseName, teacherName, sysUser.getSchoolId(), sysUser.getId()));
     }
+
+    @ApiOperation(value = "命题计划统计-刷新数据")
+    @ApiResponses({@ApiResponse(code = 200, message = "返回信息", response = EditResult.class)})
+    @RequestMapping(value = "/freshen", method = RequestMethod.POST)
+    public Result freshen() {
+        return ResultUtil.ok(tcStatisticsService.freshenData());
+    }
+
+    @ApiOperation(value = "命题计划统计-删除数据")
+    @ApiResponses({@ApiResponse(code = 200, message = "返回信息", response = EditResult.class)})
+    @RequestMapping(value = "/delete", method = RequestMethod.POST)
+    public Result delete(@Valid @RequestBody ArraysParams arraysParams, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
+        }
+        return ResultUtil.ok(tcStatisticsService.deleteData(Arrays.asList(arraysParams.getIds())));
+    }
 }

+ 2 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/contant/SystemConstant.java

@@ -136,6 +136,7 @@ public class SystemConstant {
     public static final String REDIS_LOCK_MQ_PREFIX = "redis:lock:mq:";
     public static final int REDIS_MQ_MAX_RECONSUME = 5;
     public static final String MQ_TOPIC_BUFFER_LIST = "mq:topic:buffer:list";
+    public static final String REDIS_LOCK_BATCH_NO_PREFIX = "redis:lock:batchNo:";//
 
     /**
      * redis lock
@@ -143,6 +144,7 @@ public class SystemConstant {
     public static final int MAX_RETRY_COUNT = 30;
     public static final long REDIS_CACHE_TIME_OUT = 60L;
     public static final long REDIS_LOCK_MQ_TIME_OUT = 60L;
+    public static final long REDIS_LOCK_BATCH_NO_TIME_OUT = 60L * 5;
 
     /**
      * aes相关