PubStandBizService.java 14.5 KB
package cn.fw.valhalla.service.bus.pub;

import cn.fw.common.exception.BusinessException;
import cn.fw.common.web.annotation.DisLock;
import cn.fw.common.web.auth.LoginAuthBean;
import cn.fw.erp.sdk.api.result.UserRoleDataRange;
import cn.fw.valhalla.common.constant.RoleCode;
import cn.fw.valhalla.common.utils.DateUtil;
import cn.fw.valhalla.domain.db.pub.PubStandStaffInfo;
import cn.fw.valhalla.domain.dto.PubStandDTO;
import cn.fw.valhalla.domain.enums.FollowTypeEnum;
import cn.fw.valhalla.domain.enums.PubStandRang;
import cn.fw.valhalla.domain.enums.PubStandType;
import cn.fw.valhalla.domain.enums.SettingTypeEnum;
import cn.fw.valhalla.domain.vo.pool.PubStandVO;
import cn.fw.valhalla.rpc.ehr.EhrRpcService;
import cn.fw.valhalla.rpc.erp.UserRoleRpcService;
import cn.fw.valhalla.rpc.erp.dto.UserInfoDTO;
import cn.fw.valhalla.rpc.oop.OopService;
import cn.fw.valhalla.rpc.oop.dto.GroupDTO;
import cn.fw.valhalla.rpc.oop.dto.ShopDTO;
import cn.fw.valhalla.rpc.shirasawa.ShirasawaRpcService;
import cn.fw.valhalla.service.bus.setting.SettingBizService;
import cn.fw.valhalla.service.data.PubStandStaffInfoService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.MonthDay;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import static cn.fw.common.businessvalidator.Validator.BV;

