# ItticketController.java

    package cloud.wconnect.it.controller.itrequest;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.CommonFieldI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.RoleInternalService;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.it.cons.itrequest.ItRequestConstant;
    import cloud.wconnect.it.cons.itrequest.ItRequestStatusEnum;
    import cloud.wconnect.it.entity.itrequest.Itticket;
    import cloud.wconnect.it.entity.itrequest.ItticketCategory;
    import cloud.wconnect.it.entity.itrequest.ItticketComment;
    import cloud.wconnect.it.pojo.bo.itrequest.NotifyBodyBO;
    import cloud.wconnect.it.pojo.dto.itrequest.*;
    import cloud.wconnect.it.pojo.query.itrequest.ItticketQuery;
    import cloud.wconnect.it.service.itrequest.IItticketCategoryService;
    import cloud.wconnect.it.service.itrequest.IItticketCommentService;
    import cloud.wconnect.it.service.itrequest.IItticketPriorityService;
    import cloud.wconnect.it.service.itrequest.IItticketService;
    import cloud.wconnect.it.service.itrequest.notify.ItticketNotifyService;
    import com.google.common.collect.ImmutableMap;
    import jakarta.validation.Valid;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;

    /**
     * <p>
     *  控制层: IT Request
     * </p>
     *
     * @author yyt
     * @since 2023-03-25
     */
    @RestController
    @RequestMapping("/it-request")
    public class ItticketController {

        @Autowired
        private IItticketService itticketService;
        @Autowired
        private IItticketCategoryService itticketCategoryService;
        @Autowired
        private IItticketCommentService itticketCommentService;
        @Autowired
        private IItticketPriorityService itticketPriorityService;
        @Autowired
        private ItticketNotifyService itticketCreateOperation;
        @Autowired
        private ItticketNotifyService itticketUpdateOperation;
        @Autowired
        private ItticketNotifyService itticketUnResolveOperation;
        @Autowired
        private ItticketNotifyService itticketClosedOperation;
        @Autowired
        private ItticketNotifyService itticketAcceptOperation;
        @Autowired
        private ItticketNotifyService itticketDistributionOperation;
        @Autowired
        private ItticketNotifyService itticketResolveOperation;
        @Autowired
        private ItticketNotifyService itticketCommentOperation;
        @Autowired
        private RoleInternalService roleInternalService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 检索列表
         *
         * @param itticketQuery 查询条件
         * @return  itticket
         * @author yyt
         * @date 2023/3/25
         */
        @ApiId(172)
        @GetMapping
        public ListPagedDTO<ItticketDTO> listItticket(@Valid ItticketQuery itticketQuery) {
            return itticketService.listTicketDTO(sessionContext.getPropertyId(itticketQuery.getProperty()), itticketQuery);
        }

        /**
         * 1142 - 获取统计数
         *
         * @param property 资产标识
         * @return 统计数
         * @author yyt
         * @date 2024/4/15
         **/
        @ApiId(1142)
        @GetMapping("/statistics")
        public Map<String, Long> statistics(String property) {
            return ImmutableMap.of("waitProcessingCount", itticketService.statisticsWaitProcessing(sessionContext.getPropertyId(property), sessionContext.getLoginUserId()));
        }

        /**
         * 1143 - 检索统计数据
         *
         * @param query 查询条件
         * @return it
         * @author yyt
         * @date 2024/4/15
         **/
        @ApiId(1143)
        @GetMapping("/statistics/list")
        public ListPagedDTO<ItticketDTO> listStatistics(@Valid ItticketQuery query) {
            return itticketService.listStatisticsDTO(sessionContext.getPropertyId(query.getProperty()), query, sessionContext.getLoginUserId());
        }

        /**
         * 新增、编辑
         *
         * @param id                itticket id
         * @param property          资产标识
         * @param updateItticketDTO itticket 信息
         * @return {}
         * @author yyt
         * @date 2023/3/25
         */
        @ApiId(173)
        @PostMapping("/{id:\\d+}")
        public Map saveItticket(@PathVariable("id") int id, @RequestParam String property,
                                @Valid @RequestBody UpdateItticketDTO updateItticketDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            ItticketCategory category = itticketCategoryService.checkExist(updateItticketDTO.getCategory());
            if (!category.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the itticketCategory;propertyId:" + propertyId + ",itticketCategoryPropertyId:" +
                        category.getPropertyId() + ",itticketCategoryId:" + category.getId());
            }

            itticketPriorityService.checkExist(updateItticketDTO.getPriority());

            int loginUserId = sessionContext.getLoginUserId();

            if (id == 0) {
                Itticket itticket = itticketService.saveItticket(propertyId, updateItticketDTO, loginUserId);

                itticketCreateOperation.notify(new NotifyBodyBO(itticket.getId()));
            } else {
                Itticket itticket = itticketService.checkExist(id);

                checkPropertySame(itticket, propertyId);

                if(itticket.getUserId() != loginUserId){
                    throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                            BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "Only the creator can edit");
                }

                if (itticket.getTicketStatus() == ItRequestStatusEnum.CLOSED.getCode()) {
                    throw new BadRequestException(CommonErrorI18nConstant.CLOSE_CANNOT_HANDLE,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the status is closed and cannot be edited;id:" + id);
                }

                itticketService.updateItticket(itticket, updateItticketDTO, loginUserId);

                itticketUpdateOperation.notify(new NotifyBodyBO(id));
            }

            return Collections.emptyMap();
        }

        /**
         * 校验 itticket property 与 参数 property 一致
         *
         * @param itticket        itticket
         * @param paramPropertyId 参数 property
         * @author yyt
         * @date 2023/3/25
         */
        public void checkPropertySame(Itticket itticket, int paramPropertyId) {
            if (itticket.getPropertyId() != paramPropertyId) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the itticket;propertyId:" + paramPropertyId + ",itticketPropertyId:" +
                        itticket.getPropertyId() + ",itticketId:" + itticket.getId());
            }
        }

        /**
         * 查看详情
         *
         * @param id       itticket id
         * @param property 资产标识
         * @return itticket 信息
         * @author yyt
         * @date 2023/3/27
         */
        @ApiId(174)
        @GetMapping("/{id:\\d+}")
        public ItticketDTO getDetail(@PathVariable("id") int id, @RequestParam String property) {
            Itticket itticket = itticketService.checkExist(id);

            checkPropertySame(itticket, sessionContext.getPropertyId(property));

            return itticketService.getDetailDTO(itticket);
        }

        /**
         * 获取备注列表
         *
         * @param id       itticket id
         * @param property 资产标识
         * @return 备注列表
         * @author yyt
         * @date 2023/3/27
         */
        @ApiId(176)
        @GetMapping("/{id:\\d+}/comment")
        public List<ItticketCommentDTO> listItticketComment(@PathVariable("id") int id, @RequestParam String property) {
            Itticket itticket = itticketService.checkExist(id);

            checkPropertySame(itticket, sessionContext.getPropertyId(property));

            return itticketCommentService.listTicketCommentDTO(sessionContext.getTenantSymbolBO().getId(), id);
        }

        /**
         * 添加备注
         *
         * @param id                 itticket id
         * @param property           资产标识
         * @param itticketCommentDTO 备注信息
         * @return {}
         * @author yyt
         * @date 2023/3/27
         */
        @ApiId(177)
        @PostMapping("/{id:\\d+}/comment")
        public Map saveComment(@PathVariable("id") int id, @RequestParam String property,
                               @Valid @RequestBody ItticketCommentDTO itticketCommentDTO) {
            Itticket itticket = itticketService.checkExist(id);

            int propertyId = sessionContext.getPropertyId(property);
            checkPropertySame(itticket, propertyId);

            ItticketComment itticketComment = itticketCommentService.saveComment(id, propertyId, itticketCommentDTO, sessionContext.getLoginUserId());

            itticketCommentOperation.notify(new NotifyBodyBO(id, itticketComment));

            return Collections.emptyMap();
        }

        /**
         * 删除备注
         *
         * @param id       itticket id
         * @param cid      备注 id
         * @param property 资产标识
         * @return {}
         * @author yyt
         * @date 2023/3/27
         */
        @ApiId(178)
        @DeleteMapping("/{id:\\d+}/comment/{cid:\\d+}")
        public Map deleteComment(@PathVariable("id") int id, @PathVariable("cid") int cid, @RequestParam String property) {
            Itticket itticket = itticketService.checkExist(id);

            int propertyId = sessionContext.getPropertyId(property);
            checkPropertySame(itticket, propertyId);

            ItticketComment comment = itticketCommentService.checkExist(id, cid);
            if (comment.getCreateId() != sessionContext.getLoginUserId()) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "The comment is not created by the current login user;"
                        + "commentId:" + comment.getId() + ",commentCreateUserId:" + comment.getCreateId()
                        + ",loginUserId:" + sessionContext.getLoginUserId());
            }
            if (!comment.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "the property id parameter is different from the property id of the itticketComment;" +
                                "propertyId:" + propertyId + ",itticketCommentPropertyId:" + comment.getPropertyId() +
                                ",itticketId:" + itticket.getId() + ",itticketCommentId:" + comment.getId());
            }
            if (comment.getCommentType() == ItticketComment.TYPE_ITTICKET_ATTACHMENT) {
                throw new BadRequestException(ItRequestConstant.ITTICKET_ATTACHMENT_NOT_DELETABLE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The itticket attachment cannot be deleted;" +
                        "itticketId:" + id + ",itticketCommentId:" + cid);
            }

            itticketCommentService.delete(comment);

            return Collections.emptyMap();
        }

        /**
         * 1144 - 创建人处理 it request
         *
         * @param property           资产标识
         * @param id                 it id
         * @param createdByHandleDTO 处理信息
         * @return {}
         * @author yyt
         * @date 2024/4/15
         **/
        @ApiId(1144)
        @PutMapping("/{id:\\d+}/created-by/handle")
        public Map createdByHandle(String property, @PathVariable int id, @Valid @RequestBody CreatedByHandleDTO createdByHandleDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            Itticket itticket = itticketService.checkExist(id);
            checkPropertySame(itticket, propertyId);

            int loginUserId = sessionContext.getLoginUserId();
            if(loginUserId != itticket.getUserId()){
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "Only the creator can handle");
            }

            handleToBeConfirmed(itticket, createdByHandleDTO, loginUserId);

            return Collections.emptyMap();
        }

        /**
         * 1297 - 处理待确认状态的数据
         *
         * @param property           资产标识
         * @param id                 it id
         * @param createdByHandleDTO 处理信息
         * @return {}
         * @author yyt
         * @date 2024/4/15
         **/
        @ApiId(1297)
        @PutMapping("/{id:\\d+}/to-be-confirmed")
        public Map handleToBeConfirmed(String property, @PathVariable int id, @Valid @RequestBody CreatedByHandleDTO createdByHandleDTO) {
            Integer propertyId = sessionContext.getPropertyId(property);
            Itticket itticket = itticketService.checkExist(id);
            checkPropertySame(itticket, propertyId);

            if (itticket.getTicketStatus() != ItRequestStatusEnum.WAIT_CONFIRM.getCode()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_UPDATE_FAILED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "failed to update the data that maybe removed or updated by others;id:" + itticket.getId());
            }

            handleToBeConfirmed(itticket, createdByHandleDTO, sessionContext.getLoginUserId());

            return Collections.emptyMap();
        }

        /**
         * 处理待确认
         *
         * @param itticket           it request
         * @param createdByHandleDTO 处理信息
         * @param loginUserId        操作人
         * @author yyt
         * @date 2024/10/23
         **/
        private void handleToBeConfirmed(Itticket itticket, CreatedByHandleDTO createdByHandleDTO, int loginUserId) {
            if (createdByHandleDTO.getStatus() == CreatedByHandleDTO.UNSOLVED) {
                if (itticket.getTicketStatus() != ItRequestStatusEnum.WAIT_CONFIRM.getCode()) {
                    throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(ItRequestStatusEnum.WAIT_CONFIRM.getI18nCode(), CommonFieldI18nConstant.UNRESOLVED),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "Only to be confirmed data can unresolved;id:" + itticket.getId());
                }

                // 未解决时，必须填写备注
                if (StringUtils.isBlank(createdByHandleDTO.getComment())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("comment"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the comment must not be blank");
                }

                itticketService.unResolved(itticket, createdByHandleDTO, loginUserId);
                itticketUnResolveOperation.notify(new NotifyBodyBO(itticket.getId()));
            } else {
                itticketService.closed(itticket, createdByHandleDTO, loginUserId);
                itticketClosedOperation.notify(new NotifyBodyBO(itticket.getId()));
            }
        }

        /**
         * 175 - IT 部门处理 it request
         *
         * @param property    资产标识
         * @param id          it id
         * @param itHandleDTO 处理信息
         * @return {}
         * @author yyt
         * @date 2024/4/15
         **/
        @ApiId(175)
        @PutMapping("/{id:\\d+}/it/handle")
        public Map itHandle(String property, @PathVariable int id, @Valid @RequestBody ItHandleDTO itHandleDTO) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            Integer propertyId = propertyDTO.getId();
            Itticket itticket = itticketService.checkExist(id);
            checkPropertySame(itticket, propertyId);

            int loginUserId = sessionContext.getLoginUserId();
            switch (itHandleDTO.getStatus()) {
                case ItHandleDTO.ACCEPT -> {
                    itticketService.accept(itticket, loginUserId);
                    itticketAcceptOperation.notify(new NotifyBodyBO(id));
                }
                case ItHandleDTO.DISTRIBUTION -> {
                    // 分配时，必须填写分配信息
                    AcceptDTO accept = itHandleDTO.getAccept();
                    if (Objects.isNull(accept)) {
                        throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                                i18nMessageUtils.getErrorParamMapNotNeedTranslate("accept"),
                                BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the accept must not be null");
                    }

                    itticketService.distribution(propertyDTO, itticket, itHandleDTO, loginUserId);
                    itticketDistributionOperation.notify(new NotifyBodyBO(id));
                }
                case ItHandleDTO.RESOLVE -> {
                    // 只能解决责任人是自己的数据
                    if (itticket.getDutyUser() != loginUserId) {
                        throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                                BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "No permissions");
                    }

                    itticketService.resolve(itticket, loginUserId);
                    itticketResolveOperation.notify(new NotifyBodyBO(id));
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 1145 - 获取指定 it 部门角色下用户
         *
         * @param property 资产标识
         * @return 角色下用户
         * @author yyt
         * @date 2023/8/24
         */
        @ApiId(1145)
        @GetMapping("/it-handle/role/user")
        public List<ItTargetUserDTO> listApproveUserByRole(String property) {
            return beanMapper.mapList(roleInternalService.getUsers(sessionContext.getTenantSymbolBO().getId(),
                    sessionContext.getPropertyId(property), BaseConstant.IT_ROLE), ItTargetUserDTO.class);
        }

    }