laravel基础(源码解析)

)什么是composer

 

就是一个专门管理PHP扩展包的工具

composer的介绍和laravel的关系

 

laravel 主要是以组件化的开发模式,也就是把整个框架拆分为不同的单独的零件。每一个零件实际上就是一个单独的组件,每一个组件你都可以在composer的资源包中查找到

 

 

Composer的组成

它主要由三个部分组成:命令行工具、包仓库、代码库

Composer的目录结构

 

直接引用第三方别人写好composer包的工具

$ composer global require "overtrue/package-builder" --prefer-source

$ package-builder build [目标目录名]

 

laravel安装

 

1)Php版本要求7.1版本

2)Openssl秘钥的生成的扩展

3)composer create-project laravel/laravel laravel-composer(项目名)

 

Composer create-project xxxx (创建一个项目)

Composer-builder build xxx

 

框架的主配置文件

  1. .ENV   

laravel框架的主配置文件(数据库配置)

  1. .ENV.example  

配置文件副本

  1. Artisan.php    

命令行执行驱动文件

laravel访问

 

  1. Web服务器访问

2)Laravel内置服务访问(php artisan server)(不推荐使用)

Laravel执行流程

 

1)Url地址通过web.php的路由配置进行路由匹配

Route::get('winner', function () {

    return view('welcome');

});

 

2)业务逻辑分两种情况:

2.1)闭包函数,执行函数体

 

Route::get('winner', function () {

    return view('welcome',[‘name’=>’winer’] );

});

{{ $name }}

 

2.2)控制器的操作

 

Route::get('hello1','Hello1@index');

Xxx/Public/hello1

 

Route::get(index,home\Index@index');

Xxx/home/index/index

 

2.3)返回视图

视图必须以.blade.php结尾

Artisan 快捷创建文件(控制器 模型 数据迁移等)

 

查看命令:  Php artisan

创建控制器:Php artisan make:controller xxx

创建模型:  Php artisan make:model xxx

创建自己的命令

php artisan make:command 文件名 --command = winner::command

执行自己的命令:

php artisan command:winner

laravel的生命周期(请求周期)

 

1)通过入口文件index.php

2)初始化整个laravel框架 (独自创立)

$app = require_once __DIR__./../bootstrap/app.php;

  1. 获取处理用户请求的实例

$kernel=、$app->make(Illuminate\Contracts\Http\Kernel::class);

5)处理用户的请求

$response = $kernel->handle(

$request = Illuminite\Http\Request::capture()

);

  1. 返回处理的结果

$response->send();

6)结束中间件和laravel框架

$kernel->terminate( $request , $response );

 

Laravel路由文件的访问流程

1)在项目中,首先会通过index.php入口文件,由里面的bootstrap/app.php创建出applcation应用实例类

2)然后系统会根据route中所定义的路由生成对应的路由表

3)检测用户访问的请求路由是否合理。如果合理就会进入中间件组对请求做处理

4)然后会进入到控制器,执行用户的请求

5)请求可能会连接到数据库的操作

6)中间件有前置和后置的区分,所以在控制器返回的时候可能会有中间件组,然后经过视图渲染有index.php返回

 

Index.php ->

Bootstrap ->

Route路由->

控制器   ->

数据库   ->

中间件   ->

视图     ->

Tp5.2与laravel5.7的区别

对于框架的看法

 

  1. 开发思想(组件化开发)
  2. 项目的目录结构

3)路由的访问方式

(tp5:pathinfo+路由  laravel:需要先定义路由)

4)请求周期等

 

共同点:

1)都是mvc思想

2)扩展方便

3)ioc容器

4)aop思想

 

这个问题最主要的就是需要自己,在面试的时候能够清楚的描述你的观点,以及框架的主要功能点的作用

AOP思想(面向切面思想)

把相同的执行操作单独提取出来

例如:短信发送,rbac

 

把一个共有的操作,单独抽离出来,需要用到的时候,再调用

 

以前会在基类中的构造函数定义一个这样的公有的方法,然后子类继承这个基类就可以使用

现在是单独提取出来

Laravel 核心思想

 

 

1)组件化开发(车间,很多小零件组成一个功能)

2)IOC容器(控制反转)(作用就是保存着运行的类的实例)

