SpringBoot整合MyBatis极简教程:10分钟快速开发企业级增删改查

#新星杯·14天创作挑战营·第11期#

Java系列文章



前言

本文将简单介绍如何使用springboot和mybatis快速增删改查,实现通用的数据字典业务流程,项目通用配置另写文章介绍。

一、环境准备

1.1 开发工具清单

  • JDK 17+、Maven 3.8+、IntelliJ IDEA 2024.3

1.2 初始化SpringBoot项目

1.2.1项目创建

  • Spring Initializr或者IDEA快速创建(勾选Web/MyBatis/MySQL)
    在这里插入图片描述

1.2.2 目录结构

在这里插入图片描述

1.3数据库准备

1.3.1 表结构设计

在这里插入图片描述

1.3.2 添加数据

在这里插入图片描述

1.3.3 sql执行代码

CREATE TABLE `tb_dictionary` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `parent_id` int DEFAULT NULL COMMENT '父级id',
  `key` varchar(255) DEFAULT NULL COMMENT '字典key',
  `value` varchar(255) DEFAULT NULL COMMENT '字典value',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  `sort` int DEFAULT NULL COMMENT '排序',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (1, 0, 'combo_type', NULL, '套餐类型', 1, '2025-05-14 22:27:12');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (2, 1, '1', '不限', '', 2, '2025-05-14 22:27:28');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (3, 1, '2', '父母体检', '', 2, '2025-05-14 22:33:08');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (4, 1, '3', '入职体检', '', 2, '2025-05-14 22:33:23');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (5, 1, '4', '职场白领', '', 2, '2025-05-14 22:34:12');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (6, 1, '5', '个人高端', '', 2, '2025-05-14 22:35:23');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (7, 1, '6', '中青年体检', '', 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (8, 0, 'combo_tag', NULL, '套餐标签', 1, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (9, 8, '1', '男性', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (10, 8, '2', '女性', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (11, 8, '3', '中青年', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (12, 8, '4', '中老年', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (13, 8, '5', '白领精英', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (14, 0, 'combo_module', NULL, '套餐模块', 1, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (15, 0, 'shelf', NULL, '上下架', 1, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (16, 14, '1', '活动专区', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (17, 14, '2', '热卖套餐', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (18, 14, '3', '新品推荐', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (19, 14, '4', '孝敬父母', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (20, 14, '5', '白领精英', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (21, 15, '1', '上架', NULL, 2, '2025-05-14 22:35:37');
INSERT INTO `tb_dictionary` (`id`, `parent_id`, `key`, `value`, `description`, `sort`, `update_time`) VALUES (22, 15, '2', '下架', NULL, 2, '2025-05-14 22:35:37');

二、极速整合

2.1 依赖配置解析

2.1.1 pom.xml关键依赖

提示:这里使用到mybatis、mysql和driud连接池

<!--mybatis-->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.1.0</version>
</dependency>
<!--MySQL-->
<dependency>
	<groupId>com.mysql</groupId>
	<artifactId>mysql-connector-j</artifactId>
	<version>8.0.32</version>
</dependency>
<!--MySQL连接池-->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.2.15</version>
</dependency>

2.2 数据源配置

2.2.1 application.yml配置MySQL连接池(Druid)

server:
  port: 5577
  servlet:
    context-path: /mis-api

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: "数据库连接地址"
      username: "账号"
      password: "密码"
      initial-size: 8
      max-active: 16
      min-idle: 8
      max-wait: 60000
      test-while-idle: true
      test-on-borrow: true
      test-on-return: false
      validation-query: SELECT 1

mybatis:
  mapper-locations: "classpath*:mapper/*.xml"
  type-aliases-package: com.example.db.pojo
  configuration:

2.3 MyBatis核心配置

2.3.1 Mapper接口扫描路径

在这里插入图片描述

2.3.2 驼峰命名自动转换

提示:yml文件配置开启驼峰转换

mybatis:
  mapper-locations: "classpath*:mapper/*.xml"
  type-aliases-package: com.example.db.pojo
  configuration:
    # Mybatis开启驼峰映射
  	map-underscore-to-camel-case: true

2.3.3 MyBatis-X自动生成Mapper/Entity

快速生成

在这里插入图片描述

生成目录

在这里插入图片描述

三、CRUD接口编写

3.1 实体类与Mapper接口

3.1.1 DictionaryEntity实体类

package com.example.db.pojo;

import java.util.Date;
import lombok.Data;

/**
 * 
 * @TableName tb_dictionary
 */
@Data
public class DictionaryEntity {
    /**
     * id
     */
    private Integer id;

    /**
     * 父级id
     */
    private Integer parentId;

    /**
     * 字典key
     */
    private String key;

    /**
     * 字典value
     */
    private String value;

    /**
     * 描述
     */
    private String description;

    /**
     * 排序
     */
    private Integer sort;

    /**
     * 更新时间
     */
    private Date updateTime;
}

3.1.2 DictionaryDao接口

package com.example.db.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
* @author lenovo
* @description 针对表【tb_dictionary】的数据库操作Mapper
* @createDate 2025-05-14 23:04:21
* @Entity com.example.db.pojo.DictionaryEntity
*/
public interface DictionaryDao {
    // 数据字典-查询id
    public ArrayList<HashMap> searchDictionaryById(Map param);

    // 数据字典-新增
    public Integer insertDictionary(Map param);

    // 数据字典-更新
    public Integer updateDictionary(Map param);

    // 数据字典-删除
    public Integer deleteDictionary(Map param);
}

3.2 xml映射文件编写

3.2.1 DictionaryDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.db.dao.DictionaryDao">

    <resultMap id="BaseResultMap" type="com.example.db.pojo.DictionaryEntity">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="parentId" column="parent_id" jdbcType="INTEGER"/>
            <result property="key" column="key" jdbcType="VARCHAR"/>
            <result property="value" column="value" jdbcType="VARCHAR"/>
            <result property="description" column="description" jdbcType="VARCHAR"/>
            <result property="sort" column="sort" jdbcType="INTEGER"/>
            <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
    </resultMap>

    <!-- 数据字典-查询id  -->
    <select id="searchDictionaryById" parameterType="map" resultType="hashmap">
        select d.id, d.parent_id as parentId, d.key, d.value, d.description, d.sort, DATE_FORMAT(d.update_time,"%Y-%m-%d %H:%i:%s") as updateTimne
        from tb_dictionary d
        where d.parent_id = #{parentId}
    </select>

    <!-- 数据字典-新增  -->
    <insert id="insertDictionary">
        insert into tb_dictionary
        set parent_id=#{parentId}
            <if test="description!=null">
                ,`description`=#{description}
            </if>
            <if test="sort!=null">
                ,`sort`=#{sort}
            </if>
            <if test="key!=null">
                ,`key`=#{key}
            </if>
            <if test="value!=null">
                ,`value`=#{value}
            </if>
    </insert>

    <!-- 数据字典-更新  -->
    <update id="updateDictionary">
        update tb_dictionary
        set parent_id=#{parentId}
            <if test="description!=null">
                ,`description`=#{description}
            </if>
            <if test="sort!=null">
                ,`sort`=#{sort}
            </if>
            <if test="key!=null">
                ,`key`=#{key}
            </if>
            <if test="value!=null">
                ,`value`=#{value}
            </if>
        where id = #{id}
    </update>

    <!-- 数据字典-删除  -->
    <delete id="deleteDictionary">
        delete from tb_dictionary where id in
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>

</mapper>

3.3 Service层封装

package com.example.mis.service;

import com.example.db.dao.DictionaryDao;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@Service
public class DictionaryService {

    @Resource
    private DictionaryDao dictionaryDao;

    // 数据字典-查询id
    public ArrayList<HashMap> searchDictionaryById(Map param){
        ArrayList<HashMap> list = dictionaryDao.searchDictionaryById(param);
        return list;
    }

    // 数据字典-新增
    public Integer insertDictionary(Map param) {
        Integer rows = dictionaryDao.insertDictionary(param);
        return rows;
    }

    // 数据字典-更新
    public Integer updateDictionary(Map param) {
        Integer rows = dictionaryDao.updateDictionary(param);
        return rows;
    }
    // 数据字典-删除
    public Integer deleteDictionary(Map param) {
        Integer rows = dictionaryDao.deleteDictionary(param);
        return rows;
    }

}

3.4 Controller层CRUD

3.4.1 接口参数校验

表单验证目录

在这里插入图片描述

校验代码编写
package com.example.req.Dictionary;


import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Data
public class DictionaryEditForm {

    private Integer id;

    @NotNull(message = "parentId不能为空")
    private Integer parentId;

//    @NotBlank(message = "key不能为空")
    private String key;

//    @NotBlank(message = "value不能为空")
    private String value;

//    @NotBlank(message = "description不能为空")
    private String description;

//    @NotNull(message = "sort不能为空")
    private Integer sort;
}

3.4.1 增删改查接口开发

package com.example.mis.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.example.common.result.R;
import com.example.mis.service.DictionaryService;
import com.example.req.Dictionary.DictionaryDeleteForm;
import com.example.req.Dictionary.DictionaryEditForm;
import com.example.req.Dictionary.DictionarySearchForm;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/admin")
public class DictionaryController {

    @Resource
    private DictionaryService dictionaryService;

    /**
     * 数据字典-查询id
     * @param form
     * @return
     */
    @PostMapping("/dictionary/searchDictionaryById")
    public R searchDictionaryById(@RequestBody @Valid DictionarySearchForm form) {
        Map param = BeanUtil.beanToMap(form);
        ArrayList<HashMap> list = dictionaryService.searchDictionaryById(param);
        return R.success(list);
    }

    /**
     * 数据字典-编辑
     * @param form
     * @return
     */
    @PostMapping("/dictionary/editDictionary")
    public R editDictionary(@RequestBody @Valid DictionaryEditForm form) {
        Map param = BeanUtil.beanToMap(form);
        Integer rows;
        if(ObjectUtil.isEmpty(form.getId())) {
        	# 数据字典-新增
            rows = dictionaryService.insertDictionary(param);
        }else {
       		 # 数据字典-更新
            rows = dictionaryService.updateDictionary(param);
        }
        return R.success(rows);
    }

    /**
     * 数据字典-删除
     * @param form
     * @return
     */
    @PostMapping("/dictionary/deleteDictionary")
    public R deleteDictionary(@RequestBody @Valid DictionaryDeleteForm form) {
        Map param = BeanUtil.beanToMap(form);
        Integer rows = dictionaryService.deleteDictionary(param);
        return R.success(rows);
    }
}

3.5 Apifox接口测试

在这里插入图片描述

四、前端实现

4.1 界面展示

在这里插入图片描述

4.2 完整代码

<template>
  <a-card shadow="never" :style="{ height: cardHeight + 'px' }">
    <splitpanes class="split-theme">
      <pane min-size="10" size="15">
        <div class="dictionary-category">
          <div
            :class="['dictionary-category-item', activeKey === index ? 'active' : '']"
            v-for="(item, index) in categoryList"
            :key="index"
            @click="onCategoryItem(item, index)"
          >
            {{ item.key }}({{ item.description }})
          </div>
        </div>
      </pane>
      <pane min-size="70" size="85">
        <a-spin :spinning="loading" size="large" tip="Loading...">
          <div class="split-header">
            <div class="nav">
              <a-button type="primary" @click="onAddDictionary">新增</a-button>
            </div>
            <div class="action">
              <a-button type="primary">导出</a-button>
            </div>
          </div>
          <div class="split-list">
            <a-table :columns="dictionaryColumn" :data-source="dictionaryData" :pagination="false" :scroll="{ y: scrollHeight }" size="small">
              <template #bodyCell="{ column, record }">
                <template v-if="column.key === 'action'">
                  <a-button type="text" @click="onEditDictionary(record)">编辑</a-button>
                  <a-button type="text" @click="onDeleteDictionary(record)">删除</a-button>
                </template>
              </template>
            </a-table>
          </div>
        </a-spin>
      </pane>
    </splitpanes>
    <diaLogModal v-model="diaLogData.show" :diaLogData="diaLogData" @submit="onDialogConfirm">
      <a-form ref="userFormRef" :model="dataForm" :label-col="{ style: { width: '150px' } }" :wrapper-col="{ span: 14 }">
        <a-form-item label="key">
          <a-input v-model:value="dataForm.key" placeholder="请输入key" allow-clear autoComplete="off" />
        </a-form-item>
        <a-form-item label="value">
          <a-input v-model:value="dataForm.value" placeholder="请输入value" allow-clear autoComplete="off" />
        </a-form-item>
        <a-form-item label="描述">
          <a-input v-model:value="dataForm.description" placeholder="请输入字典描述" allow-clear autoComplete="off" />
        </a-form-item>
        <a-form-item label="排序">
          <a-input v-model:value="dataForm.sort" placeholder="请输入排序" allow-clear autoComplete="off" />
        </a-form-item>
      </a-form>
    </diaLogModal>
  </a-card>
</template>

<script setup>
import { searchDictionaryById, editDictionary, deleteDictionary } from '@/api/modules/system.js'
import { ref, reactive, onMounted, h } from 'vue'
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
import { Modal, message } from 'ant-design-vue'

// 页面窗口尺寸
function useWindowRise() {
  const scrollHeight = ref(window.innerHeight - 122 - 140)
  const cardHeight = ref(window.innerHeight - 122)
  const height = ref(window.innerHeight - 122 + 'px')
  window.onresize = e => {
    scrollHeight.value = e.target.innerHeight - 122 - 140
    cardHeight.value = window.innerHeight - 122
    height.value = window.innerHeight - 122 + 'px'
  }

  return {
    scrollHeight,
    cardHeight,
    height
  }
}

// 初始化数据
function useInitData() {
  const loading = ref(false)
  const activeKey = ref(0)
  const categoryList = ref([])
  const dictionaryColumn = ref([
    {
      title: 'Key',
      dataIndex: 'key',
      key: 'key',
      width: 180
    },
    {
      title: 'Value',
      dataIndex: 'value',
      key: 'value',
      width: 160
    },
    {
      title: '描述',
      dataIndex: 'description',
      key: 'description',
      width: 300
    },
    {
      title: '更新时间',
      dataIndex: 'updateTimne',
      key: 'updateTimne',
      width: 120
    },
    {
      title: '操作',
      dataIndex: 'action',
      key: 'action',
      width: 120
    }
  ])
  const dictionaryData = ref([])

  const getDictionaryData = async parentId => {
    loading.value = true
    const data = await searchDictionaryById({ parentId: parentId })
    setTimeout(() => {
      loading.value = false
    }, 200)
    return data
  }
  onMounted(async () => {
    loading.value = true
    categoryList.value = await getDictionaryData(0)
    dictionaryData.value = await getDictionaryData(categoryList.value[0]?.id)
  })

  return { loading, activeKey, categoryList, dictionaryColumn, dictionaryData, getDictionaryData }
}

// 操作事件
function useAction() {
  // 弹窗表单
  const diaLogData = reactive({
    show: false,
    title: '字典新增',
    width: '40%',
    buttons: ['取消', '确定']
  })
  const dataForm = reactive({
    id: '',
    parentId: '',
    key: '',
    value: '',
    description: '',
    sort: ''
  })
  const originForm = reactive({
    id: '',
    parentId: '',
    key: '',
    value: '',
    description: '',
    sort: ''
  })

  // 按钮事件
  const onCategoryItem = async (item, key) => {
    activeKey.value = key
    loading.value = true
    dictionaryData.value = await getDictionaryData(item.id)
  }
  const onAddDictionary = () => {
    Object.assign(dataForm, originForm)
    dataForm.parentId = categoryList.value[activeKey.value]?.id
    diaLogData.show = true
  }
  const onEditDictionary = rows => {
    diaLogData.show = true
    dataForm.id = rows.id
    dataForm.parentId = rows.parentId
    dataForm.key = rows.key
    dataForm.value = rows.value
    dataForm.description = rows.description
    dataForm.sort = rows.sort
  }
  const onDeleteDictionary = rows => {
    Modal.confirm({
      title: '警告',
      icon: h(ExclamationCircleOutlined),
      content: '是否确认删除?',
      okText: '确认',
      cancelText: '取消',
      onOk() {
        deleteDictionary({ ids: [rows.id] })
          .then(async res => {
            message.success('删除成功')
            dictionaryData.value = await getDictionaryData(categoryList.value[activeKey.value]?.id)
          })
          .catch(err => {})
      }
    })
  }
  const onDialogConfirm = async () => {
    const rows = await editDictionary(dataForm)
    diaLogData.show = false
    dictionaryData.value = await getDictionaryData(categoryList.value[activeKey.value]?.id)
    message.success(rows > 0 ? '更新成功' : '更新失败')
  }
  return {
    onCategoryItem,
    diaLogData,
    dataForm,
    onAddDictionary,
    onEditDictionary,
    onDeleteDictionary,
    onDialogConfirm
  }
}
const { scrollHeight, cardHeight, height } = useWindowRise()
const { loading, activeKey, categoryList, dictionaryColumn, dictionaryData, getDictionaryData } = useInitData()
const { onCategoryItem, diaLogData, dataForm, onAddDictionary, onEditDictionary, onDeleteDictionary, onDialogConfirm } = useAction()
</script>

<style lang="scss" scoped>
.dictionary {
  width: 100%;
  height: 100%;
  background: #fff;
  box-sizing: border-box;
}

.split-theme {
  height: v-bind(height);
}

.dictionary-category {
  padding: 4px 0;
}

.dictionary-category-item {
  padding: 8px;
  margin: 4px;
  color: #515a6e;
  font-size: 14px;
  font-weight: 400;
  cursor: pointer;
  border-radius: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  user-select: none;
  transition: all 0.5s;
  &.active {
    background-color: #3963bc33 !important;
    color: #3963bc;
    font-weight: 600;
  }
  &:hover {
    background-color: #f3f3f3;
  }
}

.split-header {
  height: 60px;
  padding: 16px;
  /* box-sizing: border-box; */
  border-bottom: 1px solid #f0f0f0;
  display: flex;
  align-items: center;
  gap: 16px;
  justify-content: space-between;
  .action {
    display: flex;
    align-items: center;
    gap: 16px;
  }
}
.split-list {
  padding: 16px;
  user-select: none;
  overflow: hidden;
}
:deep(.splitpanes--vertical > .splitpanes__splitter) {
  background: #f0f0f0a8 !important;
  width: 1px;
  margin: 8px 0;
  &::after {
    content: '';
    width: 200px;
    height: 200px;
    background: url(../../../assets/images/split.png);
    position: absolute;
    top: 50%;
    margin: -100px 0 0 -100px;
    border-radius: 8px;
    transform: scale(0.2);
  }
}

:deep(.ant-card-body) {
  padding: 0px !important;
}

:deep(.ant-spin-nested-loading) {
  position: initial !important;
}

:deep(.ant-spin-nested-loading .ant-spin-container) {
  position: initial !important;
}

:deep(.splitpanes__pane) {
  position: relative !important;
  min-width: 150px;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值