spring中使用rabbitmq(spring-boot-starter-amqp)

2025-12-12 0 438

记录在spring中如何使用amqp、一些典型的问题该怎么处理。顺带介绍一下rabbitmq中的概念和原理。

github.com/zhouruibest…

前提

创建一个Maven多模块项目,父配置文件pom.xml。其中 spring.boot.version 3.0.2, spring.cloud.version 2022.0.0, spring.cloud.alibaba.version 2022.0.0.0是官方指定的稳定版本组合。子模块可以继承这些配置,无需在各自的pom.xml中重复定义依赖版本。

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.boot.version>3.0.2</spring.boot.version>
    <spring.cloud.version>2022.0.0</spring.cloud.version>
    <spring.cloud.alibaba.version>2022.0.0.0</spring.cloud.alibaba.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>${spring.boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring.cloud.alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

子模块的pom文件不用指定版本了:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <scope>compile</scope>
</dependency>

1. Direct、Topic、Fanout三种交换机的基本使用

使用配置类声明queue、exchange和binding。RabbitAdmin会识别到并在rabbitmq中创建出来。

@Configuration
public class RabbitMQConfig {
    private Logger logger = LoggerFactory.getLogger(RabbitMQConfig.class);

    // 配置Jackson消息转换器
    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    // 配置RabbitTemplate并设置消息转换器
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 设置消息转换器
        rabbitTemplate.setMessageConverter(jsonMessageConverter());

        // 监控消息是否成功到达交换机
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                logger.info(\"消息已成功到达交换机: {}\", correlationData != null ? correlationData.getId() : \"未知ID\");
            } else {
                logger.error(\"消息到达交换机失败,原因: {}\", cause);
            }
        });

        // 用于监控消息路由失败的情况
        rabbitTemplate.setReturnsCallback(returnedMessage -> {
            logger.error(\"消息路由失败: 交换机={}, 路由键={}, 消息={}, 原因={}\", returnedMessage.getExchange(), returnedMessage.getRoutingKey(), returnedMessage.getMessage(), returnedMessage.getReplyText());
        });

        return rabbitTemplate;
    }

    // 配置AsyncRabbitTemplate并设置消息转换器 
    @Bean
    public AsyncRabbitTemplate asyncRabbitTemplate(RabbitTemplate rabbitTemplate) {
        // AsyncRabbitTemplate基于已配置好的RabbitTemplate构建
        // 会自动使用RabbitTemplate中设置的消息转换器
        AsyncRabbitTemplate asyncTemplate = new AsyncRabbitTemplate(rabbitTemplate);
        return asyncTemplate;
    }

    /*
    每一类Exchange的演示都通过单独的配置类来声明
    Tips:1. 配置类及其内部静态类中的@Bean注解,都可以被Spring Boot扫描到
          2. 我们只是声明了这些Queue、Exchange、Binding,RabbitAdmin 初始化的时候会从 spring 容器
             里取出所有的交换器 bean, 队列 bean, Binding Bean然后创建到RabbitMQ中
     */

    /**
     * Direct Exchange 示例的配置类
     */
    public static class DirectExchangeDemoConfiguration {

        @Bean
        public Queue queue0() {
            return new Queue(MessageForDirectExchange.QUEUE_NAME,
                    true, // durable: 是否持久化到磁盘,当 RabbitMQ 重启后,仍然存在
                    false, // exclusive: 是否排它,队列只对它的连接可见
                    false); // autoDelete: 当没有消费者时,自动删除
        }

        @Bean
        public DirectExchange exchange0() {
            return new DirectExchange(MessageForDirectExchange.EXCHANGE_NAME,
                    true,  // durable: 持久化到磁盘,当 RabbitMQ 服务重启后,该交换机会保留
                    false);  // autoDelete: 当最后一个绑定到该交换机的队列/交换机被解绑后,交换机会被自动删除
        }

        @Bean
        public Binding binding0() {
            return BindingBuilder.bind(queue0()).to(exchange0()).with(MessageForDirectExchange.ROUTING_KEY);
        }
    }

    /**
     * Topic Exchange 示例的配置类
     */
    public static class TopicExchangeDemoConfiguration {

        @Bean
        public Queue queue1() {
            return new Queue(MessageForTopicExchange.QUEUE_NAME,
                    true, // durable
                    false, // exclusive
                    false); // autoDelete
        }

        @Bean
        public TopicExchange exchange1() {
            return new TopicExchange(MessageForTopicExchange.EXCHANGE_NAME,
                    true,  // durable
                    false);  // autoDelete
        }

        @Bean
        public Binding binding1() {
            return BindingBuilder.bind(queue1()).to(exchange1()).with(MessageForTopicExchange.ROUTING_KEY);
        }

    }

    /**
     * Fanout Exchange 示例的配置类
     */
    public static class FanoutExchangeDemoConfiguration {

        @Bean
        public Queue queueA() {
            return new Queue(MessageForFanoutExchange.QUEUE_NAMEA,
                    true, // durable
                    false, // exclusive
                    false); // autoDelete
        }

        @Bean
        public Queue queueB() {
            return new Queue(MessageForFanoutExchange.QUEUE_NAMEB,
                    true, // durable
                    false, // exclusive
                    false); // autoDelete
        }

        @Bean
        public FanoutExchange exchange2() {
            return new FanoutExchange(MessageForFanoutExchange.EXCHANGE_NAME,
                    true,  // durable
                    false);  // autoDelete
        }

        @Bean
        public Binding bindingA() {
            return BindingBuilder.bind(queueA()).to(exchange2());
        }

        @Bean
        public Binding bindingB() {
            return BindingBuilder.bind(queueB()).to(exchange2());
        }
    }
}