3)application (继承于容器,是laravel的心脏;是框架运行的核心,container离开laravel还可以使用,application离开的话就不行)

4)契约(通过接口的方式去约束服务,服务不规范,可以使用契约去规范)

5)门面(通过门面去介绍服务都有哪些)

6)中间件(管道机制,守护框架)

中间件的理解

  1. 这是一个保护程序围墙

2)中间件分为 :

    前置(进入,例如身份权限验证rbac)

后置(出去)

前置和后置中间件和 注册无关

只和``$response = $next($request);`` 代码的前后顺序有关

 

<?php

namespace App\Http\Middleware;

use Closure;

class CheckAge

{

    public function handle($request, Closure $next)

{

 

     If( $request->age <= 200 ){

        Return  response(“年龄超标”) ;

}

         // 在 $next($request)前执行的就是前置

        

         $response = $next($request); // 闭包,controller

        

         // 在 $next($request); 后置中间

         return $response;

    }

}

?>

中间件的使用

1)定义中间件

<?php

namespace App\Http\Middleware;

use Closure;

class CheckAge

{

    public function handle($request, Closure $next)

{

    }

}

?>

2)需要在 blog\app\Http\Kernel.php(核心) 进行注册

不同场景如下:

2.1)全局:

Protected $middleware = { }

2.2)分组:

Protected $middlewareGroup = { }

2.3)单独中间件,中间件标识,中间件类:Protected $routemiddleware = { }

2.4)中间件执行的顺序:

Protected $middlewarePriority = { }

 

3)路由:

Route::get( ‘char, function(){

   Return “ 通过”;

})->middleware( route-char );

 

4)控制器中的使用:

