【架构】[MyBatis-Plus]----MyBatis-Plus Pro升级版、 提升开发效率80%,代码简洁80% 封装实战 通用 BaseController

基于 SpringBoot 3.3.5MyBatis-Plus 3.5.5Lombok 技术栈,封装通用 BaseController 实现 Controller 层 CRUD 逻辑复用。通过标准化注解、精简注释,确保代码可读性与可维护性,子类仅需继承即可拥有全套通用接口。

一、环境准备:依赖配置

pom.xml 中引入核心依赖,包含 SpringBoot 基础、MyBatis-Plus、Lombok 及 Swagger(接口文档,可选但推荐):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
        <relativePath/>
    </parent>

    <dependencies>
        <!-- SpringBoot Web 核心 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- MyBatis-Plus 启动器(3.5.5 适配 SpringBoot 3.x) -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.5</version>
        </dependency>

        <!-- Lombok(简化实体类 getter/setter、构造器等) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- MySQL 驱动(根据数据库类型调整) -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Swagger/OpenAPI(接口文档,便于调试) -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.5.0</version>
        </dependency>
    </dependencies>
</project>

二、核心工具类:ApprenticeUtil

提供 格式转换QueryWrapper 自动构建反射获取字段值 能力,为 BaseController 提供底层支持,所有方法均添加详细说明与参数注解。

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 通用工具类:提供驼峰/下划线转换、QueryWrapper构建、反射字段值获取等能力
 * @author 开发者名称
 * @since JDK 17(SpringBoot 3.x 最低支持 JDK 17)
 */
public class ApprenticeUtil {
    /** 驼峰转下划线正则:匹配大写字母 */
    private static final Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
    /** 下划线转驼峰正则:匹配下划线+后续字符 */
    private static final Pattern LINE_PATTERN = Pattern.compile("_(\\w)");