RabbitTemplateAsyncRabbitTemplate

RabbitTemplate 的应用场景:

  • 同步发送消息
  • 简单消息发送,对性能要求不高
  • 不需要接收方接收消息后、将返回消息写到另一个queue以返回给调用方

RabbitTemplateAsyncRabbitTemplate两者的确认机制相同, 都是通过 ConfirmCallbackReturnsCallback 监控消息是否到达交换机和路由失败的情况。

AsyncRabbitTemplate 的设计初衷是支持异步请求-响应模式(RPC),重点提供了 convertSendAndReceive 方法。如果只是需要异步发送消息而不关心响应,可以直接使用 RabbitTemplateconvertAndSend 方法,并通过异步任务(如 Spring@Async )实现非阻塞.

通过配置类中的静态配置类声明

  • 配置类及其内部静态类中的@Bean注解,都可以被Spring Boot扫描到
  • 我们只是声明了这些QueueExchangeBindingRabbitAdmin(Spring AMQP 提供的核心组件) 初始化的时候会从 spring 容器里取出所有的exchange bean, queue bean, binding bean然后创建到RabbitMQ中。

创建queue的参数

参数 默认值 作用 适用场景
durable false 队列是否持久化到磁盘。如果为 true ,队列元数据会在 RabbitMQ 重启后保留;如果队列绑定的交换机也需要持久化,创建时也要设置 durable=true 需要队列在 RabbitMQ 重启后仍然存在的场景(如重要业务队列)。
exclusive false 队列是否为排他队列。如果为 true ,队列仅对声明它的连接可见,连接关闭后队列会被删除。 临时队列,仅用于单个连接的临时通信(如 RPC 响应队列)。
autoDelete false 队列是否自动删除。如果为 true ,当最后一个消费者断开连接后,队列会被自动删除。 临时队列,不需要长期存在的场景(如临时任务队列)

消息状态管理

  • 未确认的消息:如果消息被消费但未确认,重启后可能会重新入队(取决于 requeue 设置)
  • 已确认的消息:已确认的消息不会在RabbitMQ重启后恢复,因为 RabbitMQ 会删除这些消息
  • 消息 TTL:如果消息设置了 TTL,需确保 TTL 时间足够长,避免在RabbitMQ重启前过期

可能导致消息不可见的情况

  • 消息未持久化:即使队列持久化,如果消息未设置 deliveryMode=2 ,重启后消息内容会丢失
  • 消息已确认:已确认的消息会被 RabbitMQ 删除,重启后不会恢复
  • 消息已过期:如果消息在重启前已过期,且未配置死信队列,消息会被丢弃
  • 队列非持久化:如果队列未设置 durable=true ,重启后队列和消息都会丢失

消息的可见性与状态的关系

消息状态 对可见性的影响 RabbitMQ 重启后的行为
未消费的消息 消息在队列中可见,等待消费者拉取。 如果队列和消息均为持久化,重启后消息仍然可见。
已消费但未确认的消息 消息对当前消费者不可见,但对其他消费者可见(如果启用了 requeue )。 如果队列和消息均为持久化,重启后消息会重新入队(取决于 requeue 设置)。
已消费且已确认的消息 消息从队列中删除,不可见。 重启后消息不会恢复,因为已被确认删除。
已过期的消息 如果消息设置了 TTL 且已过期,会被自动删除或转移到死信队列(如果配置了死信队列)。 重启后不会恢复已过期的消息,除非消息在重启前未过期且队列和消息均为持久化

