Otimização das Classes de Tarefas para Pedidos de Loteria e de Pontuação

A análise do código original revelou uma duplicação significativa entre duas classes de tarefas independentes: uma para sincronizar pedidos de loteria e outra para pedidos de pontos. Cada classe implementava lógica semelhante para reucperar informações do vendedor (streamer), definir o proprietário e atualizar o departamento do time.

Para eliminar essa duplicação e facilitar a manutenção, foi extraída uma classe abstrata que encapsula toda a lógica comum. As impleemntações concretas agora herdam dessa classe base e fornecem apenas as partes específicas de cada tipo de pedido.

Classe Abstrata Base

package cn.iocoder.yudao.module.trade.job.order;

import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;
import cn.iocoder.yudao.module.system.api.seller.SellerApi;
import cn.iocoder.yudao.module.system.api.seller.dto.SellerRespDTO;
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.system.enums.PostTypeEnum;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public abstract class AbstractSyncSellerStreamerJob<T> implements JobHandler {

    @Resource
    private SellerApi sellerApi;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private TenantApi tenantApi;
    @Resource
    private PostApi postApi;

    protected abstract String getJobName();

    @Override
    public String execute(String param) throws Exception {
        log.info("Início da execução: {}", getJobName());

        long dias = 5; // Valor padrão: 5 dias
        List<Long> filtroTenants = null;

        if (param != null && !param.trim().isEmpty()) {
            Map<String, String> parametros = Arrays.stream(param.split("\\|"))
                    .map(p -> p.split("=", 2))
                    .filter(par -> par.length == 2)
                    .collect(Collectors.toMap(p -> p[0], p -> p[1]));

            if (parametros.containsKey("dias")) {
                dias = Long.parseLong(parametros.get("dias"));
            }
            if (parametros.containsKey("tenantId")) {
                filtroTenants = Arrays.stream(parametros.get("tenantId").split(","))
                        .map(Long::parseLong)
                        .collect(Collectors.toList());
            }
        }

        List<TenantDTO> listaTenants = tenantApi.getTenantMallList();

        if (filtroTenants != null && !filtroTenants.isEmpty()) {
            listaTenants = listaTenants.stream()
                    .filter(t -> filtroTenants.contains(t.getId()))
                    .collect(Collectors.toList());
        }

        LocalDateTime limiteData = LocalDateTime.now().minusDays(dias);

        for (TenantDTO tenant : listaTenants) {
            DynamicDataSourceContextHolder.push(tenant.getWebsite());
            try {
                processarTarefa(tenant.getId(), limiteData);
            } finally {
                DynamicDataSourceContextHolder.poll();
            }
        }

        log.info("Fim da execução: {}", getJobName());
        return null;
    }

    private void processarTarefa(Long tenantId, LocalDateTime limiteData) {
        List<T> registros = buscarRegistros(limiteData);
        if (CollectionUtils.isAnyEmpty(registros)) {
            log.info("Nenhum registro pendente para o tenant {}", tenantId);
            return;
        }

        Set<Long> uids = extrairUids(registros);
        List<MemberUserRespDTO> membros = memberUserApi.getUserList(uids);
        Map<Long, MemberUserRespDTO> mapaMembros = CollectionUtils.convertMap(membros, MemberUserRespDTO::getId);

        for (T registro : registros) {
            try {
                sincronizarRegistro(registro, tenantId, mapaMembros);
            } catch (Exception e) {
                log.error("Falha ao processar registro {}", obterIdRegistro(registro), e);
            }
        }
    }

    protected void sincronizarRegistro(T registro, Long tenantId, Map<Long, MemberUserRespDTO> mapaMembros) {
        Long uid = obterUidRegistro(registro);
        MemberUserRespDTO membro = mapaMembros.get(uid);

        if (membro == null || membro.getSellerUid() == null) {
            log.warn("Registro {}: usuário {} não encontrado ou sem sellerUid. Pulando sincronização.", obterIdRegistro(registro), uid);
            return;
        }

        SellerRespDTO vendedor = sellerApi.getSellerByUid(tenantId, membro.getSellerUid());
        if (vendedor == null || vendedor.getId() == null) {
            log.warn("Registro {}: dados do vendedor não encontrados para sellerUid: {}", obterIdRegistro(registro), membro.getSellerUid());
            return;
        }

        Long timeId = null;
        Long idFuncionario = obterIdFuncionario(registro);

        if (idFuncionario != null) {
            AdminUserRespDTO usuarioProprietario = adminUserApi.getUserByTidAndTenantId(idFuncionario, tenantId);

            if (usuarioProprietario != null && usuarioProprietario.getDeptId() != null) {
                definirDepartamentoRegistro(registro, usuarioProprietario.getDeptId());

                List<PostRespDTO> cargos = postApi.getPostList(usuarioProprietario.getPostIds(), tenantId);
                if (!CollectionUtils.isAnyEmpty(cargos)) {
                    for (PostRespDTO cargo : cargos) {
                        if (Objects.equals(cargo.getType(), PostTypeEnum.TUANDUI.getType())) {
                            timeId = cargo.getId();
                            break;
                        }
                    }
                }
            }
        }

        definirCamposSincronizacao(registro, vendedor, timeId);
        atualizarRegistro(registro);
    }

    protected abstract List<T> buscarRegistros(LocalDateTime limiteData);
    protected abstract Set<Long> extrairUids(List<T> registros);
    protected abstract Long obterIdRegistro(T registro);
    protected abstract Long obterUidRegistro(T registro);
    protected abstract Long obterIdFuncionario(T registro);
    protected abstract void definirDepartamentoRegistro(T registro, Long departamentoId);
    protected abstract void definirCamposSincronizacao(T registro, SellerRespDTO vendedor, Long timeId);
    protected abstract void atualizarRegistro(T registro);
}

