C# 技术使用笔记:ASP.NET Core 实战开发教程:基于 Dapper ORM 和 Layui 的 Book 管理系统

ASP.NET Core 作为微软推出的一个高性能、跨平台的开源 Web 开发框架,凭借其强大的功能和灵活的扩展性,受到了广大开发者的青睐。它不仅支持多种编程语言,还提供了丰富的中间件和工具,帮助开发者快速构建高效、可靠的 Web 应用。

在实际开发中,数据库操作是 Web 应用的核心功能之一。Dapper 是一个轻量级的 ORM(对象关系映射)工具,它能够高效地将数据库表与 C# 对象进行映射,同时保持代码的简洁性和高性能。Dapper 的使用可以大大简化数据库操作的复杂性,提高开发效率,尤其适合于需要高性能和灵活操作的场景。

前端页面的用户体验同样至关重要。Layui 是一个基于 HTML5 的前端 UI 框架,它提供了丰富的 UI 组件和简洁的样式,能够快速搭建出美观、易用的前端页面。通过 Layui 的表格组件,可以方便地实现数据的展示、分页、排序等功能,提升用户的交互体验。

本教程将结合 ASP.NET Core 8 MVC、Dapper ORM 和 Layui 框架,通过一个完整的实战项目——Book 管理系统,详细讲解如何实现 SQL Server 数据库中 Book 表的增、删、改、查操作。我们将从项目搭建、数据库设计、后端开发到前端页面实现,逐步展开,帮助读者掌握 ASP.NET Core 8 MVC 的开发流程和技巧,以及如何使用 Dapper ORM 和 Layui 框架进行高效开发。通过本教程的学习,读者将能够快速上手并应用到实际项目中,提升开发能力。

1. 项目搭建与环境配置

1.1 创建 ASP.NET Core 8 MVC 项目

  • 打开 Visual Studio 2022 或更高版本,选择“创建新项目”。

  • 在项目类型中选择“ASP.NET Core Web 应用程序”,点击“下一步”。

  • 输入项目名称(如 BookManagementSystem),选择项目保存位置,点击“创建”。

  • 在项目模板中选择“MVC”,确保目标框架为“.NET 8.0”,点击“创建”完成项目创建。

1.2 安装 Dapper 和 SQL Server 驱动

  • 在解决方案资源管理器中右键点击项目名称,选择“管理 NuGet 包”。

  • 在 NuGet 包管理器中搜索并安装以下包:

    • Dapper:用于简化数据库操作的 ORM 工具。

    • Microsoft.Data.SqlClient:用于连接 SQL Server 数据库的驱动程序。

  • 安装完成后,可以在项目的 Dependencies 中看到已安装的包。