DirectExchange

待发送的消息

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageForDirectExchange {
    public static final String QUEUE_NAME = \"QUEUE0\";
    public static final String EXCHANGE_NAME = \"EXCHANGE0\";
    public static final String ROUTING_KEY = \"ROUTING_KEY0\";

    private String message;
}

生产者

@Component
@AllArgsConstructor
public class ProducerForDirectExchange {

    private final RabbitTemplate rabbitTemplate;

    public void syncSend(String msg) {
        var message = new MessageForDirectExchange();
        message.setMessage(msg);
        // 同步发送消息
        rabbitTemplate.convertAndSend(
                MessageForDirectExchange.EXCHANGE_NAME, // exchange
                MessageForDirectExchange.ROUTING_KEY, // routingKey
                message); // message
    }

    public void syncSendDefault(String msg) {
        MessageForDirectExchange message = new MessageForDirectExchange();
        message.setMessage(msg);
        // 同步发送消息, 使用默认交换机.默认的交换机连接了每一个队列并使用队列名作为路由键
        rabbitTemplate.convertAndSend(
                MessageForDirectExchange.QUEUE_NAME, // routingKey
                message); // message
    }

    @Async
    public ListenableFuture asyncSend(String msg) {
        try {
            // 发送消息
            this.syncSend(msg);
            this.syncSendDefault(msg + \", 使用默认交换机和路由键\");
            // 返回成功的 Future
            return AsyncResult.forValue(null);
        } catch (Throwable ex) {
            // 返回异常的 Future
            return AsyncResult.forExecutionException(ex);
        }
    }
}
  • syncSend 最通用的使用场景,同步发送一条简单消息,发送到RabbitMQ就算成功
  • asyncSend 异步发送消息场景,在一个单独的线程中发送、不阻塞当前的线程。对结果也不在意,AsyncResult.forValue(null)创建并返回一个已完成状态的Future对象。

消费者

消费者仅仅将消息打印一下:

public class ConsumerForDirectExchange {
    private Logger logger = LoggerFactory.getLogger(ConsumerForDirectExchange.class);