/**
 * PubStandBizService
 *
 * @author : kurisu
 * @version : 1.0
 * @className : PubStandBizService
 * @description : 公共池站岗人员配置
 * @date : 2023-03-10 15:09
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class PubStandBizService {
    private final EhrRpcService ehrRpcService;
    private final OopService oopService;
    private final UserRoleRpcService userRoleRpcService;
    private final PubStandStaffInfoService pubStandStaffInfoService;
    private final ShirasawaRpcService shirasawaRpcService;
    private final StringRedisTemplate redisTemplate;
    private final SettingBizService settingBizService;
    @Value("${spring.cache.custom.global-prefix}:stand:pub")
    @Getter
    private String keyPrefix;

    /**
     * 同步站岗队列
     */
    public void syncQueue() {
        List<GroupDTO> list = oopService.allGroup();
        for (GroupDTO group : list) {
            syncQueue(group.getId());
        }
    }

    /**
     * 同步站岗队列
     *
     * @param groupId
     */
    public void syncQueue(final Long groupId) {
        final String key = generateKey(groupId);
        List<PubStandStaffInfo> staffList = pubStandStaffInfoService.queryLiningStaff(groupId);
        if (CollectionUtils.isEmpty(staffList)) {
            return;
        }
        String[] idArr = staffList.stream().map(PubStandStaffInfo::getId).map(String::valueOf).toArray(String[]::new);
        redisTemplate.delete(key);
        redisTemplate.opsForList().rightPushAll(key, idArr);
        Date ashita = DateUtil.localDateTime2Date(LocalDate.now().plusDays(1).atStartOfDay().plusHours(1L));
        redisTemplate.expireAt(key, ashita);
    }

    /**
     * 查询用户站岗详情
     *
     * @param currentUser
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public PubStandVO standInfo(final LoginAuthBean currentUser) {
        final Long staffId = currentUser.getUserId();
        final Long groupId = currentUser.getGroupId();
        final UserInfoDTO user = ehrRpcService.user(staffId);
        BV.notNull(user, () -> "人员信息读取失败,请稍后重试");
        List<UserRoleDataRange> range = userRoleRpcService.getUserRoleDataRange(staffId, RoleCode.FWGW);
        BV.notNull(range, () -> "非服务顾问无法访问");
        UserRoleDataRange dataRange = range.get(0);
        final Long shopId = dataRange.getRangeValue();
        final PubStandVO standVO = new PubStandVO();

        Optional<PubStandStaffInfo> staffInfo = pubStandStaffInfoService.queryStaffByGroupId(staffId, groupId);
        PubStandStaffInfo info = staffInfo.orElseGet(() -> this.generateStandInfo(user));
        if (Objects.isNull(info.getId())) {
            ShopDTO shop = oopService.shop(shopId);
            BV.notNull(shop, () -> "门店信息读取失败,请稍后重试");
            info.setShopId(shopId)
                    .setAreaCode(shop.getCityBh())
                    .setShopName(shop.getShortName())
                    .setQueueable(Boolean.TRUE);
            pubStandStaffInfoService.save(info);
        }
        fillPubStandVO(standVO, info);
        fillSetting(standVO, groupId);
        standVO.setShopId(shopId);
        return standVO;
    }

    /**
     * 查询用户站岗详情
     *
     * @param currentUser
     * @param standDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo(final LoginAuthBean currentUser, final PubStandDTO standDTO) {
        final Long staffId = currentUser.getUserId();
        final Long groupId = currentUser.getGroupId();
        final UserInfoDTO user = ehrRpcService.user(staffId);
        BV.notNull(user, () -> "人员信息读取失败,请稍后重试");
        PubStandStaffInfo staffInfo = null;
        if (Objects.nonNull(standDTO.getId())) {
            staffInfo = pubStandStaffInfoService.getById(standDTO.getId());
        }
        if (Objects.isNull(staffInfo)) {
            staffInfo = pubStandStaffInfoService.queryStaffByGroupId(staffId, groupId).orElse(null);
        }
        BV.notNull(staffInfo, () -> "修改失败,请刷新后重试");
        PubStandType standType = PubStandType.ofValue(standDTO.getStandType());
        staffInfo.setStandType(standType);
        staffInfo.setStandRang(PubStandRang.ofValue(standDTO.getStandRang()));
        staffInfo.setIdCode(standDTO.getIdCode());
        if (PubStandType.PUB.equals(standType)) {
            staffInfo.setIdCode(null);
        }
        pubStandStaffInfoService.updateById(staffInfo);
    }

    /**
     * 重置状态
     */
    @Transactional(rollbackFor = Exception.class)
    public void reset() {
        pubStandStaffInfoService.update(Wrappers.<PubStandStaffInfo>lambdaUpdate()
                .set(PubStandStaffInfo::getNoInvolved, Boolean.FALSE)
                .set(PubStandStaffInfo::getQueueable, Boolean.TRUE)
                .set(PubStandStaffInfo::getLining, Boolean.FALSE)
                .set(PubStandStaffInfo::getReasonOfNoLining, "")
                .gt(PubStandStaffInfo::getStaffId, 0)
        );
    }

    /**
     * 加入队列
     *
     * @param staffId
     * @param manual  手动站岗
     */
    @DisLock(prefix = "#this.getKeyPrefix()", key = "#staffId", message = "请勿重复站岗")
    @Transactional(rollbackFor = Exception.class)
    public boolean join(final Long staffId, final boolean manual) {
        final UserInfoDTO user = ehrRpcService.user(staffId);
        BV.notNull(user, () -> "人员信息读取失败,请稍后重试");
        List<UserRoleDataRange> range = userRoleRpcService.getUserRoleDataRange(staffId, RoleCode.FWGW);
        if (CollectionUtils.isEmpty(range)) {
            if (manual) {
                throw new BusinessException("非服务顾问无法站岗");
            }
            return false;
        }
        UserRoleDataRange dataRange = range.get(0);
        final Long shopId = dataRange.getRangeValue();
        ShopDTO shop = oopService.shop(shopId);
        BV.notNull(shop, () -> "门店信息读取失败,请稍后重试");
        final String shopName = shop.getShortName();
        final String cityBh = shop.getCityBh();
        final Long groupId = user.getGroupId();
        Optional<PubStandStaffInfo> staffInfo = pubStandStaffInfoService.queryStaffByGroupId(staffId, groupId);
        PubStandStaffInfo info = staffInfo.orElseGet(() -> this.generateStandInfo(user))
                .setShopId(shopId)
                .setAreaCode(cityBh)
                .setShopName(shopName);
        if (manual) {
            info.setNoInvolved(Boolean.FALSE);
        }
        boolean notodo = Boolean.FALSE.equals(shirasawaRpcService.hasOngoingFollow(staffId));
        if (!notodo && Boolean.FALSE.equals(info.getNoInvolved())) {
            info.setLining(Boolean.FALSE);
            info.setQueueable(Boolean.FALSE);
            info.setReasonOfNoLining("跟进待办未完成");
            pubStandStaffInfoService.saveOrUpdate(info);
        } else {
            info.setLining(Boolean.TRUE);
            info.setQueueable(Boolean.TRUE);
            info.setReasonOfNoLining("");
            this.join(info);
        }
        return true;
    }

    /**
     * 退出队列
     *
     * @param userId
     * @param groupId
     */
    @Transactional(rollbackFor = Exception.class)
    public void exitStand(final Long userId, final Long groupId) {
        Optional<PubStandStaffInfo> staffInfo = pubStandStaffInfoService.queryStaffByGroupId(userId, groupId);
        staffInfo.ifPresent(r -> {
            r.setLining(Boolean.FALSE);
            r.setNoInvolved(Boolean.TRUE);
            boolean updated = pubStandStaffInfoService.updateById(r);
            if (updated) {
                CompletableFuture.runAsync(() -> syncQueue(groupId));
            }
        });
    }

    /**
     * 退出队列
     *
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public void endWorkExitStand(final Long userId) {
        final UserInfoDTO user = ehrRpcService.user(userId);
        BV.notNull(user, () -> "人员信息读取失败,请稍后重试");
        Long groupId = user.getGroupId();
        Optional<PubStandStaffInfo> staffInfo = pubStandStaffInfoService.queryStaffByGroupId(userId, groupId);
        staffInfo.ifPresent(r -> {
            final String key = generateKey(groupId);
            r.setLining(Boolean.FALSE);
            pubStandStaffInfoService.updateById(r);
            redisTemplate.opsForList().remove(key, 0, String.valueOf(r.getId()));
        });
    }

    /**
     * 删除人员队列信息
     *
     * @param userId
     * @param shopId
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeStand(final Long userId, Long shopId) {
        ShopDTO shop = oopService.shop(shopId);
        BV.notNull(shop, () -> "门店信息读取失败,请稍后重试");
        Long groupId = shop.getGroupId();
        Optional<PubStandStaffInfo> staffInfo = pubStandStaffInfoService.queryStaffByGroupId(userId, groupId);
        staffInfo.ifPresent(r -> {
            boolean removed = pubStandStaffInfoService.removeById(r.getId());
            if (removed) {
                CompletableFuture.runAsync(() -> syncQueue(groupId));
            }
        });
    }

    /**
     * 加入队列
     *
     * @param info
     */
    private void join(final PubStandStaffInfo info) {
        final String key = generateKey(info.getGroupId());
        BoundListOperations<String, String> ops = redisTemplate.boundListOps(key);

        final boolean lining = Boolean.TRUE.equals(info.getLining());
        final boolean queueable = Boolean.TRUE.equals(info.getQueueable());
        final boolean noInvolved = Boolean.TRUE.equals(info.getNoInvolved());

        final String id = String.valueOf(info.getId());
        if (!queueable || noInvolved) {
            return;
        }
        if (lining) {
            List<String> stringList = Optional.ofNullable(ops.range(0, -1)).orElse(new ArrayList<>());
            boolean isLining = new HashSet<>(stringList).contains(id);
            if (isLining) {
                return;
            }
        }
        pubStandStaffInfoService.saveOrUpdate(info);
        ops.rightPush(id);
        Date ashita = DateUtil.localDateTime2Date(LocalDate.now().plusDays(1).atStartOfDay().plusHours(1L));
        final Boolean hasKey = redisTemplate.hasKey(key);
        if (!Boolean.TRUE.equals(hasKey)) {
            redisTemplate.expireAt(key, ashita);
        }
    }


    private PubStandStaffInfo generateStandInfo(UserInfoDTO infoDTO) {
        return new PubStandStaffInfo()
                .setLining(Boolean.FALSE)
                .setQueueable(Boolean.FALSE)
                .setNoInvolved(Boolean.FALSE)
                .setStandRang(PubStandRang.SHOP)
                .setStandType(PubStandType.PUB)
                .setGroupId(infoDTO.getGroupId())
                .setStaffId(infoDTO.getId())
                .setGroupId(infoDTO.getGroupId())
                .setStaffName(infoDTO.getUserName());
    }


    private String generateKey(final Long groupId) {
        Assert.notNull(groupId, "groupId cannot be null");
        String day = MonthDay.now().toString().replace("-", "");
        return String.format("%s:%s:%s", getKeyPrefix(), day, groupId);
    }

    private void fillSetting(final PubStandVO standVO, final Long groupId) {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            settingBizService.querySettingByType(FollowTypeEnum.OT, SettingTypeEnum.MINIMUM_CLUE_COUNT, groupId, null)
                    .ifPresent(r -> standVO.setInitNum(r.getDetailValue()));
        });
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> settingBizService.querySettingByType(FollowTypeEnum.OT, SettingTypeEnum.MINIMUM_CONVERSION_RATE, groupId, null)
                .ifPresent(r -> {
                    standVO.setRatio(BigDecimal.valueOf(r.getDetailValue()));
                }));
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
            settingBizService.querySettingByType(FollowTypeEnum.OT, SettingTypeEnum.INCREASE_COUNT, groupId, null)
                    .ifPresent(r -> standVO.setIncreaseCount(r.getDetailValue()));
        });

        try {
            CompletableFuture.allOf(future1, future2, future3).get();
        } catch (Exception e) {
            log.error("查询配置失败:", e);
        }
    }

    private void fillPubStandVO(final PubStandVO vo, final PubStandStaffInfo staffInfo) {
        vo.setId(staffInfo.getId());
        vo.setLining(staffInfo.getLining());
        vo.setQueueable(staffInfo.getQueueable());
        vo.setNoInvolved(staffInfo.getNoInvolved());
        vo.setStandType(staffInfo.getStandType());
        vo.setIdCode(staffInfo.getIdCode());
        vo.setStandRang(staffInfo.getStandRang());
        vo.setReasonOfNoLining(staffInfo.getReasonOfNoLining());
    }
}