1.3 配置数据库连接字符串

  • 打开项目的 appsettings.json 文件。

  • ConnectionStrings 节点中添加数据库连接字符串,示例如下:

    "ConnectionStrings": {
      "DefaultConnection": "Server=你的服务器地址;Database=你的数据库名称;User Id=你的用户名;Password=你的密码;"
    }
  • Program.cs 文件中,通过 Configuration 读取连接字符串,并在 ConfigureServices 方法中注册数据库连接,示例如下:

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.EntityFrameworkCore;
    using System.Data.SqlClient;
    using Dapper;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // 从 appsettings.json 中读取连接字符串
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    
    // 配置数据库上下文
    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString));
    
    // 添加其他服务
    builder.Services.AddControllersWithViews();
    
    var app = builder.Build();
    
    // 配置中间件
    if (app.Environment.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.Run();
  • 通过上述配置,项目可以正确连接到 SQL Server 数据库,并为后续的数据库操作提供支持。

2. 数据库设计与表创建

2.1 设计 Book 表结构

在设计 Book 表时,需要考虑存储书籍的基本信息,包括书名、作者、出版日期、价格等字段。以下是 Book 表的结构设计:

  • Id:主键,使用 int 类型,自增。

  • Title:书名,使用 nvarchar(255) 类型,非空。

  • Author:作者,使用 nvarchar(255) 类型,非空。

  • PublishDate:出版日期,使用 datetime 类型,非空。

  • Price:价格,使用 decimal(18,2) 类型,非空。

  • Description:描述,使用 nvarchar(max) 类型,可空。 根据以上设计,Book 表的结构可以满足存储书籍基本信息的需求,方便后续进行增、删、改、查操作。

2.2 初始化 SQL Server 数据库

在 SQL Server 数据库中创建 Book 表,可以使用以下 SQL 脚本:

CREATE TABLE Book (
    Id INT PRIMARY KEY IDENTITY(1,1),
    Title NVARCHAR(255) NOT NULL,
    Author NVARCHAR(255) NOT NULL,
    PublishDate DATETIME NOT NULL,
    Price DECIMAL(18,2) NOT NULL,
    Description NVARCHAR(MAX)
);

执行上述脚本后,Book 表将被创建在数据库中,为后续的开发工作做好准备。

3. DapperHelper 帮助类开发

3.1 创建 DapperHelper 类

在项目中创建一个名为 DapperHelper.cs 的类文件,用于封装数据库操作的通用方法。该类将提供数据库连接、执行 SQL 语句、查询数据等功能,以简化后续的数据库操作代码。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;

public static class DapperHelper
{
    // 数据库连接字符串
    private static readonly string ConnectionString = "Server=你的服务器地址;Database=你的数据库名称;User Id=你的用户名;Password=你的密码;";

    // 获取数据库连接
    private static IDbConnection GetConnection()
    {
        return new SqlConnection(ConnectionString);
    }
}

3.2 实现数据库连接与关闭方法

DapperHelper 类中,实现数据库连接的打开和关闭方法,确保每次操作数据库时都能正确管理连接资源。

public static class DapperHelper
{
    private static readonly string ConnectionString = "Server=你的服务器地址;Database=你的数据库名称;User Id=你的用户名;Password=你的密码;";

    private static IDbConnection GetConnection()
    {
        return new SqlConnection(ConnectionString);
    }

    // 打开数据库连接
    private static IDbConnection OpenConnection()
    {
        var connection = GetConnection();
        if (connection.State != ConnectionState.Open)
        {
            connection.Open();
        }
        return connection;
    }

    // 关闭数据库连接
    private static void CloseConnection(IDbConnection connection)
    {
        if (connection.State == ConnectionState.Open)
        {
            connection.Close();
        }
    }
}

3.3 封装增删改查通用方法

DapperHelper 类中,封装增删改查的通用方法,以便在后续的业务逻辑中直接调用这些方法,减少重复代码。

using Microsoft.Data.SqlClient;
using System.Data;
using Dapper;

namespace BookManagementSystem.Common
{
    public static class DapperHelper
    {
        static DapperHelper()
        {
            // 从配置文件中读取连接字符串
            var configuration = new ConfigurationBuilder()
                .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
                .AddJsonFile("appsettings.json")
                .Build();

            ConnectionString = configuration.GetConnectionString("DefaultConnection");
        }
        private static readonly string ConnectionString ;

        private static IDbConnection GetConnection()
        {
            return new SqlConnection(ConnectionString);
        }

        private static IDbConnection OpenConnection()
        {
            var connection = GetConnection();
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            return connection;
        }

        private static void CloseConnection(IDbConnection connection)
        {
            if (connection.State == ConnectionState.Open)
            {
                connection.Close();
            }
        }

        // 插入数据
        public static int Insert<T>(string sql, T parameters)
        {
            using (var connection = OpenConnection())
            {
                var result = connection.Execute(sql, parameters);
                CloseConnection(connection);
                return result;
            }
        }

        // 更新数据
        public static int Update<T>(string sql, T parameters)
        {
            using (var connection = OpenConnection())
            {
                var result = connection.Execute(sql, parameters);
                CloseConnection(connection);
                return result;
            }
        }

        // 删除数据
        public static int Delete(string sql, object parameters)
        {
            using (var connection = OpenConnection())
            {
                var result = connection.Execute(sql, parameters);
                CloseConnection(connection);
                return result;
            }
        }

        // 查询单条数据
        public static T QueryFirstOrDefault<T>(string sql, object parameters = null)
        {
            using (var connection = OpenConnection())
            {
                var result = connection.QueryFirstOrDefault<T>(sql, parameters);
                CloseConnection(connection);
                return result;
            }
        }

        // 查询多条数据
        public static List<T> Query<T>(string sql, object parameters = null)
        {
            using (var connection = OpenConnection())
            {
                var result = connection.Query<T>(sql, parameters).ToList();
                CloseConnection(connection);
                return result;
            }
        }
    }
}

通过上述封装,DapperHelper 类提供了通用的数据库操作方法,使得在后续的业务逻辑中可以更加方便地进行增删改查操作,同时保证了代码的简洁性和可维护性。

4. 后端开发

4.1 创建 Book 模型类

在项目中创建一个名为 Book.cs 的类文件,用于定义书籍的模型类。该类将包含与数据库中 Book 表对应的属性,代码如下:

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public DateTime PublishDate { get; set; }
    public decimal Price { get; set; }
    public string Description { get; set; }
}

