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);
}
}