    //@RabbitHandler
    @RabbitListener(queues = MessageForDirectExchange.QUEUE_NAME)
    public void onMessage(Message message) {
        logger.info(\"接收到消息: \" + message);
        logger.info(\"消息头: {}\", message.getMessageProperties().getHeaders());
        // 打印消息体(原始字节流)
        logger.info(\"消息体: {}\", new String(message.getBody()));
        // 打印 content-type
        logger.info(\"content-type: {}\", message.getMessageProperties().getContentType());
    }
}

测试用例

@SpringBootTest(classes = Demo0Application.class)
public class DirectExchangeTest {
    private Logger logger = LoggerFactory.getLogger(DirectExchangeTest.class);

    @Autowired
    private ProducerForDirectExchange producer;

    @Test
    public void testSyncSend() throws InterruptedException {
        int id = (int) (System.currentTimeMillis() / 1000);
        producer.syncSend(\"66666666\");
        logger.info(\"[testSyncSend][发送编号:[{}] 发送成功]\", id);

        // 阻塞等待,保证消费
        new CountDownLatch(1).await();
    }

    @Test
    public void testAsyncSend() throws InterruptedException {
        int id = (int) (System.currentTimeMillis() / 1000);
        producer.asyncSend(\"这是一条direct exchange的消息\").addCallback(new ListenableFutureCallback() {

            @Override
            public void onFailure(Throwable e) {
                logger.info(\"[testASyncSend][发送编号:[{}] 发送异常]]\", id, e);
            }

            @Override
            public void onSuccess(Void aVoid) {
                logger.info(\"[testASyncSend][发送编号:[{}] 发送成功]\", id);
            }

        });
        logger.info(\"[testASyncSend][发送编号:[{}] 调用完成]\", id);

        // 阻塞等待,保证消费
        new CountDownLatch(1).await();
    }
}

TopicExchange

待发送的消息

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageForTopicExchange {
    public static final String QUEUE_NAME = \"QUEUE1\";
    public static final String EXCHANGE_NAME = \"EXCHANGE1\";
    public static final String ROUTING_KEY = \"order.#\";

    private String message;
}

生产者

@Component
@AllArgsConstructor
public class ProducerForTopicExchange {
    private final RabbitTemplate rabbitTemplate;
    private final AsyncRabbitTemplate asyncRabbitTemplate;

    public void syncSend(String msg) {
        var message = new MessageForTopicExchange();
        message.setMessage(msg);
        // 同步发送消息,阻塞式,返回条件为消息发送到交换机
        rabbitTemplate.convertAndSend(
                MessageForTopicExchange.EXCHANGE_NAME, // exchange
                MessageForTopicExchange.ROUTING_KEY, // routingKey
                message); // message
    }

    public RabbitFuture asyncSend(String msg, String correlationId) {
        var message = new MessageForTopicExchange();
        message.setMessage(msg);

        CorrelationData correlationData = new CorrelationData(correlationId);

        MessagePostProcessor messagePostProcessor =  m-> {
            // 设置correlationId到消息属性中
            m.getMessageProperties().setCorrelationId(correlationId);
            // 可以在这里设置其他消息属性,如优先级、过期时间等
            return m;
        };

        // 异步发送消息,非阻塞式,返回条件为消费完成并收到回复
        return asyncRabbitTemplate.convertSendAndReceive(
                MessageForTopicExchange.EXCHANGE_NAME, // exchange
                MessageForTopicExchange.ROUTING_KEY, // routingKey
                message,
                messagePostProcessor); // message
    }
}

消费者

public class ConsumerForTopicExchange {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @RabbitListener(queues = MessageForTopicExchange.QUEUE_NAME)
    public void onMessage(MessageForTopicExchange message, Channel channel, Message amqpMessage) {
        logger.info(\"[onMessage][线程编号:{} 消息内容:{}], start\", Thread.currentThread().getId(), message);
        try {
            Thread.sleep(1000);
            String replyContent = \"消费成功\" + message.getMessage();
            String replyTo = amqpMessage.getMessageProperties().getReplyTo();
            String correlationId = amqpMessage.getMessageProperties().getCorrelationId();
            if (replyTo != null && correlationId != null) {
                logger.info(\"[onMessage][线程编号:{} 消息内容:{}], 准备回复消息:{}, correlationId {}\",
                        Thread.currentThread().getId(), message, replyContent, correlationId);
                rabbitTemplate.convertAndSend(
                        replyTo, // routingKey
                        replyContent, // message
                        msg -> { // postProcessMessage
                            msg.getMessageProperties().setCorrelationId(correlationId);
                            return msg;
                        });
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        logger.info(\"[onMessage][线程编号:{} 消息内容:{}], end\", Thread.currentThread().getId(), message);
    }

}

测试用例

@SpringBootTest(classes = Demo0Application.class)
public class TopicExchangeTest {
    private Logger logger = LoggerFactory.getLogger(TopicExchangeTest.class);

    @Autowired
    private ProducerForTopicExchange producer;

    @Test
    public void testSyncSend() throws InterruptedException {
        int id = (int) (System.currentTimeMillis() / 1000);
        producer.syncSend(\"66666666\");
        logger.info(\"[testSyncSend][发送编号:[{}] 发送成功]\", id);

        // 阻塞等待,保证消费
        new CountDownLatch(1).await();
    }

    @Test
    public void testAsyncSend() throws InterruptedException {
        // 创建唯一的correlationId,它通常作为消息的一部分
        String correlationId = String.valueOf(System.currentTimeMillis() / 1000);

        // 发送消息并获取future对象
        RabbitFuture future = producer.asyncSend(\"777777\", correlationId);


        // 使用正确的correlationId进行日志记录
        future.whenComplete((result, throwable) -> {
            if (throwable != null) {
                logger.error(\"对象消息发送异常[correlationId: \" + correlationId + \"]:\" + throwable.getMessage());
            } else {
                logger.info(\"对象消息发送操作完成[correlationId: \" + correlationId + \"]\");
            }
        });

        // 阻塞等待,保证消费
        new CountDownLatch(1).await();
    }

}

调用 testSyncSend时,输出如下:

[           main] c.dx.demo0.producer.TopicExchangeTest    : [testSyncSend][发送编号:[1759937164] 发送成功]
[ntContainer#3-1] c.d.d.consumer.ConsumerForTopicExchange  : [onMessage][线程编号:42 消息内容:MessageForTopicExchange(message=66666666)], start
[ntContainer#3-1] c.d.d.consumer.ConsumerForTopicExchange  : [onMessage][线程编号:42 消息内容:MessageForTopicExchange(message=66666666)], end

调用 testAsyncSend时,输出如下:

这是RPC通信的场景。

  • replyTo: 指定消费者处理完消息后,应该将响应消息发送到哪个队列。由消息发送方(请求方)设置,供消息接收方(响应方)使用。Spring AMQP会自动生成临时队列并设置为replyTo属性。
  • correlationId用来标志消息的唯一性,通常作为消息体的一部分。
[           main] .l.DirectReplyToMessageListenerContainer : SimpleConsumer [queue=amq.rabbitmq.reply-to, index=0, consumerTag=amq.ctag-K6zYDC8dk5o8UyzQEB8LlQ identity=7bede4ea] started
[ntContainer#3-1] c.d.d.consumer.ConsumerForTopicExchange  : [onMessage][线程编号:42 消息内容:MessageForTopicExchange(message=777777)], start
[ntContainer#3-1] c.d.d.consumer.ConsumerForTopicExchange  : [onMessage][线程编号:42 消息内容:MessageForTopicExchange(message=777777)], 准备回复消息:消费成功777777, correlationId 1759937014
[ntContainer#3-1] c.d.d.consumer.ConsumerForTopicExchange  : [onMessage][线程编号:42 消息内容:MessageForTopicExchange(message=777777)], end
[pool-1-thread-9] c.dx.demo0.producer.TopicExchangeTest    : 对象消息发送操作完成[correlationId: 1759937014]

FanoutExchange

待发送的消息

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageForFanoutExchange {
    public static final String QUEUE_NAMEA = \"QUEUE2A\";
    public static final String QUEUE_NAMEB = \"QUEUE2B\";
    public static final String EXCHANGE_NAME = \"EXCHANGE2\";

    private String message;
}

生产者

@Component
@AllArgsConstructor
public class ProducerForFanoutExchange {
    private final RabbitTemplate rabbitTemplate;

    public void syncSend(String msg) {
        MessageForFanoutExchange message = new MessageForFanoutExchange();
        message.setMessage(msg);
        // 同步发送消息,阻塞式,返回条件为消息发送到交换机
        rabbitTemplate.convertAndSend(
                MessageForFanoutExchange.EXCHANGE_NAME, // exchange
                \"\", // routingKey
                message); // message
    }

}

消费者

消费者有两个:

第一个:

@Component
public class ConsumerAForFanoutExchange {
    private Logger logger = LoggerFactory.getLogger(ConsumerAForFanoutExchange.class);

    @RabbitListener(queues = MessageForFanoutExchange.QUEUE_NAMEA)
    public void onMessage(MessageForFanoutExchange message) {
        logger.info(\"AAAAA 接收到消息: \" + message);
    }
}

第二个:

@Component
public class ConsumerBForFanoutExchange {

    private Logger logger = LoggerFactory.getLogger(ConsumerBForFanoutExchange.class);

    @RabbitListener(queues = MessageForFanoutExchange.QUEUE_NAMEB)
    public void onMessage(MessageForFanoutExchange message) {
        logger.info(\"BBBBB 接收到消息: \" + message);
    }
}

测试用例

@SpringBootTest(classes = Demo0Application.class)
public class FanoutExchangeTest {
    private Logger logger = LoggerFactory.getLogger(FanoutExchangeTest.class);

    @Autowired
    private ProducerForFanoutExchange producer;

    @Test
    public void testSyncSend() throws InterruptedException {
        int id = (int)(System.currentTimeMillis() / 1000);
        producer.syncSend(\"111111111111\");
        logger.info(\"[testSyncSend][发送编号:[{}] 发送成功]\", id);
        new CountDownLatch(1).await();
    }
}

输出,连接到两个队列上的消费者都收到了消息。

[           main] c.dx.demo0.producer.FanoutExchangeTest   : [testSyncSend][发送编号:[1759937942] 发送成功]
[ntContainer#1-1] c.d.d.c.ConsumerBForFanoutExchange       : BBBBB 接收到消息: MessageForFanoutExchange(message=111111111111)
[ntContainer#0-1] c.d.d.c.ConsumerAForFanoutExchange       : AAAAA 接收到消息: MessageForFanoutExchange(message=111111111111)

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 spring中使用rabbitmq(spring-boot-starter-amqp) https://www.zuozi.net/35882.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务