通过定义 Book 模型类,可以方便地在代码中表示和操作书籍数据,使其与数据库表结构保持一致,为后续的业务逻辑开发提供基础。

4.2 创建 Book 服务类

在项目中创建一个名为 BookService.cs 的类文件,用于封装与书籍相关的业务逻辑。该类将依赖于 DapperHelper 类来实现对数据库的操作,代码如下:

using System.Collections.Generic;
using System.Linq;

public class BookService
{
    // 获取所有书籍
    public List<Book> GetAllBooks()
    {
        string sql = "SELECT * FROM Book";
        return DapperHelper.Query<Book>(sql);
    }

    // 根据 ID 获取书籍
    public Book GetBookById(int id)
    {
        string sql = "SELECT * FROM Book WHERE Id = @Id";
        return DapperHelper.QueryFirstOrDefault<Book>(sql, new { Id = id });
    }

    // 添加书籍
    public int AddBook(Book book)
    {
        string sql = "INSERT INTO Book (Title, Author, PublishDate, Price, Description) VALUES (@Title, @Author, @PublishDate, @Price, @Description)";
        return DapperHelper.Insert(sql, book);
    }

    // 更新书籍
    public int UpdateBook(Book book)
    {
        string sql = "UPDATE Book SET Title = @Title, Author = @Author, PublishDate = @PublishDate, Price = @Price, Description = @Description WHERE Id = @Id";
        return DapperHelper.Update(sql, book);
    }

    // 删除书籍
    public int DeleteBook(int id)
    {
        string sql = "DELETE FROM Book WHERE Id = @Id";
        return DapperHelper.Delete(sql, new { Id = id });
    }
}

通过创建 BookService 类,将书籍相关的业务逻辑集中管理,使得代码更加清晰和易于维护。同时,利用 DapperHelper 类提供的通用方法,可以高效地实现对数据库的增删改查操作,提高开发效率。

4.3 实现 Book 控制器

在项目中创建一个名为 BooksController.cs 的控制器文件,用于处理与书籍相关的 HTTP 请求。该控制器将依赖于 BookService 类来实现具体的业务逻辑,代码如下:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

public class BooksController : Controller
{
    private readonly BookService _bookService;

    public BooksController()
    {
        _bookService = new BookService();
    }

    // 获取所有书籍
    [HttpGet]
    public IActionResult Index()
    {
        List<Book> books = _bookService.GetAllBooks();
        return View(books);
    }

    // 添加书籍
    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create(Book book)
    {
        if (ModelState.IsValid)
        {
            _bookService.AddBook(book);
            return RedirectToAction("Index");
        }
        return View(book);
    }

    // 编辑书籍
    [HttpGet]
    public IActionResult Edit(int id)
    {
        Book book = _bookService.GetBookById(id);
        if (book == null)
        {
            return NotFound();
        }
        return View(book);
    }

