package com.ycwl.basic.service.task.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.facebody.model.v20191230.AddFaceEntityRequest;
import com.aliyuncs.facebody.model.v20191230.AddFaceRequest;
import com.aliyuncs.facebody.model.v20191230.AddFaceResponse;
import com.aliyuncs.facebody.model.v20191230.CreateFaceDbRequest;
import com.aliyuncs.facebody.model.v20191230.DeleteFaceDbRequest;
import com.aliyuncs.facebody.model.v20191230.DeleteFaceEntityRequest;
import com.aliyuncs.facebody.model.v20191230.ListFaceDbsRequest;
import com.aliyuncs.facebody.model.v20191230.ListFaceDbsResponse;
import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesRequest;
import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesResponse;
import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse;
import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.config.FaceDetectConfig;
import com.ycwl.basic.constant.FaceConstant;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.mapper.FaceDetectLogMapper;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.ScenicMapper;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.faceDetectLog.entity.FaceDetectLog;
import com.ycwl.basic.model.pc.faceDetectLog.resp.MatchLocalRecord;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.model.task.resp.AddFaceSampleRespVo;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.ratelimiter.FixedRateLimiter;
import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.task.TaskFaceService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.utils.StorageUtil;
import com.ycwl.basic.task.VideoPieceGetter;
import com.ycwl.basic.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.ycwl.basic.constant.FaceConstant.USER_FACE_DB_NAME;