```php

<?php

class UserController extends Controller

{

    /**

     * Instantiate a new controller instance.

     *

     * @return void

     */

    public function __construct()

    {

        $this->middleware('auth');

        

// 允许哪些方法可以使用

        $this->middleware('log')->only('index');

        

// 不允许哪些方法不可以使用

     $this->middleware('subscribed')->except('store');

    }

}

?>

服务提供者的理解

工具类 -- 项目中常用的方法

服务提供者使用

1)服务提供者的定义

 

app\http\Providers

 

public function register(){}

 

public function boot(){}

 

2)注册

   config/app.php

 Providers

容器的使用

1)定义

App\Util\CharUtil

    

Public function index(){}

 

2)注册

App\providers\RiakServiceProvider

 

美女在一个叫做 container 的app软件上注册了一个家庭地址信息

姓名   家庭地址

Public function register{

  $this->app->singleton( ‘char’ , \app\Util\CharUtil :: class )

}

 

3)控制器使用

    App\http\Controller

App( ‘char’ )->char();

//给app()传递标识的时候就是 从容器中,根据标识获取到对应的服务实例

Char()就是方法

IOC容器

是什么:依赖注入

为什么需要容器:解耦

 

 

 

 

 

<?php

// 废弃 users  使用 admin类

class user{

 

}

 

// admin 功能比 user 强

class admin

{

 

}

 

//使用IOC思想

class ioc

{

    protected $bind = [];

    public function bind($key, $value)

    {

        $this->bind[$key] =  [$value];

    }

 

    public function make($key)

    {

        return $this->bind[$key];

    }

}

 

$ioc = new ioc;

$ioc->bind('admin', function (){

  return new admin;

});

 

// 控制反转 就是把一个类创建的过程,交给了第三者

class client{

    protected $ioc;

    public function __construct()

    {

      $this->ioc = new ioc;

    }

    public function index()

    {

        $this->ioc->make('admin'); // user 创建的控制器权就归于 client  这个类

    }

 

    public function demo()

    {

        $this->ioc->make('admin'); // user 创建的控制器权就归于 index  这个类

    }

}

 //***********************************************************************

 

// di 方法中会以参数的方式传递 真正的实现就是通过反射机制

new index($ioc);

class index{

    protected $ioc;

 

    public function __construct(ioc $ioc) // 泛型的约束;限制传递的参数的类型

    {

        $this->ioc = $ioc;

    }

 

    public function index()

    {

        $this->ioc->make('admin'); // user 创建的控制器权就归于 index  这个类

    }

 

    public function demo()

    {

        $this->ioc->make('admin'); // user 创建的控制器权就归于 index  这个类

    }

}

进阶1:可以使用容器来存放服务类

Application

1)应用实例

2)项目/框架的核心

3)项目核心的应用

4)启动软件程序(需要有用户的信息,启动的方法)

5) Container只是提供的是初始方法

    Application是对应用程序的注册,启动

 

 

 

 

Container关键点解析

 

(Application这个类继承与Container这个类)

1)

$binds这是一个容器类,在里面会保存整个框架运行所需要的服务,本质上是通过key,value以数组的方式保存着实际类在里面

$binds = [

“标识”  =>“实际类的对象”

“route”=> new route();

]

2)

Class Container{

protected static $instance;     

// 单例模式

protected $instances = [];      

// 保存解析出来之后的实例(保存已经使用过的实例)

 

protected $bindings = [];       

// 容器绑定实例 不会立即使用 信息的注册

protected $aliases = [];        

// 记录类库定义的别名

 

 

public function bind(){}        

// 就是绑定到容器的方法

 

public function has(){}         

// 校验是否在容器中

 

public function getInstance(){}   

// 把解析出来之后的容器保存在实例中

 

public function make(){}

// 从容器中解析

}

门面

介绍服务类,这是一个可以快速从容器中调用实例类的方式

 Facades 为应用的 服务容器提供了一个「静态」 接口

门面的使用

1)门面类的定义

app\Facade\char

   Class char extends Facade{

      Protected static function getFacadeAccessor(){

         Return \app\Util\CharUtil::class;

}

}

  2)注册

app\Providers

  Public function register(){

$this->app->singleton( ‘char’ ,

 \App\Util\CharUtil::class );

}

 

3)app\Http\Controller

   Use App\Facade\Char

 

   Char::char();

//通过char门面找到app中绑定的charUtil的实例

契约

直白点就是一个接口类,形象点就是定义了一个规范

 

1)控制器

app\Http\Controllers

Class IndexController extends Controller{

    Public function test{

       Return Char::char();

}

}

 

2)注册

app\providers\RiakServiceProvider

   Public function register(){

       //$this->app->singleton( ‘char’ ,

 \App\Util\CharUtil::class );

       $this->app->singleton( ‘char’ ,

 \App\Util\SVIPCharUtil::class );

}

 

3)工具类

app\Util

Use App\Contracts\CharUtil   as  contracts;

   Class SVIPCharUtil implements contracts{

    // Publlic function char(){

     //   Return xxxxxxxxxxxxxxx;

//  }

     Public function demo(){

       Return ‘this is app util SVIPChartUtil’;

}

}

 

 app\Util;

Use App\Contracts\CharUtil   as  contracts;

Class CharUtil implements contracts{

Publlic function char(){

        Return xxxxxxxxxxxxxxx;

}

}

 

app\Contracts

    Interface CharUtil{

       Public function char(){}

}

进阶2:Contract契约的存在

// 方法的参数可能不一致

// 假设oracle -> mysql

$app = new Application();

$db  = $app->make('db');

echo $db->demo();   

//mysql中有这个demo方法,而换了oracle方法变成desc并且有参数。

 

规范为什么会选择接口,而不选择抽象

  1. 最主要的原因是规范它不仅仅只有一种,抽象类只能继承一次,接口可以多次实现
  2. trait 类中的方法体是一个实例的,无法启动约束的作用

 

 

 

 

 

自定义路由

 

 

 

 

 

 

 

 

 

 

 

(供应者)

1)先在一个控制器上写好路由

2)App\providers\RouteServiceProvider

中的Map()方法负责注册路由

路由不同的请求访问方式

Get , post , put , patch , delete , options , any , match

 

Route::get( ‘test’, ‘TestController@index’);

Route::get( ‘test’, ‘Test\TestController@index’);

Route::match( [‘get’ , ’put’ , ’post’] , ‘test’ , ‘TestController@index’)

 

// 1)允许自定义的方式

Route::match(['get', 'post' , ""], '/', function () {

 

});

 

// 2)允许全部的访问方式

Route::any('foo', function () {

 

});

*/

 

// 3)根据某一些规则分组,前缀,中间件,命名空间

// Route::prefix('pre')->group(function () {

//     Route::get('/', function () {

//         return "pre ii ";

//     });

//

//     Route::get('user', function () {

//         return "pre ii  user";

//     });

// });

 

 

Route::group(["prefix"=>"fix"],function () {

//     Route::get('/', function () {

//         return "fix ii ";

//     });

//

//     Route::get('user', function () {

//         return "fix ii  user";

//     });

// });

 

 

// 根据请求方式定义路由

// Route::get($uri, $callback);

// Route::post($uri, $callback);

// Route::put($uri, $callback);

Laravel核心框架文件

Vendor(卖主,销售公司)

 

Vendor\laravel\framework\src\Illuminate

 

DB 和 model 区别

  1. DB操作数据库的话返回就是一个数组

model返回的是一个当前模型对象,可以通过toArray()转化为数组输出

2)model 对数据表进行抽象的描述,数据表中的字段名就是模型中的属性

dd(Test::where('id',1)->first()->name);

  1. 功能上没什么区别,底层实现: 本质上的话都是依托于

 \Illuminate\Database\DatabaseManager::class

  1. model就是比DB都一些属性维护起来更加方便(表名一旦更改,修改起来更方便)

普通一对一( has one() )

 

 

 

一对多( has many() )

 

 

 

预载入

 

 

 

预载入的理解

 

接口开发的两种方式

1)restful : http json

2)soap : 传输数据的协议 xml

 

用户 (传统接口)

http://xxx.com.user/findUser

http://xxx.com.user/addUser

http://xxx.com.user/delUser

 

soap风格方式(简单 隐式 安全

http://xxx.com/user  get请求   查询

http://xxx.com/user     更put请求新

http://xxx.com/user  delete请求 删除

 

 

 

什么是Csrf

1)Csrf:跨站点请求伪造

2)凡是非get方式请求的url都会进行csrf检测

 

Route::get( ‘csrf’ , function(){

   Return “ get - csrf”;

})

 

Route::post( ‘csrf’ , function(){

   Return “ post - csrf”;

})

 

Csrf解决(跳过检测)

  1. 可以在视图上 添加 @csrf
  2. 中间件添加黑白名单

app\Http\Middleware   

verifyCsrfTokenMiddleware  

protected $exception = [ ]

Csrf的工作流程

1)会生成两个token保存

2)一个通过session的方式保存在服务器端

3)另一个通过cookie的方式保存在客户端

4)如果是非get请求,两者会进行比较

 

 

行为控制器

  1. 特殊时期只需要处理一件事情,称为单个行为控制器

2)例如:错误提示  受到攻击后发邮件

 

php artisan make:controller Only/ShilpController –invokable

 

资源控制器

php artsain make:controller Api/UserController --resourece

 

Route::resource(user,Api\UserController)

 

获取Request对象

 

源码:

真正实现功能是里面的symfonyRequest

 

获取参数直接通过$request来接收请求参数。

$request->input(username);

$request->input(‘password’);

 

Method()

Root()

Url()

Path()

Input()

All()

Cookie()

Session()

 

Has()

Field()

 

Request

在Request类继承SymfonyRequest,而对于这个类中的一些常用的获取请求参数的方法,实际在

Illuminate\Http\Concerns\InteractsWithInput类中。

Response响应

响应:就是返回结果

laravel框架提供了很多种的访问方式,可以返回一个字符串也可以返回数组,而laravel会自动把这些参数内容进行转化为json格式进行输出

这里我们主要会使用到Illuminate\Http\Response类做处理。

 

cookie

因为cookie在客户端,你需要通过http请求把数据返回给请求的客户端。

 

1)设置cookie需要从响应来设置

Response()->cookie(name,value,time());

 

2)获取

Request()->cookie(name);

Cookie::get(name);

 

3)删除

Cookie::forge(name);

 

session

1)设置session

Request()->session()->put(name , value);

 

Session( [ ‘name1’=>’value1’ , ‘name2’=>’value2’]  )

 

2)获取

Request()->session()->all();

 

3)删除session  一个forge   多个flush

 

 

数据库配置

 

 

 

  1. .env文件
  2. 自定义配置文件

   Config\database.php

 

原生sql

预处理

1.占位符:?  

2.按顺序传递值  :Name   按名称传递值。

 

增:

DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']);

删:

DB::delete('delete from users');

改:

DB::update('update users set votes = 100 where name = ?', ['John']);

查:

DB::select('select * from users where id = :id', ['id' => 1]);

 

事务

  1. 自动——闭包事件,事务会自动提交,回滚触发条件是抛出异常

DB::transaction(function(){

DB::update('update users set votes = 100 where name = ?', ['John']);

})

2)手动

DB::beginTransaction();

DB::rollBack();

DB::commit();

Sql监听

可以调用sql语句

Providers\AppServiceProvider.php  

Public function boot(){

DB::listen(function($query){

   Echo $query->sql;

})

}

查询构造器

  1. 查询构造器结果返回的是staClass对象

 

$result  = DB::table('user_base')->get();  
      前端输出值
     foreach ($result as $val){
        echo $val->user_id;
     }

 

  1. 对象转化为数组

 

toArray 只是转化一维结果为数组
      $result  = DB::table('user_base')->get()
         ->map(function ($value){
        return (array)$value;
      })->toArray();

 

3.查找指定字段,不要用*  并且去重

$result  = DB::table('user_base')->select(‘xxxx’ ,’xxxx’ ) ->distinct()->get();

 

4.Where方法的使用 写法

 

$rseult = DB::table('user')->where('uid', 63)->get();

$rseult = DB::table('user')->where('uid', '=', 63)->get();

5.多个条件查询:

 

        $rseult = DB::table('user')->where([

            ['uid'    , '=', 63],

            ['role_id', '=', 1]

        ])->get();

 

        $rseult = DB::table('user')->where([

            'uid'     => 63,

            'role_id'  => 1

        ])->get();

 

6.查询语句写原生条件需要用DB::raw

 

$result = DB::table('user_base')->where(DB::raw("user_name = 'linss' OR user_id > 1"))->get();

        

$result = DB::table('user_base')->where('user_id','>',3)->orWhere('user_name','winner')->get();

 

  1. 分块操作(chunk) 相当于分页        

做大任务的时候

 

chunk 参数:1、分块的大小 2、闭合函数

       

$reuslt = DB::table('user_base')->orderBy('user_id')->chunk(2,function ($citys){

            var_dump($citys);

             //终止分块处理

            return false;

        });

         var_dump($reuslt);

 

分块操作

分两次处理全部

如果你需要处理上千条数据库记录,你可以考虑使用 chunk 方法。该方法一次获取结果集的一小块,并将其传递给 闭包 函数进行处理。该方法在 Artisan 命令 编写数千条处理数据的时候非常有用。

   DB::table('city')->orderBy('city_id')->chunk(100, function ($citys) {

            foreach ($citys as $city) {

                echo $city->city_id.'  name : '.$city->city_name.'<br>';

            }

        });

 

 

8.join == inner join

 

    $result = DB::table('user')

                  ->join('user_role' , 'user.role_id', '=', 'user_role.role_id')

                  ->first();

        $result = DB::table('user')

                  ->join('user_role' , 'user.role_id', '=', 'user_role.role_id')

                  ->join('user_group' , 'user_role.group_id_array',

'=', 'user_group.group_id')

                  ->first();

        $result = DB::table('user')

                  ->join('user_role' , function($role){

                      $role->on('user.role_id', '=', 'user_role.role_id')

->where('user_role.group_id_array', 55);

                  })->first();

        dd($result);

 

9.Ordering, latest / oldest

 

     $result = DB::table('user')

                ->orderBy('uid', 'desc')

                ->get();

 

// latest / oldest  默认使用 created_at 列作为排序依据可以传递自定义的列名:

         $result = DB::table('user')

                ->latest('uid')

                ->get();

      dd($result);

 

10.严格模式

 

注意这里有一个问题 ,在使用这个方法的时候一定要把config/databases.php中的'strict'  => false,修改一下

$result = DB::table('config')

                ->groupBy('key')

                ->get();

 

11.skip / take---》》 limit查询表示查询的数据量,或跳过指定数量的结果

 

$result=DB::table('config')->skip(10)->take(5)->get();

$result=DB::table('config')->offset(10)->limit(5)->get();

12.获取自增id

 

$id = DB::table('user_role')->insertGetId(

          ['role_name'  => '测试','group_id_array' => 54,

'is_role'  => 0,'role_status' => 1,'desc' => '测试']

        );

        dd($id);

 

$result = DB::table('user_role')->where('role_id', 5)->update([

          'role_name' => '管理员'

        ]);

        $result = DB::table('user_role')->where('role_id', 5)->delete();

        dd($result);

 

13.联合查询(join==inner join)

 

first 查询单条

 

$resutl= DB::table('user_base')->join('users','user_base.user_id','=', 'users.id')->first();

 

联合查询闭包方式  (多个表的话,可以使用这个)

 

$data = DB::table('user_base')->join('users',function ($role){

          $role->on('user_base.user_id','=','users.id')->

                where('users.id',2);

        })->first();

        var_dump($data);

 

子查询  子查询就是一个临时表

 

       $last = DB::table ('user_base')

            ->select ('user_id',DB::raw('user_email as email'))

            ->where ('user_id','<=','2')

            ->orderBy ('user_id','desc');

 

        joinsub  子查询  参数:1、结果集 2、临时表别名 3、闭包函数

        $users = DB::table ('users')

            ->joinsub ($last, 'user_base2',function ($join){

                $join->on('users.id','=','user_base2.user_id');

            })->get();

        var_dump($users);

 

order group limit

 

排序:latest降序   oldest 升序  默认按照created_at日期时间排序

        select * from `user_base` order by `user_id` asc

        $result = DB::table('user_base')

            ->oldest('user_id')->get();

        var_dump($result);

分组

        $result = DB::table('user_base')

            ->groupBy('user_status')

            ->get();

        var_dump($result);

 

限制结果集

limit 限制结果显示 offset  偏移量

$result1 = DB::table('user_base')->offset(2)->limit(2)->get();

 

//take 跳转指定数量   skip 偏移量  (这个效率比 limit offset快很多)

 $result = DB::table('user_base')->skip(2)->take(2)->get();

 var_dump($result,$result1);

 

修改数据

$resurt = DB::table('user_base')->where('user_id',4)->update(['user_name'=>'小五老师']);

var_dump($resurt);

Laravel组件开发

对应的项目目录

 

 

Shinyork

Src

   Web

      http

         Controller

      Resource

         View

      Middleware

      Model

      Route

      Composer.json

   Pc

   Mobile

Composer.json

Laravel组件的开发的步骤

 

 

1)composer require xxx

2)Composer-builder build xxx

*********************************

3)Composer init 交互的方式要求我们填写一些信息

4)Composer.json修改后,composer update

5)自定义服务类(数据库)

6)简单测试

引入服务类 new()->方法();

 

进阶1:可以使用容器来存放服务类

Application

1)应用实例

2)项目/框架的核心

3)项目核心的应用

4)启动软件程序(需要有用户的信息,启动的方法)

5) Container只是提供的是初始方法

    Application是对应用程序的注册,启动

 

 

 

 

进阶2:Contract契约的存在

// 方法的参数可能不一致

// 假设oracle -> mysql

$app = new Application();

$db  = $app->make('db');

echo $db->demo();   

//mysql中有这个demo方法,而换了oracle方法变成desc并且有参数。

 

规范为什么会选择接口,而不选择抽象

  1. 最主要的原因是规范它不仅仅只有一种,抽象类只能继承一次,接口可以多次实现
  2. trait 类中的方法体是一个实例的,无法启动约束的作用

 

 

 

 

 

进阶3:Facade门面类的引入

// 快速调用

// 便于维护

 

 

 

 

 

 

Container关键点解析

 

(Application这个类继承与Container这个类)

1)

$binds这是一个容器类,在里面会保存整个框架运行所需要的服务,本质上是通过key,value以数组的方式保存着实际类在里面

$binds = [

“标识”  =>“实际类的对象”

“route”=> new route();

]

2)

Class Container{

protected static $instance;     

// 单例模式

protected $instances = [];      

// 保存解析出来之后的实例(保存已经使用过的实例)

 

protected $bindings = [];       

// 容器绑定实例 不会立即使用 信息的注册

protected $aliases = [];        

// 记录类库定义的别名

 

 

public function bind(){}        

// 就是绑定到容器的方法

 

public function has(){}         

// 校验是否在容器中

 

public function getInstance(){}   

// 把解析出来之后的容器保存在实例中

 

public function make(){}

// 从容器中解析

}

原生代码诠释(简化版)

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值