    [HttpPost]
    public IActionResult Edit(Book book)
    {
        if (ModelState.IsValid)
        {
            _bookService.UpdateBook(book);
            return RedirectToAction("Index");
        }
        return View(book);
    }

    // 删除书籍
    [HttpGet]
    public IActionResult Delete(int id)
    {
        Book book = _bookService.GetBookById(id);
        if (book == null)
        {
            return NotFound();
        }
        return View(book);
    }

    [HttpPost]
    public IActionResult DeleteConfirmed(int id)
    {
        _bookService.DeleteBook(id);
        return RedirectToAction("Index");
    }
}

通过实现 BooksController 控制器,可以将前端页面的请求与后端的业务逻辑进行有效的连接。控制器中的每个方法对应一种 HTTP 请求类型和业务操作,通过调用 BookService 类中的方法来完成具体的数据库操作,从而实现书籍的增删改查功能。同时,利用 ASP.NET Core MVC 的路由机制和视图引擎,可以方便地生成和管理前端页面,为用户提供良好的交互体验。

5. 前端开发

5.1 引入 Layui 框架

在 ASP.NET Core 8 MVC 项目中引入 Layui 框架,可以方便地实现前端页面的美化和功能增强。具体步骤如下:

  • 在项目的 wwwroot 文件夹中创建一个名为 layui 的文件夹,用于存放 Layui 相关资源。

  • 从 Layui 官方网站下载最新版本的 Layui 压缩包,解压后将其中的 cssjs 文件夹复制到 wwwroot/layui 文件夹中。

  • _Layout.cshtml 文件中引入 Layui 的 CSS 和 JavaScript 文件,代码如下:

    <!-- 引入 Layui CSS -->
    <link rel="stylesheet" href="~/layui/css/layui.css" media="all">
    <!-- 引入 Layui JavaScript -->
    <script src="~/layui/layui.js" charset="utf-8"></script>

通过上述步骤,Layui 框架将被成功引入到项目中,为后续的前端页面开发提供支持。

5.2 创建 Book 列表页面

创建一个名为 Index.cshtml 的视图文件,用于展示书籍列表页面。在该页面中,使用 Layui 的表格组件来显示书籍数据,并添加操作按钮用于编辑和删除书籍。代码如下:

@model List<Book>

@{
    ViewData["Title"] = "书籍列表";
}

<h2>书籍列表</h2>

<!-- 添加书籍按钮 -->
<button class="layui-btn" onclick="window.location.href='/Books/Create'">添加书籍</button>

<!-- 表格容器 -->
<table id="bookTable" lay-filter="bookTableFilter"></table>

@section Scripts {
    <script>
        layui.use(['table'], function () {
            var table = layui.table;

            // 初始化表格
            table.render({
                elem: '#bookTable',
                url: '/Books/GetBooks', // 数据接口
                cols: [[ // 表头
                    { field: 'id', title: 'ID', width: 50, sort: true },
                    { field: 'title', title: '书名', width: 150 },
                    { field: 'author', title: '作者', width: 150 },
                    { field: 'publishDate', title: '出版日期', width: 150, sort: true },
                    { field: 'price', title: '价格', width: 100, sort: true },
                    { field: 'description', title: '描述', width: 200 },
                    { field: 'right', title: '操作', width: 150, toolbar: '#operationBar' }
                ]],
                page: true // 开启分页
            });

            // 操作栏模板
            var operationBar = document.getElementById('operationBar');
            operationBar.innerHTML = '<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>' +
                                     '<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="delete">删除</a>';

            // 监听操作栏事件
            table.on('tool(bookTableFilter)', function (obj) {
                var data = obj.data; // 获取当前行的数据
                var layEvent = obj.event; // 获取 lay-event 对应的值
                var tr = obj.tr; // 获取当前行 tr 的 DOM 对象

                if (layEvent === 'edit') {
                    window.location.href = '/Books/Edit/' + data.id; // 跳转到编辑页面
                } else if (layEvent === 'delete') {
                    layer.confirm('确定要删除这本书吗?', function (index) {
                        $.post('/Books/DeleteConfirmed', { id: data.id }, function (result) {
                            if (result.success) {
                                layer.msg('删除成功');
                                table.reload(); // 刷新表格
                            } else {
                                layer.msg('删除失败');
                            }
                        });
                    });
                }
            });
        });
    </script>
}