    /**
     * 驼峰命名转下划线命名(如 userName → user_name)
     * @param str 输入的驼峰格式字符串,可为空(为空时返回空)
     * @return java.lang.String 转换后的下划线格式字符串,若输入为空则返回空
     */
    public static String humpToLine(String str) {
        if (ObjectUtils.isEmpty(str)) {
            return str;
        }
        Matcher matcher = HUMP_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 下划线命名转驼峰命名(如 user_name → userName)
     * @param str 输入的下划线格式字符串,可为空(为空时返回空)
     * @return java.lang.String 转换后的驼峰格式字符串,若输入为空则返回空
     */
    public static String lineToHump(String str) {
        if (ObjectUtils.isEmpty(str)) {
            return str;
        }
        str = str.toLowerCase();
        Matcher matcher = LINE_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 根据实体非空字段自动构建 QueryWrapper(仅生成 eq 条件)
     * @param entity 实体对象,不可为空(为空时返回空 QueryWrapper)
     * @param <E> 实体泛型类型
     * @return com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<E> 构建后的查询条件,若反射异常返回空
     */
    public static <E> QueryWrapper<E> getQueryWrapper(E entity) {
        if (ObjectUtils.isEmpty(entity)) {
            return new QueryWrapper<>();
        }
        Field[] fields = entity.getClass().getDeclaredFields();
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();

        for (Field field : fields) {
            // 跳过 final 修饰的常量字段
            if (Modifier.isFinal(field.getModifiers())) {
                continue;
            }
            field.setAccessible(true); // 突破私有字段访问限制

            try {
                Object fieldValue = field.get(entity);
                // 字段值非空时,添加 eq 条件(字段名转下划线匹配数据库列)
                if (!ObjectUtils.isEmpty(fieldValue)) {
                    String dbColumn = humpToLine(field.getName());
                    queryWrapper.eq(dbColumn, fieldValue);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return null;
            }
        }
        return queryWrapper;
    }

    /**
     * 反射获取实体指定字段的值(通过 getter 方法,遵循 JavaBean 规范)
     * @param entity 实体对象,不可为空(为空时返回空)
     * @param fieldName 字段名(如 "id"、"name"),不可为空(为空时返回空)
     * @param <E> 实体泛型类型
     * @return java.lang.Object 字段的具体值,若反射异常(如字段不存在)则返回空
     */
    public static <E> Object getValueForClass(E entity, String fieldName) {
        if (ObjectUtils.isEmpty(entity) || ObjectUtils.isEmpty(fieldName)) {
            return null;
        }

        Field field = null;
        PropertyDescriptor propertyDescriptor = null;
        try {
            // 获取实体类的指定字段
            field = entity.getClass().getDeclaredField(fieldName);
            // 获取字段的 PropertyDescriptor(封装 getter/setter)
            propertyDescriptor = new PropertyDescriptor(field.getName(), entity.getClass());
        } catch (NoSuchFieldException | IntrospectionException e) {
            e.printStackTrace();
            return null;
        }

        // 调用 getter 方法获取字段值
        Method getterMethod = Objects.requireNonNull(propertyDescriptor).getReadMethod();
        return ReflectionUtils.invokeMethod(getterMethod, entity);
    }
}

三、通用响应工具类:ResponseUtils

统一接口返回格式,包含状态码、提示信息、业务数据,使用 Lombok 简化代码。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 接口统一响应工具类:封装返回结果格式
 * @author 开发者名称
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseUtils<T> {
    /** 状态码:200=成功,500=失败,400=参数错误(可扩展更多状态码) */
    private Integer code;
    /** 提示信息:如 "操作成功"、"参数不能为空" */
    private String msg;
    /** 业务数据:如列表、单条实体、统计数量等 */
    private T data;

    /**
     * 成功响应(无业务数据)
     * @param msg 成功提示信息
     * @return ResponseUtils<T> 统一响应对象
     */
    public static <T> ResponseUtils<T> success(String msg) {
        return new ResponseUtils<>(200, msg, null);
    }

    /**
     * 成功响应(带业务数据)
     * @param msg 成功提示信息
     * @param data 业务数据
     * @return ResponseUtils<T> 统一响应对象
     */
    public static <T> ResponseUtils<T> success(String msg, T data) {
        return new ResponseUtils<>(200, msg, data);
    }

    /**
     * 失败响应(无业务数据)
     * @param msg 失败提示信息
     * @return ResponseUtils<T> 统一响应对象
     */
    public static <T> ResponseUtils<T> error(String msg) {
        return new ResponseUtils<>(500, msg, null);
    }

    /**
     * 参数错误响应(无业务数据)
     * @param msg 参数错误提示信息
     * @return ResponseUtils<T> 统一响应对象
     */
    public static <T> ResponseUtils<T> paramError(String msg) {
        return new ResponseUtils<>(400, msg, null);
    }
}

四、分页参数 DTO:PageParamDto

封装分页查询所需参数(页码、页大小、排序字段),使用 Lombok 简化代码,字段添加注释说明。

import lombok.Data;
import org.springframework.util.ObjectUtils;

/**
 * 分页查询参数 DTO:统一分页与排序参数格式
 * @author 开发者名称
 * @param <E> 筛选条件实体类型(可为空,用于分页+条件查询)
 */
@Data
public class PageParamDto<E> {
    /** 页码:默认1,不能小于1 */
    private Integer page = 1;
    /** 页大小:默认10,最大不超过100(避免性能问题) */
    private Integer size = 10;
    /** 升序排序字段:多字段用逗号分隔(如 "createTime,name"),可为空 */
    private String asc;
    /** 降序排序字段:多字段用逗号分隔(如 "updateTime,id"),可为空 */
    private String desc;
    /** 筛选条件实体:非空字段作为查询条件,可为空 */
    private E entity;

    /**
     * 分页参数校验与修正:确保页码≥1,页大小≤100
     */
    public void validateParam() {
        this.page = ObjectUtils.isEmpty(this.page) || this.page < 1 ? 1 : this.page;
        this.size = ObjectUtils.isEmpty(this.size) || this.size > 100 ? 100 : this.size;
    }
}

五、MyBatis-Plus 分页配置

SpringBoot 3.x 环境下,通过配置类注入 MyBatis-Plus 分页插件,确保分页功能生效。

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MyBatis-Plus 配置类:注入分页插件(适配 SpringBoot 3.x)
 * @author 开发者名称
 */
@Configuration
public class MyBatisPlusConfig {

    /**
     * 注册 MyBatis-Plus 拦截器链:添加分页插件
     * @return com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor 拦截器对象
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件:指定数据库类型为 MySQL(其他数据库需修改 DbType,如 DbType.ORACLE)
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        // 设置单页最大条数(与 PageParamDto 保持一致,避免超大页查询)
        paginationInterceptor.setMaxLimit(100L);
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}

六、核心基类:BaseController

通过泛型适配任意实体与 Service,封装 8 个通用接口,所有参数与方法均添加详细注解说明。

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Controller 通用基类:封装 CRUD、分页、排序等通用接口
 * @author 开发者名称
 * @param <S> Service 类型:必须继承 MyBatis-Plus 的 IService 接口
 * @param <E> 实体类型:与 Service 对应的数据库实体
 */
@Tag(name = "通用CRUD接口", description = "所有业务Controller继承此基类,自动获得通用接口")
public class BaseController<S extends IService<E>, E> {

    /** 通用 Service:自动注入子类对应的 Service 实现(子类无需重复注入) */
    @Autowired
    protected S baseService;

    /**
     * 新增数据(单条)
     * @param entity 实体对象:包含新增的业务数据(需符合数据库字段规则)
     * @return ResponseUtils<E> 响应结果:成功返回200+提示,失败返回500+错误信息
     */
    @Operation(summary = "新增数据", description = "单条数据新增,实体对象非空字段为新增内容")
    @PostMapping("/insert")
    public ResponseUtils<E> insert(
            @Parameter(description = "新增实体对象", required = true) @RequestBody E entity
    ) {
        boolean isSuccess = baseService.save(entity);
        return isSuccess ? ResponseUtils.success("新增成功") : ResponseUtils.error("新增失败");
    }

    /**
     * 批量删除数据(按ID列表)
     * @param ids ID列表:需删除的实体ID集合(不可为空且不可包含无效ID)
     * @return ResponseUtils<E> 响应结果:成功返回200+提示,失败返回500+错误信息
     */
    @Operation(summary = "批量删除数据", description = "按ID列表批量删除,ID不可为空且需为有效数字")
    @PostMapping("/deleteByIds")
    public ResponseUtils<E> deleteByIds(
            @Parameter(description = "删除ID列表(如[1,2,3])", required = true) @RequestBody List<Long> ids
    ) {
        if (ids == null || ids.isEmpty()) {
            return ResponseUtils.paramError("请选择需删除的记录ID");
        }
        boolean isSuccess = baseService.removeByIds(ids);
        return isSuccess ? ResponseUtils.success("删除成功") : ResponseUtils.error("删除失败");
    }

    /**
     * 按ID更新数据
     * @param entity 实体对象:必须包含ID字段(用于定位数据),其他非空字段为更新内容
     * @return ResponseUtils<E> 响应结果:成功返回200+提示,失败返回500+错误信息
     */
    @Operation(summary = "按ID更新数据", description = "实体需包含ID字段,非空字段为更新内容")
    @PostMapping("/updateById")
    public ResponseUtils<E> updateById(
            @Parameter(description = "更新实体对象(含ID)", required = true) @RequestBody E entity
    ) {
        // 反射校验ID是否存在(简化版:实际可结合 ApprenticeUtil 完善)
        Object id = ApprenticeUtil.getValueForClass(entity, "id");
        if (id == null) {
            return ResponseUtils.paramError("更新需指定ID");
        }
        boolean isSuccess = baseService.updateById(entity);
        return isSuccess ? ResponseUtils.success("更新成功") : ResponseUtils.error("更新失败");
    }

    /**
     * 按ID查询单条数据
     * @param id 实体ID:不可为空且需为有效数字(如1、2)
     * @return ResponseUtils<E> 响应结果:成功返回200+实体数据,无数据返回200+空数据
     */
    @Operation(summary = "按ID查询数据", description = "根据实体ID查询单条记录,ID不可为空")
    @GetMapping("/getById")
    public ResponseUtils<E> getById(
            @Parameter(description = "实体ID", required = true) @RequestParam Long id
    ) {
        if (id == null || id < 1) {
            return ResponseUtils.paramError("ID无效(需为正整数)");
        }
        E entity = baseService.getById(id);
        return ResponseUtils.success("查询成功", entity);
    }

    /**
     * 保存或更新数据(自动判断)
     * @param entity 实体对象:含ID则更新,不含ID则新增(ID生成策略需配置,如自增、雪花算法)
     * @return ResponseUtils<E> 响应结果:成功返回200+提示,失败返回500+错误信息
     */
    @Operation(summary = "保存或更新数据", description = "自动判断:含ID则更新,不含ID则新增")
    @PostMapping("/saveOrUpdate")
    public ResponseUtils<E> saveOrUpdate(
            @Parameter(description = "保存/更新实体对象", required = true) @RequestBody E entity
    ) {
        boolean isSuccess = baseService.saveOrUpdate(entity);
        return isSuccess ? ResponseUtils.success("操作成功") : ResponseUtils.error("操作失败");
    }

    /**
     * 带条件查询列表
     * @param entity 实体对象:非空字段作为查询条件(如name="张三"则查询name为张三的所有数据)
     * @return ResponseUtils<List<E>> 响应结果:成功返回200+列表数据,无数据返回空列表
     */
    @Operation(summary = "带条件查询列表", description = "实体非空字段作为eq条件,返回符合条件的所有数据")
    @PostMapping("/list")
    public ResponseUtils<List<E>> list(
            @Parameter(description = "查询条件实体(非空字段为条件)", required = false) @RequestBody(required = false) E entity
    ) {
        QueryWrapper<E> queryWrapper = ApprenticeUtil.getQueryWrapper(entity);
        List<E> dataList = baseService.list(queryWrapper);
        return ResponseUtils.success("查询成功", dataList);
    }

    /**
     * 分页查询(支持排序)
     * @param pageParamDto 分页参数:包含页码、页大小、排序字段、筛选条件实体
     * @return ResponseUtils<Page<E>> 响应结果:成功返回200+分页数据(含总条数、当前页数据)
     */
    @Operation(summary = "分页查询", description = "支持分页+多字段排序+条件筛选,页码默认1,页大小默认10")
    @PostMapping("/page")
    public ResponseUtils<Page<E>> page(
            @Parameter(description = "分页参数(含排序、筛选条件)", required = true) @RequestBody PageParamDto<E> pageParamDto
    ) {
        // 分页参数校验与修正
        pageParamDto.validateParam();
        // 初始化分页对象
        Page<E> page = new Page<>(pageParamDto.getPage(), pageParamDto.getSize());
        // 构建查询条件(含筛选+排序)
        QueryWrapper<E> queryWrapper = ApprenticeUtil.getQueryWrapper(pageParamDto.getEntity());
        // 处理升序排序
        if (!org.springframework.util.ObjectUtils.isEmpty(pageParamDto.getAsc())) {
            String[] ascFields = pageParamDto.getAsc().split(",");
            queryWrapper.orderByAsc(ascFields);
        }
        // 处理降序排序
        if (!org.springframework.util.ObjectUtils.isEmpty(pageParamDto.getDesc())) {
            String[] descFields = pageParamDto.getDesc().split(",");
            queryWrapper.orderByDesc(descFields);
        }
        // 执行分页查询
        Page<E> resultPage = baseService.page(page, queryWrapper);
        return ResponseUtils.success("分页查询成功", resultPage);
    }

    /**
     * 按条件统计记录数
     * @param entity 实体对象:非空字段作为统计条件(如status=1则统计状态为1的总条数)
     * @return ResponseUtils<Long> 响应结果:成功返回200+统计数量,无数据返回0
     */
    @Operation(summary = "按条件统计数量", description = "实体非空字段作为eq条件,返回符合条件的总条数")
    @PostMapping("/count")
    public ResponseUtils<Long> count(
            @Parameter(description = "统计条件实体(非空字段为条件)", required = false) @RequestBody(required = false) E entity
    ) {
        QueryWrapper<E> queryWrapper = ApprenticeUtil.getQueryWrapper(entity);
        long total = baseService.count(queryWrapper);
        return ResponseUtils.success("统计成功", total);
    }
}

七、实体类示例:Blog(博客实体)

使用 Lombok 简化代码,字段添加一行注释说明数据库含义,配合 MyBatis-Plus 注解映射数据库表。

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * 博客实体:映射数据库 blog 表
 * @author 开发者名称
 */
@Data
@TableName("blog") // 数据库表名(若实体名与表名一致可省略)
public class Blog {
    /** 博客ID:主键,自增 */
    @TableId(type = IdType.AUTO)
    private Long id;
    /** 博客标题:非空,最长255字符 */
    private String title;
    /** 博客内容:非空,长文本 */
    private String content;
    /** 作者ID:关联用户表主键 */
    private Long authorId;
    /** 博客状态:0=草稿,1=发布,2=删除 */
    private Integer status;
    /** 创建时间:自动填充(需配置MyBatis-Plus自动填充) */
    private LocalDateTime createTime;
    /** 更新时间:自动填充(需配置MyBatis-Plus自动填充) */
    private LocalDateTime updateTime;
}

八、Service 层示例:IBlogService 与实现

遵循 MyBatis-Plus 规范,Service 接口继承 IService,实现类继承 ServiceImpl,无需编写基础 CRUD 逻辑。

1. 接口:IBlogService

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.Blog;

/**
 * 博客 Service 接口:继承 MyBatis-Plus IService,获得基础 CRUD 能力
 * @author 开发者名称
 */
public interface IBlogService extends IService<Blog> {
    // 如需自定义业务方法,在此添加(如:根据作者ID查询博客列表)
}

2. 实现类:BlogServiceImpl

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.Blog;
import com.example.demo.mapper.BlogMapper;
import com.example.demo.service.IBlogService;
import org.springframework.stereotype.Service;

/**
 * 博客 Service 实现类:继承 MyBatis-Plus ServiceImpl,无需编写基础 CRUD 实现
 * @author 开发者名称
 */
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
    // 基础 CRUD 逻辑由 MyBatis-Plus 自动实现,自定义方法在此实现
}

九、子类 Controller 示例:BlogController

继承 BaseController,指定泛型(Service 类型 + 实体类型),无需编写代码即可拥有所有通用接口。

import com.example.demo.entity.Blog;
import com.example.demo.service.IBlogService;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 博客 Controller:继承 BaseController 获得通用 CRUD 接口
 * @author 开发者名称
 */
@RestController
@RequestMapping("/api/blog") // 接口基础路径(前端请求路径:http://localhost:8080/api/blog/xxx)
@Tag(name = "博客管理", description = "博客相关操作:新增、删除、更新、查询、分页等")
public class BlogController extends BaseController<IBlogService, Blog> {
    // 无需编写代码,已自动拥有以下接口:
    // 1. POST /api/blog/insert        新增
    // 2. POST /api/blog/deleteByIds   批量删除
    // 3. POST /api/blog/updateById    按ID更新
    // 4. GET  /api/blog/getById       按ID查询
    // 5. POST /api/blog/saveOrUpdate  保存或更新
    // 6. POST /api/blog/list          条件查询列表
    // 7. POST /api/blog/page          分页查询
    // 8. POST /api/blog/count         条件统计数量

    // 如需自定义接口(如:根据作者ID查询博客),在此新增即可
}

十、关键注意事项

  1. JDK 版本:SpringBoot 3.x 最低支持 JDK 17,需确保开发环境与部署环境一致;
  2. 实体 ID 规范:实体 ID 建议统一命名为 id(Long 类型),若使用其他名称需修改 BaseController 中反射获取 ID 的逻辑(ApprenticeUtil.getValueForClass(entity, "id"));
  3. 自动填充配置createTimeupdateTime 等字段建议配置 MyBatis-Plus 自动填充,避免手动设置;
  4. 异常处理:实际项目中建议添加全局异常处理器(@RestControllerAdvice),统一捕获并处理反射异常、数据库异常等;
  5. 权限控制:通用接口需结合权限框架(如 Spring Security、Sa-Token)添加权限校验,避免未授权访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值