告别手动映射:在 Spring Boot 3 中优雅集成 MapStruct
在日常的后端开发中,我们经常需要在不同的对象之间进行数据转换,例如将数据库实体(Entity)转换为数据传输对象(DTO)发送给前端,或者将接收到的 DTO 转换为实体进行业务处理或持久化。手动进行这种对象属性的拷贝工作不仅枯燥乏味,而且容易出错,特别是在对象属性较多时。
MapStruct 是一个 Java 注解处理器,它可以极大地简化这一过程。它通过在编译时生成高性能、类型安全的映射代码来解决对象映射的痛点。与一些基于反射的映射框架(如 ModelMapper、Dozer)不同,MapStruct 生成的代码是普通的 Java 方法调用,因此具有更好的性能和编译时检查,能够提前发现潜在的映射错误。
本文将详细介绍如何在最新的 Spring Boot 3 项目中集成和使用 MapStruct。
为什么选择 MapStruct?
在深入集成之前,我们先快速回顾一下 MapStruct 的主要优势:
- 编译时生成代码: 这是 MapStruct 最核心的特点。它不是在运行时通过反射进行属性查找和复制,而是在编译阶段根据你定义的接口生成具体的实现类。这意味着:
- 高性能: 生成的代码是直接的方法调用,没有反射带来的开销。
- 类型安全: 编译时就能检查映射是否合法,避免运行时错误。
- 易于调试: 你可以看到生成的代码,理解映射过程。
- 减少样板代码: 无需手动编写大量的
getter
和setter
调用来复制属性。 - Spring 集成: MapStruct 可以很容易地生成 Spring Bean,无缝集成到 Spring IoC 容器中。
- 灵活: 支持复杂的映射场景,如嵌套对象、列表、自定义转换逻辑、条件映射等。
在 Spring Boot 3 项目中集成 MapStruct
Spring Boot 3 要求 Java 17 或更高版本。确保你的项目满足这个前提。
集成的步骤主要包括添加依赖、配置构建工具以及编写 Mapper 接口。
步骤 1: 添加 MapStruct 依赖
在你的 Spring Boot 项目的构建文件中,你需要引入 MapStruct 的核心库和注解处理器。
-
Maven (
pom.xml
)<properties><org.mapstruct.version>1.5.5.Final</org.mapstruct.version> <!-- 确保使用最新的稳定版本 --><maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <!-- 确保编译器插件版本与你的JDK兼容且支持注解处理 --> </properties><dependencies><!-- MapStruct Core --><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${org.mapstruct.version}</version></dependency><!-- 其他 Spring Boot Dependencies... --> </dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>${maven-compiler-plugin.version}</version><configuration><source>17</source> <!-- 与你的项目JDK版本一致 --><target>17</target> <!-- 与你的项目JDK版本一致 --><annotationProcessorPaths><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${org.mapstruct.version}</version></path><!-- 如果你使用了 Lombok,这里也需要添加 Lombok 的注解处理器 --><!-- MapStruct 与 Lombok 的集成非常常见 --><!--<path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok-mapstruct-binding</artifactId><version>0.2.0</version> // 这是一个辅助库,帮助 MapStruct 识别 Lombok 生成的方法</path>--></annotationProcessorPaths><!-- 推荐配置: 设置 MapStruct 的组件模型为 spring --><compilerArgs><compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg></compilerArgs></configuration></plugin></plugins> </build>
-
Gradle (
build.gradle
- Groovy DSL)plugins {id 'java'id 'org.springframework.boot' version '3.x.x' // 使用你的 Spring Boot 版本id 'io.spring.dependency-management' version '1.1.x' // 使用你的 Spring Dependency Management 版本 }java {sourceCompatibility = JavaVersion.VERSION_17 // 确保与你的项目JDK版本一致 }repositories {mavenCentral() }ext {mapstructVersion = "1.5.5.Final" // 确保使用最新的稳定版本// lombokVersion = "x.x.x" // 如果使用 Lombok// lombokMapstructBindingVersion = "0.2.0" // 如果使用 Lombok }dependencies {implementation "org.mapstruct:mapstruct:${mapstructVersion}"// 注解处理器依赖 - 注意 scopeannotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"// 如果使用 Lombok// compileOnly "org.projectlombok:lombok:${lombokVersion}"// annotationProcessor "org.projectlombok:lombok:${lombokVersion}"// annotationProcessor "org.projectlombok:lombok-mapstruct-binding:${lombokMapstructBindingVersion}"// 其他 Spring Boot Dependencies... }tasks.withType(JavaCompile) {options.encoding = 'UTF-8'// 推荐配置: 设置 MapStruct 的组件模型为 springoptions.compilerArgs += ['-Amapstruct.defaultComponentModel=spring'] }
-
Gradle (
build.gradle.kts
- Kotlin DSL)plugins {javaid("org.springframework.boot") version "3.x.x" // 使用你的 Spring Boot 版本id("io.spring.dependency-management") version "1.1.x" // 使用你的 Spring Dependency Management 版本 }java {sourceCompatibility = JavaVersion.VERSION_17 // 确保与你的项目JDK版本一致 }repositories {mavenCentral() }val mapstructVersion = "1.5.5.Final" // 确保使用最新的稳定版本 // val lombokVersion = "x.x.x" // 如果使用 Lombok // val lombokMapstructBindingVersion = "0.2.0" // 如果使用 Lombokdependencies {implementation("org.mapstruct:mapstruct:$mapstructVersion")// 注解处理器依赖 - 注意 scopeannotationProcessor("org.mapstruct:mapstruct-processor:$mapstructVersion")// 如果使用 Lombok// compileOnly("org.projectlombok:lombok:$lombokVersion")// annotationProcessor("org.projectlombok:lombok:$lombokVersion")// annotationProcessor("org.projectlombok:lombok-mapstruct-binding:$lombokMapstructBindingVersion")// 其他 Spring Boot Dependencies... }tasks.withType<JavaCompile> {options.encoding = "UTF-8"// 推荐配置: 设置 MapStruct 的组件模型为 springoptions.compilerArgs.add("-Amapstruct.defaultComponentModel=spring") }
关键点说明:
mapstruct-processor
: 这是 MapStruct 的核心,它是一个注解处理器。- 构建工具配置:
maven-compiler-plugin
或 Gradle 的annotationProcessor
必须配置正确,指向mapstruct-processor
依赖。这样,在编译*.java
文件时,编译器会调用 MapStruct 处理器来生成 Mapper 实现类。 - JDK 版本: 确保你的
source
和target
JDK 版本与 Spring Boot 3 的要求一致(Java 17+)。 - Lombok 集成: 如果你的实体或 DTO 使用了 Lombok 生成
getter
/setter
,强烈建议添加lombok-mapstruct-binding
并确保 Lombok 的注解处理器也在列表中。处理器的顺序有时很重要,通常 Lombok 在前。 -Amapstruct.defaultComponentModel=spring
: 这个编译器参数告诉 MapStruct 默认使用spring
作为生成的组件模型。这意味着 MapStruct 会为生成的 Mapper 实现类添加@Component
(或 Spring 可识别的其他注解),从而让 Spring 能够扫描到并将其注册为 Bean,无需你在每个@Mapper
注解中重复指定componentModel = "spring"
。
步骤 2: 定义你的实体类和 DTO
假设我们有以下简单的实体和 DTO:
// src/main/java/.../domain/Product.java
public class Product {private Long id;private String name;private String description;private double price;// 省略 Getters 和 Setters (如果使用 Lombok 则无需手动编写)// public Long getId() { ... }// public void setId(Long id) { ... }// ...
}// src/main/java/.../dto/ProductDto.java
public class ProductDto {private Long productId;private String productName;private String details;private double itemPrice;// 省略 Getters 和 Setters (如果使用 Lombok 则无需手动编写)// public Long getProductId() { ... }// public void setProductId(Long productId) { ... }// ...
}
注意 Product
和 ProductDto
的属性名称不完全匹配。
步骤 3: 创建 Mapper 接口
创建一个 Java 接口,并使用 @Mapper
注解标记它。
// src/main/java/.../mapper/ProductMapper.java
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
// import org.mapstruct.factory.Mappers; // 当使用 componentModel="spring" 时,通常无需手动获取实例@Mapper(componentModel = "spring") // 关键:告诉 MapStruct 生成 Spring Bean
public interface ProductMapper {// 映射 Product -> ProductDto// 如果属性名不同,使用 @Mapping 指定源属性和目标属性@Mapping(source = "id", target = "productId")@Mapping(source = "name", target = "productName")@Mapping(source = "description", target = "details")@Mapping(source = "price", target = "itemPrice")ProductDto toDto(Product product);// 映射 ProductDto -> Product// 注意,如果需要双向映射,需要单独定义方法@Mapping(source = "productId", target = "id")@Mapping(source = "productName", target = "name")@Mapping(source = "details", target = "description")@Mapping(source = "itemPrice", target = "price")Product toEntity(ProductDto productDto);// 你也可以定义其他映射方法,例如 List<Product> -> List<ProductDto>// List<ProductDto> toDtoList(List<Product> products);// MapStruct 会自动处理集合的映射
}
@Mapper(componentModel = "spring")
的作用:
这个属性告诉 MapStruct 生成的实现类应该符合 Spring 的组件模型。这意味着生成的实现类(例如 ProductMapperImpl.java
)会自动带上 Spring 的 @Component
注解(或者如果配置了其他组件扫描规则,可能是 @Service
, @Repository
等,但默认是 @Component
),从而使得 Spring 能够扫描到它,并将其作为一个 Bean 放入应用程序上下文中。这样,你就可以在其他 Spring 管理的 Bean 中通过 @Autowired
或构造函数注入来使用它了。
@Mapping
的作用:
当源对象和目标对象的属性名称不一致时,你需要使用 @Mapping
注解来明确指定映射关系。source
指定源对象的属性名,target
指定目标对象的属性名。如果属性名相同,MapStruct 会默认进行映射,无需 @Mapping
。
步骤 4: 在 Spring 组件中使用 Mapper
由于你在 Mapper 接口上设置了 componentModel = "spring"
,MapStruct 生成的实现类会自动成为 Spring Bean。你可以在 Service、Controller 或其他组件中像注入普通 Bean 一样注入和使用它。
// src/main/java/.../service/ProductService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class ProductService {private final ProductMapper productMapper;// 假设你有一个 Repository 来获取数据// private final ProductRepository productRepository;@Autowired // 推荐使用构造函数注入public ProductService(ProductMapper productMapper /*, ProductRepository productRepository */) {this.productMapper = productMapper;// this.productRepository = productRepository;}public ProductDto getProductDto(Long id) {// 模拟从数据库获取实体Product product = findProductEntityById(id); // 这是一个假设的方法if (product != null) {// 使用 MapStruct 生成的 Mapper 将实体转换为 DTOreturn productMapper.toDto(product);}return null; // 或抛出异常}public Product createProduct(ProductDto productDto) {// 使用 MapStruct 生成的 Mapper 将 DTO 转换为实体Product product = productMapper.toEntity(productDto);// 可以在这里进行进一步的业务处理或调用 Repository 保存实体// productRepository.save(product); // 假设保存操作return product; // 返回创建的实体}// 模拟获取 Product 实体的方法private Product findProductEntityById(Long id) {// 实际应用中会调用 Repositoryif (id == 1L) {Product p = new Product();p.setId(1L);p.setName("Sample Product");p.setDescription("This is a detailed description.");p.setPrice(199.99);return p;}return null;}
}
步骤 5: 构建项目
执行构建命令(如 mvn clean install
或 gradle build
)。构建过程中,MapStruct 的注解处理器会被触发,生成 Mapper 接口的实现类。这些生成的类通常位于项目的 target/generated-sources/annotations
(Maven) 或 build/generated/sources/annotationProcessor/java/main
(Gradle) 目录下。
启动你的 Spring Boot 应用程序,Spring 会扫描并注册生成的 Mapper 实现类,你就可以在运行时正常使用了。
进阶用法和注意事项
- 集合映射: MapStruct 可以自动处理集合类型的映射,如
List<Product> toProductDtoList(List<Product> products);
。 - 嵌套对象映射: 如果你的对象包含其他复杂对象,MapStruct 默认会尝试递归映射这些嵌套对象,前提是你为这些嵌套对象也提供了相应的 Mapper。
- 自定义映射逻辑: 对于复杂的转换,可以使用
@BeforeMapping
,@AfterMapping
注解在映射前或后执行自定义代码,或者定义自定义的方法并在@Mapping
中引用。 - 默认值和表达式: 可以使用
@Mapping(target = "someField", defaultValue = "N/A")
或@Mapping(target = "calculatedField", expression = "java(source.getPropertyA() + source.getPropertyB())")
来设置默认值或使用表达式进行计算。 - 忽略字段: 使用
@Mapping(target = "fieldToIgnore", ignore = true)
可以忽略特定字段的映射。 - 更新现有对象: 除了创建新对象,MapStruct 也可以将源对象的属性映射到已存在的目标对象上,使用
@MappingTarget
注解:void updateProduct(@MappingTarget Product product, ProductDto productDto);
- 配置未映射策略: 可以通过
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
等配置来控制当存在未映射的目标属性时的行为(报告警告、忽略或报错)。
总结
在 Spring Boot 3 项目中集成 MapStruct 是一个非常推荐实践。它利用注解处理器在编译时生成高效、类型安全的映射代码,显著减少了手动编写映射代码的工作量和潜在错误。通过简单的依赖添加、构建工具配置以及 @Mapper
注解和 componentModel = "spring"
的使用,MapStruct 可以无缝地集成到 Spring IoC 容器中,让你像使用其他 Spring Bean 一样方便地进行对象映射。
如果你还在手动进行对象拷贝,或者使用基于反射的映射工具,不妨试试 MapStruct,相信它能为你的开发带来效率和可靠性的提升。
相关文章:
告别手动映射:在 Spring Boot 3 中优雅集成 MapStruct
在日常的后端开发中,我们经常需要在不同的对象之间进行数据转换,例如将数据库实体(Entity)转换为数据传输对象(DTO)发送给前端,或者将接收到的 DTO 转换为实体进行业务处理或持久化。手动进行这…...
uv run 都做了什么?
uv run 都做了什么? uv run <命令> [参数...] 的主要作用是:在一个由 uv 管理或发现的 Python 虚拟环境中,执行你指定的 <命令>。它会临时配置一个子进程的环境,使其表现得如同该虚拟环境已经被激活一样。这意味着&am…...

求解,如何控制三相无刷电机?欢迎到访评论
问题:通过一个集成的TF2104芯片控制H桥上桥臂和下桥臂,如何控制?还是说得需要PWM_UH和PWM_UL分开控制?...
Java ThreadLocal与内存泄漏
当我们利用 ThreadLocal 来管理数据时,我们不可避免地会面临内存泄漏的风险。 原因在于 ThreadLocal 的工作方式。当我们在当前线程的 ThreadLocalMap 中存储一个值时,一旦这个值不再需要,释放它就变得至关重要。如果不这样做,那么…...

365打卡第R3周: RNN-心脏病预测
🍨 本文为🔗365天深度学习训练营中的学习记录博客 🍖 原作者:K同学啊 🏡 我的环境: 语言环境:Python3.10 编译器:Jupyter Lab 深度学习环境:torch2.5.1 torchvision0…...
1.1.1 用于排序规则的IComparable接口使用介绍
在C#中,IComparable 是一个核心接口,用于定义对象的自然排序规则。实现该接口的类可以指定其实例如何与其他实例比较大小,从而支持排序操作(如 Array.Sort()、List.Sort()). 1. 该接口CompareTo返回值含义:…...

【实战】基于强化学习的 Agent 训练框架全流程拆解
一、引言 在人工智能蓬勃发展的今天,强化学习(Reinforcement Learning, RL)作为让智能体(Agent)在复杂环境中自主学习并做出最优决策的核心技术,正日益受到关注。从游戏领域中击败人类顶尖选手的 AlphaGo&a…...

【音视频】⾳频处理基本概念及⾳频重采样
一、重采样 1.1 什么是重采样 所谓的重采样,就是改变⾳频的采样率、sample format、声道数等参数,使之按照我们期望的参数输出。 1.2 为什么要重采样 为什么要重采样? 当然是原有的⾳频参数不满⾜我们的需求,⽐如在FFmpeg解码⾳频的时候…...

Prompt 结构化提示工程
Prompt 结构化提示工程 目前ai开发工具都大同小异,随着deepseek的流行,ai工具的能力都差不太多,功能基本都覆盖到了。而prompt能力反而是需要更加关注的(说白了就是能不能把需求清晰的输出成文档)。因此大家可能需要加…...
设计心得——数据结构的意义
一、数据结构 在老一些的程序员中,可能都听说过,程序其实就是数据结构算法这种说法。它是由尼克劳斯维特在其著作《算法数据结构程序》中提出的,然后在一段时期内这种说法非常流行。这里不谈论其是否正确,只是通过这种提法&#…...
【Pandas】pandas DataFrame rdiv
Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象(如 DataFrame、Series 或标量)的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…...

Pycharm 代理配置
Pycharm 代理配置 文章目录 Pycharm 代理配置1. 设置系统代理1.1 作用范围1.2 使用场景1.3 设置步骤 2. 设置 python 运行/调试代理2.1 作用范围2.2 使用场景2.3 设置步骤 Pycharm 工具作为一款强大的 IDE,其代理配置在实际开发中也是必不可少的,下面介绍…...
GPU 加速库(CUDA/cuDNN)
现代数字图像处理与深度学习任务对计算效率提出极高要求,GPU 加速库通过硬件并行计算能力大幅提升数据处理速度。 一、CUDA 并行计算架构深度解析 1. 架构设计与硬件协同 CPU-GPU 异构计算模型CPU 作为主机端,主要负责逻辑控制、任务调度以及数据预处…...

Spring Native:GraalVM原生镜像编译与性能优化
文章目录 引言一、Spring Native与GraalVM基础1.1 GraalVM原理与优势1.2 Spring Native架构设计 二、原生镜像编译实践2.1 构建配置与过程2.2 常见问题与解决方案 三、性能优化技巧3.1 内存占用优化3.2 启动时间优化3.3 实践案例分析 总结 引言 微服务架构的普及推动了轻量级、…...
JAVA JVM面试题
你的项目中遇到什么问题需要jvm调优,怎么调优的,堆的最小值和最大值设置为什么不设置成一样大? 在项目中,JVM调优通常源于以下典型问题及对应的调优思路,同时关于堆内存参数(-Xms/-Xmx)的设置逻…...

药监平台上传数据报资源码不存在
问题:电子监管码上传药监平台提示“导入的资源码不存在” 现象:从生产系统导出的关联关系数据包上传到药监平台时显示: 原因:上传数据包的通道的资源码与数据包的资源码不匹配。 解决方法:检查药监平台和生产系统的药…...
世界比较权威的新车安全评鉴协会(汽车安全性测试,自动驾驶功能测试)
NCAP是英文“New Car Assessment Program”的缩写,即新车评价规程,最能考验汽车安全性的测试,在自动驾驶发展迅速的现阶段,安全问题频发,自动驾驶相关功能显然也需要进行测试评价。 1. 欧洲新车安全评鉴协会ÿ…...

【Linux应用】交叉编译环境配置,以及最简单粗暴的环境移植(直接从目标板上复制)
【Linux应用】交叉编译环境配置,以及最简单粗暴的环境移植(直接从目标板上复制) 文章目录 交叉编译器含有三方库的交叉编译直接从目标板上复制编译环境glibc库不一致报错方法1方法2 附录:ZERO 3烧录ZERO 3串口shell外设挂载连接Wi…...
CentOS 7 磁盘阵列搭建与管理全攻略
CentOS 7 磁盘阵列搭建与管理全攻略 在数据存储需求日益增长的今天,磁盘阵列(RAID)凭借其卓越的性能、数据安全性和可靠性,成为企业级服务器和数据中心的核心存储解决方案。CentOS 7 作为一款稳定且功能强大的 Linux 操作系统&am…...

CSS3布局方式介绍
CSS3布局方式介绍 CSS3布局(Layout)系统是现代网页设计中用于构建页面结构和控制元素排列的一组强大工具。CSS3提供了多种布局方式,每种方式都有其适用场景,其中最常用的是Flexbox和CSS Grid。 先看传统上几种布局方式ÿ…...
FPGA设计 时空变换
1、时空变换基本概念 1.1、时空概念简介 时钟速度决定完成任务需要的时间,规模的大小决定完成任务所需要的空间(资源),因此速度和规模就是FPGA中时间和空间的体现。 如果要提高FPGA的时钟,每个clk内组合逻辑所能做的事…...
AI心理健康服务平台项目面试实战
AI心理健康服务平台项目面试实战 第一轮提问: 面试官: 请简要介绍一下AI心理健康服务平台的核心技术架构。在AI领域,心理健康服务的机遇主要体现在哪些方面?如何利用NLP技术提升用户与AI的心理健康对话体验? 马架构…...
Eigen稀疏矩阵类 (SparseMatrix)
1. SparseMatrix 核心属性与初始化 模板参数 cpp SparseMatrix<Scalar, Options, StorageIndex> Scalar:数据类型(如 double, float)。 Options:存储格式(默认 ColMajor,可选 RowMajor࿰…...

《AI大模型趣味实战》智能Agent和MCP协议的应用实例:搭建一个能阅读DOC文件并实时显示润色改写过程的Python Flask应用
智能Agent和MCP协议的应用实例:搭建一个能阅读DOC文件并实时显示润色改写过程的Python Flask应用 引言 随着人工智能技术的飞速发展,智能Agent与模型上下文协议(MCP)的应用场景越来越广泛。本报告将详细介绍如何基于Python Flask框架构建一个智能应用&…...

uniapp开发03-轮播图组件swiper的简单使用案例
uniapp开发03-轮播图组件swiper的简单使用案例!这个仅仅是官方提供的一个轮播图组件啊。实际上我们项目开发的时候,会应用到其他第三方公司的轮播图组件资源!效果更强大。兼容性更强。 废话不多说,我们直接上代码。分析代码。 &l…...
DAM-3B,英伟达推出的多模态大语言模型
DAM-3B是什么 DAM-3B(Describe Anything 3B)是英伟达推出的一款多模态大语言模型,专门用于为图像和视频中的特定区域生成详细描述。用户可以通过点、边界框、涂鸦或掩码等方式来标识目标区域,从而得到精准且符合上下文的文本描述…...
【虚幻C++笔记】碰撞检测
目录 碰撞检测参数详情示例用法 碰撞检测 显示名称中文名称CSphere Trace By Channel按通道进行球体追踪UKismetSystemLibrary::SphereTraceSingleSphere Trace By Profile按描述文件进行球体追踪UKismetSystemLibrary::SphereTraceSingleByProfileSphere Trace For Objects针…...
C++学习:六个月从基础到就业——STL:分配器与设计原理
C学习:六个月从基础到就业——STL:分配器与设计原理 本文是我C学习之旅系列的第三十篇技术文章,也是第二阶段"C进阶特性"的第九篇,主要介绍C STL中的分配器设计原理与实现。查看完整系列目录了解更多内容。 引言 在之前…...

【Android】四大组件之Service
目录 一、什么是Service 二、启停 Service 三、绑定 Service 四、前台服务 五、远程服务扩展 六、服务保活 七、服务启动方法混用 你可以把Service想象成一个“后台默默打工的工人”。它没有UI界面,默默地在后台干活,比如播放音乐、下载文件、处理…...

TRO再添新案 TME再拿下一热门IP,涉及Paddington多个商标
4月2日和4月8日,TME律所代理Paddington & Company Ltd.对热门IP Paddington Bear帕丁顿熊的多类商标发起维权,覆盖文具、家居用品、毛绒玩具、纺织用品、游戏、电影、咖啡、填充玩具等领域。跨境卖家需立即排查店铺内的相关产品! 案件基…...