在上述代码中,使用 Layui 的表格组件 table 来渲染书籍数据,并通过 url 属性指定数据接口 /Books/GetBooks,该接口将在后端通过 BookService 类获取书籍数据并返回 JSON 格式。同时,在表格的每一行中添加了操作按钮,用于编辑和删除书籍,通过监听操作栏事件来实现相应的功能。

5.3 实现表格数据加载与操作

为了实现表格数据的加载和操作功能,需要在后端添加一个名为 GetBooks 的方法,用于返回书籍数据的 JSON 格式。在 BooksController.cs 文件中添加以下代码:

// 获取书籍数据
[HttpGet]
public IActionResult GetBooks()
{
    List<Book> books = _bookService.GetAllBooks();
    return Json(new { code = 0, msg = "", data = books });
}

在上述代码中,GetBooks 方法调用 BookService 类的 GetAllBooks 方法获取所有书籍数据,并将其包装成 Layui 表格所需的 JSON 格式返回。

对于编辑和删除操作,已经在前端页面的 JavaScript 代码中通过监听操作栏事件实现了。当点击编辑按钮时,会跳转到编辑页面;当点击删除按钮时,会弹出确认框,确认后通过发送 POST 请求到 /Books/DeleteConfirmed 方法来删除书籍。在 BooksController.cs 文件中,DeleteConfirmed 方法已经实现了删除书籍的逻辑。

通过上述步骤,实现了书籍列表页面的表格数据加载与操作功能,用户可以在前端页面中方便地查看、编辑和删除书籍信息。

6. 测试与优化

6.1 测试增删改查功能

在完成 ASP.NET Core 8 MVC 项目开发后,需要对书籍的增删改查功能进行全面测试,确保系统的稳定性和可靠性。以下是测试的具体步骤和方法:

  • 添加书籍测试

    • 打开浏览器,访问书籍列表页面,点击“添加书籍”按钮,跳转到添加书籍页面。

    • 在添加书籍表单中,输入书名、作者、出版日期、价格等信息,点击“提交”按钮。

    • 检查数据库中是否成功插入了一条新的书籍记录,同时检查页面是否正确跳转回书籍列表页面,并显示新增的书籍信息。

    • 测试边界条件,如输入空的书名或作者,检查系统是否能够正确提示用户输入错误信息。

  • 编辑书籍测试

    • 在书籍列表页面,选择一本书籍,点击“编辑”按钮,跳转到编辑书籍页面。

    • 修改书籍的部分信息,如书名或价格,点击“提交”按钮。

    • 检查数据库中对应的书籍记录是否被正确更新,同时检查页面是否正确跳转回书籍列表页面,并显示更新后的书籍信息。

    • 测试编辑功能的边界条件,如将价格设置为负数或输入非法的日期格式,检查系统是否能够正确处理并提示用户。

  • 删除书籍测试

    • 在书籍列表页面,选择一本书籍,点击“删除”按钮,弹出确认框。

    • 点击“确定”按钮,检查数据库中对应的书籍记录是否被正确删除,同时检查页面是否正确刷新并移除了该书籍信息。

    • 点击“取消”按钮,检查书籍信息是否保持不变,确保删除操作的可撤销性。

  • 查询书籍测试

    • 在书籍列表页面,通过分页功能查看不同页码的书籍数据,检查分页功能是否正常工作。

    • 检查表格中显示的书籍信息是否与数据库中的记录一致,包括书名、作者、出版日期、价格等字段。

    • 测试表格的排序功能,如点击“出版日期”或“价格”列标题,检查表格是否能够按照相应的字段进行升序或降序排序。

通过上述测试步骤,可以全面验证书籍增删改查功能的正确性和稳定性,确保系统能够满足用户的基本操作需求。