Implementação para Pedidos de Pontuação

package cn.iocoder.yudao.module.trade.job.order;

import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.api.seller.dto.SellerRespDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.integral.StoreIntegralOrderDO;
import cn.iocoder.yudao.module.trade.dal.mysql.integral.StoreIntegralOrderMapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;

@Component
public class SyncIntegralOrderSellerStreamerId extends AbstractSyncSellerStreamerJob<StoreIntegralOrderDO> {

    @Resource
    private StoreIntegralOrderMapper orderMapper;

    @Override
    protected String getJobName() {
        return this.getClass().getSimpleName();
    }

    @Override
    protected List<StoreIntegralOrderDO> buscarRegistros(LocalDateTime limiteData) {
        return orderMapper.selectNullDeptIdOrSellerId(limiteData);
    }

    @Override
    protected Set<Long> extrairUids(List<StoreIntegralOrderDO> registros) {
        return CollectionUtils.convertSet(registros, StoreIntegralOrderDO::getUid);
    }

    @Override
    protected Long obterUidRegistro(StoreIntegralOrderDO registro) {
        return registro.getUid();
    }

    @Override
    protected Long obterIdRegistro(StoreIntegralOrderDO registro) {
        return registro.getId();
    }

    @Override
    protected Long obterIdFuncionario(StoreIntegralOrderDO registro) {
        return registro.getEmployeeId();
    }

    @Override
    protected void definirDepartamentoRegistro(StoreIntegralOrderDO registro, Long departamentoId) {
        registro.setDeptId(departamentoId);
    }

    @Override
    protected void definirCamposSincronizacao(StoreIntegralOrderDO registro, SellerRespDTO vendedor, Long timeId) {
        registro.setStreamerId(vendedor.getStreamerId());
        registro.setTeamId(timeId);
        registro.setSellerId(vendedor.getSellerTid());
        registro.setSellerUid(vendedor.getMiniAppId());
    }

    @Override
    protected void atualizarRegistro(StoreIntegralOrderDO registro) {
        LambdaUpdateWrapper<StoreIntegralOrderDO> atualizador = new LambdaUpdateWrapper<StoreIntegralOrderDO>()
                .eq(StoreIntegralOrderDO::getId, registro.getId())
                .set(StoreIntegralOrderDO::getStreamerId, registro.getStreamerId())
                .set(StoreIntegralOrderDO::getTeamId, registro.getTeamId())
                .set(StoreIntegralOrderDO::getSellerId, registro.getSellerId())
                .set(StoreIntegralOrderDO::getSellerUid, registro.getSellerUid())
                .set(StoreIntegralOrderDO::getDeptId, registro.getDeptId());
        orderMapper.update(atualizador);
    }
}

Implementação para Pedidos de Loteria

package cn.iocoder.yudao.module.trade.job.order;

import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.api.seller.dto.SellerRespDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.luck.LuckLotteryRecordDO;
import cn.iocoder.yudao.module.trade.dal.mysql.luck.LuckLotteryRecordMapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;

@Component
public class SyncLuckLotteryOrderSellerStreamerId extends AbstractSyncSellerStreamerJob<LuckLotteryRecordDO> {

    @Resource
    private LuckLotteryRecordMapper lotteryMapper;

    @Override
    protected String getJobName() {
        return this.getClass().getSimpleName();
    }

    @Override
    protected List<LuckLotteryRecordDO> buscarRegistros(LocalDateTime limiteData) {
        return lotteryMapper.selectNullDeptIdOrSellerId(limiteData);
    }

    @Override
    protected Set<Long> extrairUids(List<LuckLotteryRecordDO> registros) {
        return CollectionUtils.convertSet(registros, LuckLotteryRecordDO::getUid);
    }

    @Override
    protected Long obterIdRegistro(LuckLotteryRecordDO registro) {
        return registro.getId();
    }

    @Override
    protected Long obterUidRegistro(LuckLotteryRecordDO registro) {
        return registro.getUid();
    }

    @Override
    protected Long obterIdFuncionario(LuckLotteryRecordDO registro) {
        return registro.getEmployeeId();
    }

    @Override
    protected void definirDepartamentoRegistro(LuckLotteryRecordDO registro, Long departamentoId) {
        registro.setDeptId(departamentoId);
    }

    @Override
    protected void definirCamposSincronizacao(LuckLotteryRecordDO registro, SellerRespDTO vendedor, Long timeId) {
        registro.setTeamId(timeId)
                .setSellerId(vendedor.getSellerTid())
                .setSellerUid(vendedor.getMiniAppId())
                .setStreamerId(vendedor.getStreamerId());
    }

    @Override
    protected void atualizarRegistro(LuckLotteryRecordDO registro) {
        LambdaUpdateWrapper<LuckLotteryRecordDO> atualizador = new LambdaUpdateWrapper<LuckLotteryRecordDO>()
                .eq(LuckLotteryRecordDO::getId, registro.getId())
                .set(LuckLotteryRecordDO::getStreamerId, registro.getStreamerId())
                .set(LuckLotteryRecordDO::getSellerId, registro.getSellerId())
                .set(LuckLotteryRecordDO::getSellerUid, registro.getSellerUid())
                .set(LuckLotteryRecordDO::getTeamId, registro.getTeamId())
                .set(LuckLotteryRecordDO::getDeptId, registro.getDeptId());
        lotteryMapper.update(atualizador);
    }
}

Tags: Spring Boot java MyBatis-Plus Quartz Sincronização de Dados

Publicado em 6-2 18:02 por Thomas