# DrawerCountController.java

    package cloud.wconnect.frontoffice.controller.drawercount;

    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.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.frontoffice.cons.drawercount.DrawerCountCons;
    import cloud.wconnect.frontoffice.cons.drawercount.DrawerCountErrorI18nConstant;
    import cloud.wconnect.frontoffice.cons.drawercount.DrawerCountFieldI18nConstant;
    import cloud.wconnect.frontoffice.entity.drawercount.ShiftDrawerComment;
    import cloud.wconnect.frontoffice.entity.drawercount.ShiftDrawerCount;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerCommentDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerCountDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerGSADTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerWitnessDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.ShiftDrawerDTO;
    import cloud.wconnect.frontoffice.service.drawercount.IShiftDrawerCommentService;
    import cloud.wconnect.frontoffice.service.drawercount.IShiftDrawerCountService;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    import jakarta.validation.Valid;

    import java.time.ZonedDateTime;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 前端控制器
     * </p>
     *
     * @author lichen
     * @since 2023-03-27
     */
    @RestController
    @RequestMapping("/shift-drawer-count")
    public class DrawerCountController {

        @Autowired
        private IShiftDrawerCountService shiftDrawerCountService;
        @Autowired
        private IShiftDrawerCommentService shiftDrawerCommentService;
        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取 Drawer Count 数据列表
         *
         * @param property   资产标识
         * @param targetDate 目标日期 yyyyMMdd
         * @return ShiftDrawerCountDTO
         * @author zc
         * @date 2023/8/25
         */
        @ApiId(190)
        @GetMapping
        public List<ShiftDrawerCountDTO> list(String property, String targetDate) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            String timezone = propertyDTO.getTimezone();

            List<Long> queryDates = Lists.newArrayListWithExpectedSize(2);

            //按照逗号分隔成两个日期
            String[] targetDates = StringUtils.split(targetDate, ",");

            //当只有一个日期时，校验日期不能大于当前日期
            if (targetDates.length == 1) {
                //目标日期 转换成 时间戳
                Long firstDate = DateTimeUtils.dateStrToEpochSecond(targetDates[0], BaseConstant.DATE_FORMAT_YYYYMMDD, timezone);
                //校验日期必须小于当前日期
                checkParmDateLessThanNowDate(firstDate, timezone);
                queryDates.add(firstDate);
            } else if (targetDates.length == 2) {
                //目标日期转换成时间戳
                Long firstDate = DateTimeUtils.dateStrToEpochSecond(targetDates[0], BaseConstant.DATE_FORMAT_YYYYMMDD, timezone);
                Long secondDate = DateTimeUtils.dateStrToEpochSecond(targetDates[1], BaseConstant.DATE_FORMAT_YYYYMMDD, timezone);
                //校验日期必须小于当前日期
                checkParmDateLessThanNowDate(firstDate, timezone);
                checkParmDateLessThanNowDate(secondDate, timezone);
                //目标日期转换成ZoneDateTime
                ZonedDateTime firstZonedDateTime = DateTimeUtils.toZonedDateTime(firstDate, timezone);
                ZonedDateTime secondZonedDateTime = DateTimeUtils.toZonedDateTime(secondDate, timezone);
                //检验两个日期必须是相邻日期
                if (!firstZonedDateTime.plusDays(1).isEqual(secondZonedDateTime)) {
                    throw new BadRequestException(CommonErrorI18nConstant.SELECT_DATE_LIMIT,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate(2),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The targetDate must be adjacent and first targetDate < second targetDate;targetDate：" + targetDate);
                }
                queryDates.add(firstDate);
                queryDates.add(secondDate);
            } else {
                throw new BadRequestException(CommonErrorI18nConstant.LENGTH,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("targetDateList", 1, 2),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "the targetDate value is illegal;targetDate：" + targetDate);
            }
            //查询数据
            return shiftDrawerCountService.list(propertyDTO, queryDates);
        }

        /**
         * 编辑 Drawer Count
         *
         * @param id             Drawer Count 主键id
         * @param property       资产标识
         * @param type           1:in; 2:out
         * @param shiftDrawerDTO 编辑的数据DTO
         * @return ShiftDrawerGSADTO
         * @author zc
         * @date 2023/8/26
         */
        @ApiId(191)
        @PostMapping("/{id:\\d+}")
        public ShiftDrawerGSADTO update(@PathVariable int id, String property,
                                        int type, @RequestBody @Valid ShiftDrawerDTO shiftDrawerDTO) {
            ArrayList<Integer> typeList = Lists.newArrayList(DrawerCountCons.DRAWER_IN, DrawerCountCons.DRAWER_OUT);
            if (!typeList.contains(type)) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("type", typeList),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The type must be in [1: drawerIn; 2: drawerOut]");
            }
            int propertyId = sessionContext.getPropertyId(property);
            int loginUserId = sessionContext.getLoginUserId();
            String username = sessionContext.getUsername();

            checkExistAndBelongProperty(id, propertyId);
            // 修改数据
            return shiftDrawerCountService.update(id, propertyId, type, shiftDrawerDTO, loginUserId, username);
        }

        /**
         * 审核Drawer Count
         *
         * @param id       Drawer Count 主键id
         * @param property 资产标识
         * @return cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerWitnessDTO
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(192)
        @PutMapping("/audit/{id:\\d+}")
        public ShiftDrawerWitnessDTO audit(@PathVariable int id, String property) {
            int propertyId = sessionContext.getPropertyId(property);
            int loginUserId = sessionContext.getLoginUserId();
            String username = sessionContext.getUsername();

            ShiftDrawerCount shiftDrawerCount = checkExistAndBelongProperty(id, propertyId);
            //drawerOut 未提交时不可审计
            if (StringUtils.isEmpty(shiftDrawerCount.getDrawerOut())) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(DrawerCountFieldI18nConstant.HANDOVER),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The current drawerOut has not been submitted.");
            }
            //已审计的数据不可再次审计
            if (shiftDrawerCount.getWitnessAuditTime() > BaseConstant.ZERO) {
                throw new BadRequestException(DrawerCountErrorI18nConstant.DRAWER_COUNT_ALREADY_AUDIT,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The current drawer has been audit.");
            }
            //审核
            return shiftDrawerCountService.audit(id, propertyId, loginUserId, username);
        }

        /**
         * 保存Comment
         *
         * @param id         Drawer Count 主键id
         * @param property   资产标识
         * @param commentDTO 备注DTO
         * @return cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerCommentDTO
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(193)
        @PostMapping("/comment/{id:\\d+}")
        public ShiftDrawerCommentDTO saveComment(@PathVariable int id, String property,
                                                 @Valid @RequestBody ShiftDrawerCommentDTO commentDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            int loginUserId = sessionContext.getLoginUserId();
            String username = sessionContext.getUsername();

            checkExistAndBelongProperty(id, propertyId);
            commentDTO.setPropertyId(propertyId);
            //保存comment
            return shiftDrawerCountService.saveComment(id, commentDTO, loginUserId, username);
        }

        /**
         * 删除Comment
         *
         * @param id        Drawer Count 主键id
         * @param property  资产标识
         * @param commentId 备注id
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(194)
        @DeleteMapping("/comment/{id:\\d+}")
        public Map deleteComment(@PathVariable int id, String property,
                                 @RequestParam(name = "commentId") Integer commentId) {
            int propertyId = sessionContext.getPropertyId(property);
            checkExistAndBelongProperty(id, propertyId);
            //校验数据是否存在
            ShiftDrawerComment shiftDrawerComment = shiftDrawerCommentService.getById(commentId);
            if (Objects.isNull(shiftDrawerComment)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "commentId=" + commentId + " ,Drawer Count Comment Data not found");
            }
            //校验是否有权限删除
            Integer createUid = shiftDrawerComment.getCreateUid();
            if (!createUid.equals(sessionContext.getLoginUserId())) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "Only the creator can delete");
            }
            //删除comment
            shiftDrawerCommentService.removeById(commentId);
            return Collections.emptyMap();
        }

        /**
         * 查询Drawer Count的Comment列表
         *
         * @param id       Drawer Count 主键id
         * @param property 资产标识
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerCommentDTO>
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(195)
        @GetMapping("/comment/{id:\\d+}")
        public List<ShiftDrawerCommentDTO> listComment(@PathVariable int id, String property) {
            int propertyId = sessionContext.getPropertyId(property);
            Integer tenantId = sessionContext.getTenantSymbolBO().getId();
            checkExistAndBelongProperty(id, propertyId);

            List<ShiftDrawerComment> commentList = shiftDrawerCommentService.listByDrawerCountId(propertyId, id);
            if (CollectionUtils.isEmpty(commentList)) {
                return Collections.emptyList();
            }
            //查询用户信息
            List<Integer> userIds = commentList.stream()
                    .map(ShiftDrawerComment::getCreateUid)
                    .filter(uid -> uid != BaseConstant.ZERO)
                    .distinct().collect(Collectors.toList());
            Map<Integer, String> userId2Name = userInternalService.getNameMapByIds(tenantId, userIds);
            //List<ShiftDrawerComment> -> List<ShiftDrawerCommentDTO>
            return beanMapper.mapList(commentList, ShiftDrawerCommentDTO.class, (comment, dto) -> {
                dto.setUserName(userId2Name.get(dto.getCreateUid()));
            });
        }

        /**
         * 校验数据是否存在；校验数据是否归属于资产
         *
         * @param id         主键id
         * @param propertyId 资产id
         * @return cloud.wconnect.frontoffice.entity.drawercount.ShiftDrawerCount
         * @author lichen
         * @date 2025/8/26
         */
        private ShiftDrawerCount checkExistAndBelongProperty(Integer id, Integer propertyId) {
            ShiftDrawerCount byId = shiftDrawerCountService.getById(id);
            if (Objects.isNull(byId)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "id=" + id + " ,Drawer Count Data not found");
            }
            if (byId.getPropertyId().compareTo(propertyId) != 0) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "data has no access");
            }
            return byId;
        }

        /**
         * 校验参数日期必须小于当前日期
         *
         * @param parmDate 参数日期
         * @param timeZone 时区
         * @return void
         * @author lichen
         * @date 2023/5/24
         */
        private void checkParmDateLessThanNowDate(Long parmDate, String timeZone) {
            ZonedDateTime zonedDateTime = DateTimeUtils.dateNow(timeZone);
            String currentDateStr = DateTimeUtils.parseZonedDateTimeToStr(zonedDateTime, BaseConstant.DATE_FORMAT_YYYYMMDD);
            //获取指定时区下的当前日期
            Long nowDate = zonedDateTime.toEpochSecond();
            //target不能大于当前时间
            if (parmDate > nowDate) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_LESS_THAN_ZERO,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("targetDate", currentDateStr),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "the targetDate cannot be operated at the current time");
            }
        }
    }