6.2 性能优化与异常处理

在系统测试过程中,除了功能测试外,还需要关注系统的性能和异常处理能力,以提升用户体验和系统的可靠性。以下是性能优化和异常处理的具体措施:

  • 性能优化

    • 数据库查询优化:在 BookService 类中,对 SQL 查询语句进行优化,避免使用复杂的嵌套查询和大量的数据扫描。例如,在获取书籍列表时,可以使用索引来加速查询速度,减少数据库的响应时间。

    • 缓存机制:对于一些不经常变动的数据,如书籍列表,可以引入缓存机制。使用 ASP.NET Core 的内存缓存或分布式缓存(如 Redis),将书籍数据缓存起来,减少对数据库的直接查询次数,提高系统的响应速度。

    • 异步操作:在控制器中,将数据库操作改为异步方法,如使用 Taskasync/await 关键字。这样可以避免阻塞主线程,提高系统的并发处理能力,提升用户体验。

    • 前端优化:在前端页面中,对 Layui 表格的渲染进行优化,减少不必要的 DOM 操作。例如,通过分页功能限制每次显示的书籍数量,避免一次性加载过多数据导致页面卡顿。

  • 异常处理

    • 全局异常处理:在 Startup.cs 文件中,配置全局异常处理中间件,捕获系统运行时的异常。当发生异常时,记录异常信息到日志文件中,并向用户显示友好的错误提示页面,避免直接将异常信息暴露给用户。

    • 数据库异常处理:在 DapperHelper 类中,对数据库操作的异常进行捕获和处理。例如,在执行 SQL 语句时,如果发生数据库连接失败或 SQL 语法错误,记录详细的异常信息,并抛出自定义的异常,以便在上层业务逻辑中进行统一处理。

    • 业务逻辑异常处理:在 BookService 类中,对业务逻辑的异常进行处理。例如,在添加书籍时,如果发现书名或作者为空,抛出自定义的业务异常,并在控制器中捕获该异常,向用户提示输入错误信息。

通过性能优化和异常处理措施的实施,可以显著提升系统的性能和稳定性,为用户提供更加流畅和可靠的使用体验。

7. 总结

通过上述章节的详细阐述,我们已经完成了一个基于 ASP.NET Core 8 MVC 的实战开发项目,实现了对 SQL Server 数据库中 Book 表的增、删、改、查操作,并使用 Dapper ORM 进行数据库操作,同时利用 Layui 框架美化前端页面并实现数据展示。

在项目开发过程中,我们从项目搭建与环境配置开始,逐步完成了数据库设计、DapperHelper 帮助类开发、后端业务逻辑实现以及前端页面开发。通过合理的分层架构设计,我们将项目分为数据访问层、业务逻辑层和表示层,使得代码结构清晰、易于维护和扩展。

在数据访问层,我们通过 DapperHelper 类封装了通用的数据库操作方法,包括增、删、改、查等,提高了代码的复用性和开发效率。在业务逻辑层,我们创建了 BookService 类,集中管理与书籍相关的业务逻辑,通过调用 DapperHelper 类实现对数据库的具体操作。在表示层,我们利用 ASP.NET Core MVC 的路由机制和视图引擎,结合 Layui 框架的表格组件,实现了书籍列表的展示、添加、编辑和删除功能,为用户提供了良好的交互体验。

在开发过程中,我们注重代码的规范性和可读性,遵循了良好的编程习惯。同时,我们也对系统的性能和异常处理进行了优化和处理,通过数据库查询优化、缓存机制、异步操作以及全局异常处理等措施,提升了系统的性能和稳定性,确保了用户在使用过程中的流畅体验。

通过本项目的实战开发,读者可以深入理解 ASP.NET Core 8 MVC 的开发流程和技巧,掌握 Dapper ORM 的使用方法,以及 Layui 框架在前端页面开发中的应用。希望本教程能够为读者在实际项目开发中提供有价值的参考和借鉴。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

caifox菜狐狸

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值