@Slf4j
@Service
public class TaskFaceServiceImpl implements TaskFaceService {
    @Autowired
    private FaceMapper faceMapper;
    @Autowired
    private FaceSampleMapper faceSampleMapper;
    @Autowired
    private ScenicMapper scenicMapper;
    @Autowired
    private FaceDetectConfig faceDetectConfig;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private FaceDetectLogMapper logMapper;
    @Autowired
    private FaceRepository faceRepository;
    @Autowired
    private SourceMapper sourceMapper;
    @Autowired
    private OrderBiz orderBiz;
    // 阿里云人脸检索限制qps=2
    private final FixedRateLimiter addEntityLimiter = new FixedRateLimiter(600, TimeUnit.MILLISECONDS);
    // 阿里云人脸检索限制qps=5
    private final FixedRateLimiter searchFaceLimiter = new FixedRateLimiter(200, TimeUnit.MILLISECONDS);
    private final FixedRateLimiter deleteDbLimiter = new FixedRateLimiter(600, TimeUnit.MILLISECONDS);
    private final FixedRateLimiter deleteEntityLimiter = new FixedRateLimiter(600, TimeUnit.MILLISECONDS);
    private final ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 1024, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1024));

    @Autowired
    private ScenicRepository scenicRepository;
    @Autowired
    private DeviceRepository deviceRepository;

    private IAcsClient getClient() {
        DefaultProfile profile = DefaultProfile.getProfile(
                faceDetectConfig.getRegion(),faceDetectConfig.getAccessKeyId(), faceDetectConfig.getAccessKeySecret());
        IAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    @Override
    public SearchFaceRespVo searchFace(Long faceId) {
        FaceRespVO faceRespVO = faceMapper.getById(faceId);
        if (faceRespVO == null) {
            SearchFaceRespVo vo = new SearchFaceRespVo();
            vo.setSampleListIds(new ArrayList<>());
            return vo;
        }
        Long scenicId = faceRespVO.getScenicId();
        SearchFaceRespVo respVo = searchFace(scenicId.toString(), faceRespVO.getFaceUrl(), "系统定时任务检索");
        if (respVo != null) {
            FaceEntity faceEntity = new FaceEntity();
            faceEntity.setId(faceId);
            faceEntity.setMatchResult(respVo.getSearchResultJson());
            faceEntity.setFirstMatchRate(BigDecimal.valueOf(respVo.getFirstMatchRate()));
            faceEntity.setScore(respVo.getScore());
            faceEntity.setMatchSampleIds(StringUtils.join(respVo.getSampleListIds(), ","));
            faceMapper.update(faceEntity);
            faceRepository.clearFaceCache(faceId);
            List<Long> sampleListIds = respVo.getSampleListIds();
            if (sampleListIds != null && !sampleListIds.isEmpty()) {// 匹配原片：照片
                List<SourceEntity> sourceEntities = sourceMapper.listBySampleIds(sampleListIds);
                List<MemberSourceEntity> memberSourceEntityList = sourceEntities.stream().map(sourceEntity -> {
                    MemberSourceEntity memberSourceEntity = new MemberSourceEntity();
                    memberSourceEntity.setScenicId(scenicId);
                    memberSourceEntity.setFaceId(faceEntity.getId());
                    memberSourceEntity.setMemberId(faceRespVO.getMemberId());
                    memberSourceEntity.setSourceId(sourceEntity.getId());
                    memberSourceEntity.setType(sourceEntity.getType());
                    IsBuyRespVO isBuy = orderBiz.isBuy(faceRespVO.getMemberId(), scenicId, sourceEntity.getType(), sourceEntity.getId());
                    if (isBuy.isBuy()) { // 如果用户买过
                        memberSourceEntity.setIsBuy(1);
                    } else if (isBuy.isFree()) { // 全免费逻辑
                        memberSourceEntity.setIsBuy(1);
                    } else {
                        memberSourceEntity.setIsBuy(0);
                    }
                    return memberSourceEntity;
                }).collect(Collectors.toList());
                if (!memberSourceEntityList.isEmpty()) {
                    sourceMapper.addRelations(memberSourceEntityList);
                    VideoPieceGetter.Task task = new VideoPieceGetter.Task();
                    task.faceId = faceEntity.getId();
                    task.faceSampleIds = sampleListIds;
                    task.memberId = faceRespVO.getMemberId();
                    VideoPieceGetter.addTask(task);
                }
            }
        }
        return respVo;
    }

    @Override
    public SearchFaceRespVo searchFace(Long scenicId, String faceUrl) {
        return searchFace(scenicId.toString(), faceUrl, "预留字段");
    }

    @Override
    public SearchFaceRespVo searchFace(String dbName, String faceUrl) {
        return searchFace(dbName, faceUrl, "预留字段");
    }

    @Override
    public SearchFaceRespVo searchFace(String dbName, String faceUrl, String reason) {
        assureFaceDB(dbName);
        IAcsClient client = getClient();
        SearchFaceRequest request = new SearchFaceRequest();
        request.setDbName(dbName);
        request.setImageUrl(faceUrl);
        request.setLimit(100);
//        request.setQualityScoreThreshold(60f);
        FaceDetectLog log = FaceDetectLog.quickCreate(reason, request);
        try {
            searchFaceLimiter.acquire();
        } catch (InterruptedException ignored) {
        }
        float threshold = 0.525F;
        if (StringUtils.isNumeric(dbName)) {
            ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(Long.valueOf(dbName));
            if (scenicConfig != null) {
                if (scenicConfig.getFaceScoreThreshold() != null) {
                    threshold = scenicConfig.getFaceScoreThreshold() / 100F;
                }
            }
        }
        final float _threshold = threshold;
        try {
            SearchFaceResponse response = client.getAcsResponse(request);
            log.fillResponse(response);
            List<SearchFaceResponse.Data.MatchListItem> matchList = response.getData().getMatchList();
            if (matchList.isEmpty()) {
                return null;
            }
            SearchFaceRespVo respVo = new SearchFaceRespVo();
            respVo.setScore(matchList.get(0).getQualitieScore());
            respVo.setSearchResultJson(JSON.toJSONString(matchList.get(0)));
            if (matchList.get(0).getFaceItems().isEmpty()) {
                return respVo;
            }
            List<SearchFaceResponse.Data.MatchListItem.FaceItemsItem> records = matchList.get(0).getFaceItems();
            log.setMatchRawRecord(records);
            List<Long> faceSampleIds = records.stream()
                    .filter(record -> record.getScore() > _threshold)
                    .map(SearchFaceResponse.Data.MatchListItem.FaceItemsItem::getExtraData)
                    .filter(StringUtils::isNumeric)
                    .map(Long::valueOf)
                    .collect(Collectors.toList());
            respVo.setFirstMatchRate(matchList.get(0).getFaceItems().get(0).getScore());
            respVo.setSampleListIds(faceSampleIds);
            return respVo;
        } catch (Exception e) {
            log.setMatchRawResult("识别错误，错误为：["+e.getLocalizedMessage()+"]");
            throw new BaseException(e.getMessage());
        } finally {
            executor.execute(() -> {
                if (log.getMatchRawRecord() != null) {
                    List<MatchLocalRecord> collect = log.getMatchRawRecord().parallelStream().map(item -> {
                        MatchLocalRecord record = new MatchLocalRecord();
                        record.setIdStr(item.getExtraData());
                        record.setFaceSampleId(Long.parseLong(item.getExtraData()));
                        if (StringUtils.isNumeric(item.getDbName())) {
                            FaceSampleEntity faceSample = faceRepository.getFaceSample(record.getFaceSampleId());
                            if (faceSample != null) {
                                DeviceEntity device = deviceRepository.getDevice(faceSample.getDeviceId());
                                if (device != null) {
                                    record.setDeviceName(device.getName());
                                }
                                record.setMatched(item.getScore() > _threshold);
                                record.setFaceUrl(faceSample.getFaceUrl());
                                record.setShotDate(faceSample.getCreateAt());
                            }
                        } else {
                            record.setFaceUrl(getFaceUrl(record.getFaceSampleId()));
                        }
                        record.setScore(item.getScore());
                        record.setConfidence(item.getConfidence());
                        return record;
                    }).collect(Collectors.toList());
                    log.setMatchLocalRecord(JSON.toJSONString(collect));
                }
                logMapper.insert(log);
            });
        }
    }

    @Override
    public AddFaceSampleRespVo addFaceSample(Long faceSampleId) {
        FaceSampleRespVO faceSampleRespVO = faceSampleMapper.getById(faceSampleId);
        String entityId = generateEntityId(faceSampleRespVO);
        AddFaceSampleRespVo respVo = addFaceSample(faceSampleRespVO.getScenicId().toString(), entityId, faceSampleRespVO.getFaceUrl(), faceSampleId.toString());
        FaceSampleEntity faceSampleEntity = new FaceSampleEntity();
        faceSampleEntity.setId(faceSampleId);
        if (respVo != null) {
            faceSampleEntity.setScore(respVo.getScore());
        }
        faceSampleEntity.setUpdateAt(new Date());
        faceSampleMapper.update(faceSampleEntity);
        return respVo;
    }

    @Override
    public AddFaceSampleRespVo addFaceSample(String dbName, String entityId, String faceUrl, String extData) {
        assureFaceDB(dbName);
        AddFaceEntityRequest request = new AddFaceEntityRequest();
        request.setDbName(dbName);
        request.setEntityId(entityId);
        IAcsClient client = getClient();
        try {
            addEntityLimiter.acquire();
        } catch (InterruptedException ignored) {
        }
        try {
            client.getAcsResponse(request);
        } catch (ClientException e) {
            log.error("addFaceEntity, {}/{}", dbName, entityId, e);
            return null;
        }
        AddFaceRequest addFaceRequest = new AddFaceRequest();
        addFaceRequest.setDbName(dbName);
        addFaceRequest.setEntityId(entityId);
        addFaceRequest.setImageUrl(faceUrl);
        addFaceRequest.setExtraData(extData);
        AddFaceSampleRespVo respVo = new AddFaceSampleRespVo();
        try {
            AddFaceResponse acsResponse = client.getAcsResponse(addFaceRequest);
            respVo.setScore(acsResponse.getData().getQualitieScore());
            return respVo;
        } catch (ClientException e) {
            log.error("addFace, {}/{}", dbName, entityId, e);
            return null;
        }
    }

    @Override
    public void batchDeleteExpiredFace(Long scenicId) {
        log.info("当前景区{}，开始删除人脸样本", scenicId);
        ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
        Integer sampleStoreDay = scenicConfig.getSampleStoreDay();
        if (sampleStoreDay == null) {
            log.info("当前景区{}，人脸样本保存天数未设置，默认7天", scenicId);
            sampleStoreDay = 7;
        }
        Date endDate = DateUtil.offsetDay(DateUtil.beginOfDay(new Date()), -sampleStoreDay);
        ListFaceEntitiesRequest listFaceEntitiesRequest = new ListFaceEntitiesRequest();

        listFaceEntitiesRequest.setDbName(USER_FACE_DB_NAME + String.valueOf(scenicId));
        listFaceEntitiesRequest.setOrder("asc");
        listFaceEntitiesRequest.setLimit(200);
        try {
            AtomicInteger count = new AtomicInteger(0);
            IAcsClient client = getClient();
            while (true) {
                listFaceEntitiesRequest.setOffset(count.getAndIncrement());
                ListFaceEntitiesResponse listFaceEntitiesResponse = client.getAcsResponse(listFaceEntitiesRequest);
                if (listFaceEntitiesResponse == null || listFaceEntitiesResponse.getData() == null || listFaceEntitiesResponse.getData().getEntities() == null || listFaceEntitiesResponse.getData().getEntities().isEmpty()) {
                    break;
                }
                listFaceEntitiesResponse.getData().getEntities().forEach(entity -> {
                    Date createdDate = new Date(entity.getCreatedAt());
                    if (createdDate.before(endDate)) {
                        deleteFaceSample(entity.getDbName(), entity.getEntityId());
                    } else {
                        count.incrementAndGet();
                        log.info("当前景区{}，人脸样本：{}未过期", scenicId, entity.getEntityId());
                    }
                });
            }
        } catch (Exception ignored) {
        }
        List<DeviceEntity> devices = deviceRepository.getAllDeviceByScenicId(scenicId);
        listFaceEntitiesRequest.setDbName(String.valueOf(scenicId));
        listFaceEntitiesRequest.setOrder("asc");
        devices.forEach(device -> {
            AtomicInteger count = new AtomicInteger(0);
            listFaceEntitiesRequest.setEntityIdPrefix(device.getId() + "_");
            try {
                IAcsClient client = getClient();
                while (true) {
                    listFaceEntitiesRequest.setOffset(count.get());
                    ListFaceEntitiesResponse listFaceEntitiesResponse = client.getAcsResponse(listFaceEntitiesRequest);
                    if (listFaceEntitiesResponse == null || listFaceEntitiesResponse.getData() == null || listFaceEntitiesResponse.getData().getEntities() == null || listFaceEntitiesResponse.getData().getEntities().isEmpty()) {
                        break;
                    }
                    listFaceEntitiesResponse.getData().getEntities().forEach(entity -> {
                        String entityId = entity.getEntityId();
                        String[] split = entityId.split("_");
                        if (split.length != 2) {
                            return;
                        }
                        String deviceId = split[0];
                        if (StringUtils.isBlank(deviceId)) {
                            return;
                        }
                        String dateString = split[1];
                        if (StringUtils.isBlank(dateString)) {
                            return;
                        }
                        if (DateUtils.parse(dateString, DATE_FORMAT).before(endDate)) {
                            deleteFaceSample(entity.getDbName(), entity.getEntityId());
                        } else {
                            count.incrementAndGet();
                            log.info("当前景区{}，人脸样本：{}未过期", scenicId, entity.getEntityId());
                        }
                    });
                }
            } catch (Exception ignored) {
            }
        });
        List<FaceSampleEntity> faceSampleList = faceSampleMapper.listEntityBeforeDate(scenicId, endDate);
        if (faceSampleList.isEmpty()) {
            log.info("当前景区{}，人脸样本为空", scenicId);
            return;
        }
        faceSampleList.forEach(faceSample -> {
            boolean success = deleteFaceSample(String.valueOf(scenicId), generateEntityId(faceSample));
            if (success) {
                log.info("当前景区{}，人脸样本ID{}，删除成功", scenicId, faceSample.getId());
                faceSampleMapper.deleteById(faceSample.getId());
            } else {
                log.info("当前景区{}，人脸样本ID{}，删除失败", scenicId, faceSample.getId());
            }
        });
    }

    @Override
    public void deleteFaceDB(String dbName) {
        ListFaceEntitiesRequest request = new ListFaceEntitiesRequest();
        request.setDbName(dbName);
        request.setLimit(200);
        IAcsClient client = getClient();
        try {
            while (true) {
                ListFaceEntitiesResponse response = client.getAcsResponse(request);
                if (response.getData().getTotalCount() == 0) {
                    break;
                }
                response.getData().getEntities().forEach(entity -> {
                    DeleteFaceEntityRequest deleteFaceEntityRequest = new DeleteFaceEntityRequest();
                    deleteFaceEntityRequest.setDbName(entity.getDbName());
                    deleteFaceEntityRequest.setEntityId(entity.getEntityId());
                    try {
                        deleteEntityLimiter.acquire();
                    } catch (InterruptedException ignored) {
                    }
                    try {
                        client.getAcsResponse(deleteFaceEntityRequest);
                    } catch (ClientException e) {
                        log.error("删除人脸数据失败！", e);
                    }
                });
            }
            DeleteFaceDbRequest deleteFaceDbRequest = new DeleteFaceDbRequest();
            deleteFaceDbRequest.setName(dbName);
            try {
                deleteDbLimiter.acquire();
            } catch (InterruptedException ignored) {
            }
            client.getAcsResponse(deleteFaceDbRequest);
            removeFaceDBCache(dbName);
        } catch (ClientException e) {
            log.error("删除人脸数据库失败！", e);
        }
    }

    @Override
    public void createFaceDB(String dbName) {
        try {
            CreateFaceDbRequest request = new CreateFaceDbRequest();
            request.setName(dbName);
            IAcsClient client = getClient();
            client.getAcsResponse(request);
            addFaceDBCache(dbName);
        } catch (ClientException e) {
            log.error("阿里云添加人脸数据库失败！", e);
        }
    }

    @Override
    public void assureFaceDB(String dbName) {
        if (redisTemplate.hasKey(FaceConstant.FACE_DB_NAME_PFX + dbName)) {
            return;
        }
        try {
            long offset = 0;
            List<ListFaceDbsResponse.Data.DbListItem> dbList = new ArrayList<>();
            IAcsClient client = getClient();
            while (true) {
                ListFaceDbsRequest request = new ListFaceDbsRequest();
                request.setLimit(200L);
                request.setOffset(offset);
                ListFaceDbsResponse response = client.getAcsResponse(request);
                List<ListFaceDbsResponse.Data.DbListItem> list = response.getData().getDbList();
                if (list == null || list.isEmpty()) {
                    break;
                } else {
                    dbList.addAll(list);
                    offset += list.size();
                }
            }
            boolean mismatch;
            if (dbList.isEmpty()) {
                clearFaceDBCache();
                mismatch = true;
            } else {
                mismatch = dbList.stream().peek(item -> {
                    redisTemplate.opsForValue().set(FaceConstant.FACE_DB_NAME_PFX + dbName, "1");
                }).noneMatch(db -> db.getName().equals(dbName));
            }
            if (mismatch) {
                createFaceDB(dbName);
            }
        } catch (ClientException e) {
            log.error("阿里云确保人脸数据库失败！", e);
        }
    }

    @Override
    public String uploadFile(MultipartFile file, Long userId) {
        if (file.isEmpty()) {
            throw new RuntimeException("文件不存在！");
        }
        String originalFilename = file.getOriginalFilename();
        //获取文件名后缀
        String suffix = originalFilename.split("\\.")[1];
        if ("Jpeg".equals(suffix)) {
            suffix = "jpg";
        }
        //文件储存路径
        String filePath = StorageUtil.joinPath("user-faces", DateUtils.format(new Date(),"yyyy-MM-dd"));
        // 生成文件名
        String fileName= userId + "." + suffix;
        IStorageAdapter adapter = StorageFactory.use("faces");
        try {
            return adapter.uploadFile(file.getInputStream(), filePath, fileName);
        } catch (IOException e) {
            log.error("文件上传失败！", e);
            return null;
        }
    }

    @Override
    public boolean deleteFaceSample(String dbName, String entityId) {
        DeleteFaceEntityRequest deleteFaceEntityRequest = new DeleteFaceEntityRequest();
        deleteFaceEntityRequest.setDbName(dbName);
        deleteFaceEntityRequest.setEntityId(entityId);
        try {
            deleteEntityLimiter.acquire();
            log.info("删除{}人脸实体：{}", dbName, entityId);
            IAcsClient client = getClient();
            client.getAcsResponse(deleteFaceEntityRequest);
            return true;
        } catch (Exception e) {
            log.error("删除人脸样本失败！", e);
            return false;
        }
    }

    public void addFaceDBCache(String dbName) {
        redisTemplate.opsForValue().set(FaceConstant.FACE_DB_NAME_PFX + dbName, "1");
    }

    public Boolean removeFaceDBCache(String dbName) {
        return redisTemplate.delete(FaceConstant.FACE_DB_NAME_PFX + dbName);
    }

    public Boolean clearFaceDBCache() {
        return redisTemplate.delete(FaceConstant.FACE_DB_NAME_PFX + "*");
    }


    private static final String DATE_FORMAT="yyyyMMddHHmmss";

    public static String generateEntityId(FaceSampleEntity entity) {
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        Random random = new Random();
        int randomNumber = random.nextInt(900) + 100;
        return entity.getDeviceId().toString() + "_" + sdf.format(entity.getCreateAt()) + randomNumber;
    }
    public static String generateEntityId(FaceSampleRespVO entity) {
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        Random random = new Random();
        int randomNumber = random.nextInt(900) + 100;
        return entity.getDeviceId().toString() + "_" + sdf.format(entity.getCreateAt()) + randomNumber;
    }

    public String getFaceUrl(Long faceId) {
        if (faceId == null) {
            return null;
        }
        if (redisTemplate.hasKey(FaceConstant.FACE_USER_URL_PFX + faceId)) {
            return redisTemplate.opsForValue().get(FaceConstant.FACE_USER_URL_PFX + faceId);
        }
        FaceRespVO faceRespVO = faceMapper.getById(faceId);
        if (faceRespVO == null) {
            return null;
        }
        String faceUrl = faceRespVO.getFaceUrl();
        if (StringUtils.isNotBlank(faceUrl)) {
            addFaceUrlCache(faceId, faceUrl);
        }
        return faceUrl;
    }
    public void addFaceUrlCache(Long faceId, String faceUrl) {
        if (faceId == null) {
            return;
        }
        if (StringUtils.isBlank(faceUrl)) {
            return;
        }
        redisTemplate.opsForValue().set(FaceConstant.FACE_USER_URL_PFX + faceId, faceUrl, 3, TimeUnit.DAYS);
    }
}
