黑马《苍穹外卖》Day1—Day2

Day-1

开发环境搭建

git

.gitinore设置忽略哪些文件的git

**/target/
.idea
*.iml
*.class
*Test.java
**/test/

 1.创建本地仓库

 

变颜色+出现git图标 

 

现无远程仓库,先commit 

 2.创建远程仓库

 

 

 

 账号为手机号,密码为正常密码

3.本地代码提交远程 

点击push 

 

 

 数据库环境搭建

 1.打开mysql服务先

 2.navicat新建查询→运行

 前后端联调

1.双击compile 编译

保证项目编译通过

 

 

问题一: Maven导入依赖、编译速度慢
解决视频链接

step1.在maven的setting.xml下添加阿里云镜像

step2.复制到IDEA与maven约定的默认地址

因为IDEA默认使用与maven约定的地址,在C:\Users\金海宇\.m2下找setting.xml,所以将修改好的setting.xml复制过去

 

 2.运行server的application,实现登录

 问题二:数据库连接问题
2023-12-26 16:04:41.414 ERROR 13996 --- [eate-1875304119] com.alibaba.druid.pool.DruidDataSource   : create connection SQLException, url: jdbc:mysql://localhost:3306/sky_take_out?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true, errorCode 1045, state 28000

java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.30.jar:8.0.30]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.30.jar:8.0.30]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:828) ~[mysql-connector-java-8.0.30.jar:8.0.30]
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:448) ~[mysql-connector-java-8.0.30.jar:8.0.30]
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241) ~[mysql-connector-java-8.0.30.jar:8.0.30]
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198) ~[mysql-connector-java-8.0.30.jar:8.0.30]
	at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1651) ~[druid-1.2.1.jar:1.2.1]
	at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1717) ~[druid-1.2.1.jar:1.2.1]
	at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2785) ~[druid-1.2.1.jar:1.2.1]

修改数据库账号密码

 3.断点调试跟踪代码

目的:看功能如何实现

1.debug运行

 2.打断点

 3.前端再次登录

 点step over进行下一句代码

进入login方法中加断点,让程序停在此处,点击放行按钮到下一个断点或运行结束

 

debug后的代码能看到其具体的值

总结:

step over一句一句 F8

resume program直接到下一个断点或执行完 F9

 完善登录功能

TODO用于标记尚未完成的功能 

下次打开直接TODO锁定未完成的位置

navicat修改完记得点下面√保存

 导入接口文档

 

YApi 导入接口文档

 

 

 swagger接口测试

 

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
    /**
     * 通过knife4j生成接口文档
     * @return
     */
    @Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    /**
     * 设置静态资源映射
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

若无静态资源映射,springmvc框架不认为/doc.html是静态页面,而以为是某个controller

 运行代码

 

 Day-2

 新增员工

 DTO接收前端数据

 

 alt+回车——创建方法

右上绿色圆标可跳转到Impl 

alt+回车 创建方法 

瞄准镜图标——锁定路径

常量类——统一修改常量

 step1:创建DTO封装前端传来的数据

sky-pojo/src/main/java/com/sky/dto/EmployeeDTO.java

@Data
public class EmployeeDTO implements Serializable {

    private Long id;

    private String username;

    private String name;

    private String phone;

    private String sex;

    private String idNumber;

}

step2: controller创建方法,调用service

sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java

    /**
     * 新增员工
     * @param employeeDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增员工")
    public Result save(@RequestBody EmployeeDTO employeeDTO){
        log.info("新增员工:{}",employeeDTO);
        employeeService.save(employeeDTO);
        return Result.success();
    }

 step3:service创建方法,绿色圆I图标跳到Impl

sky-server/src/main/java/com/sky/service/EmployeeService.java 

    /**
     * 新增员工
     * @param employeeDTO
     */
    void save(EmployeeDTO employeeDTO);

 step4:Impl实现方法

sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java

    /**
     * 新增员工
     * @param employeeDTO
     */
    @Override
    public void save(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();

        若属性名不一致,一个一个设置
        //employee.setName(employeeDTO.getName());

        //对象属性拷贝
        //属性名一致,一次性拷贝属性
        BeanUtils.copyProperties(employeeDTO,employee);

        //设置账号状态
        employee.setStatus(StatusConstant.ENABLE);

        //设置账号密码
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        //设置当前记录创建时间和修改时间
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        //设置当前记录创建人和修改人id
        //TODO 后期改为当前登录用户id
        employee.setCreateUser(10L);
        employee.setUpdateUser(10L);

        //封装完毕,调用持久层插入
        employeeMapper.insert(employee);
    }

step5:mapper实现数据库操作 

sky-server/src/main/resources/application.yml

mybatis:
  #mapper配置文件
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.sky.entity
  configuration:
    #开启驼峰命名
    map-underscore-to-camel-case: true

 sky-server/src/main/java/com/sky/mapper/EmployeeMapper.java

简单数据库操作直接注解即可

    /**
     * 插入员工数据
     * @param employee
     */
    @Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user, status) " +
            " values " +
            "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
    void insert(Employee employee);

配置SQL提示 

 

功能测试

 

返回401,因为没有通过jwt拦截器 ,令牌校验不通过,所以要在接口文档统一添加一个jwt令牌 

 

 

代码完善 

 

 

 

 控制台无异常,因为数据库抛出的 异常被异常处理方法捕获。

    /**
     * 处理SQL异常
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
        //Duplicate entry 'zhangsan' for key 'idx_username'
        String message = ex.getMessage();
        if (message.contains("Duplicate entry")){
            //根据空格分隔成数组
            String[] split = message.split(" ");
            String username = split[2];
            String msg = username + MessageConstant.ALREADY_EXISTS;
            return Result.error(msg);
        }else {
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }
    }

 每一次请求为一个线程

 

拦截器处存进去 

 service处取出来

 

 

员工分页查询

 

@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的),所以只能发送POST请求。

GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。

在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。

 step1: DTO,Result,PageResult

sky-pojo/src/main/java/com/sky/dto/EmployeePageQueryDTO.java 

@Data
public class EmployeePageQueryDTO implements Serializable {

    //员工姓名
    private String name;

    //页码
    private int page;

    //每页显示记录数
    private int pageSize;

}

sky-common/src/main/java/com/sky/result/PageResult.java 

Lombok常用注解

/**
 * 封装分页查询结果
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

    private long total; //总记录数

    private List records; //当前页数据集合

}

sky-common/src/main/java/com/sky/result/Result.java 

/**
 * 后端统一返回结果
 * @param <T>
 */
@Data
public class Result<T> implements Serializable {

    private Integer code; //编码:1成功,0和其它数字为失败
    private String msg; //错误信息
    private T data; //数据

    public static <T> Result<T> success() {
        Result<T> result = new Result<T>();
        result.code = 1;
        return result;
    }

    public static <T> Result<T> success(T object) {
        Result<T> result = new Result<T>();
        result.data = object;
        result.code = 1;
        return result;
    }

    public static <T> Result<T> error(String msg) {
        Result result = new Result();
        result.msg = msg;
        result.code = 0;
        return result;
    }

}

step2: controller

因为前端传的数据格式是query不是json,所以无需@RequestBody,spring mvc框架可正常将数据封装好 

sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java 

    /**
     * 员工分页查询
     * @param employeePageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("员工分页查询")
    public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
        //为了方便调试
        log.info("员工分页查询:{}",employeePageQueryDTO);
        //pageResult就是data,包含total,records
        PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
        //返回的是全部code,msg,data
        return Result.success(pageResult);
    }

step3: service 

sky-server/src/main/java/com/sky/service/EmployeeService.java 

    /**
     * 分页查询
     * @param employeePageQueryDTO
     * @return
     */
    PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);

step4: Impl 

 分页查询插件

sky-server/pom.xml 

            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>${pagehelper}</version>
            </dependency>

sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java 

    /**
     * 分页查询
     * @param employeePageQueryDTO
     * @return
     */
    @Override
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        // select * from employee limit 0,10 前端传入DTO,根据之动态计算拼接到SQL语句中,用插件方便操作
        //开始分页查询
        PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());

        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);

        long total = page.getTotal();
        List<Employee> records = page.getResult();

        return new PageResult(total,records);
    }

step5: mapper

sky-server/src/main/java/com/sky/mapper/EmployeeMapper.java

    /**
     * 分页查询
     * @param employeePageQueryDTO
     * @return
     */
    Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);

step6: 映射文件

小鸟下载mybatisx插件

动态SQL用注解不方便,所以映射文件

sky-server/src/main/resources/mapper/EmployeeMapper.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.sky.mapper.EmployeeMapper">
    <select id="pageQuery" resultType="com.sky.entity.Employee">
        select * from employee
        <where>
            <if test="name != null and name != '' ">
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>

映射文件被项目扫描到

sky-server/src/main/resources/application.yml

mybatis:
  #mapper配置文件
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.sky.entity
  configuration:
    #开启驼峰命名
    map-underscore-to-camel-case: true

功能测试

返回401,因为token有有效期7200000毫秒,即去三0秒,2h

sky:
  jwt:
    # 设置jwt签名加密时使用的秘钥
    admin-secret-key: itcast
    # 设置jwt过期时间
    admin-ttl: 7200000
    # 设置前端传递过来的令牌名称
    admin-token-name: token

所以swagger重新登陆获取,注意要叉掉标签页,重新进入

 最后操作时间格式有问题

代码完善

 法一,单个处理

 

法二,统一处理(推荐) 

固定代码 

sky-common/src/main/java/com/sky/json/JacksonObjectMapper.java 

package com.sky.json;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

sky-server/src/main/java/com/sky/config/WebMvcConfiguration.java 

    /**
     * 扩展是Spring MVC框架的消息转换器
     * 程序启动时被调用
     * @param converters
     */
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转化器...");
        //创建一个消息转换器对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //将自己的消息转换器加入容器中, 0表示优先级为第一个,因为converters里自带许多消息转换器,默认为最后一个
        converters.add(0,converter);
    }

 

 

   

启用禁用员工账号 

 

 非查询不用泛型

 step1: controller
    /**
     * 启用禁用员工账号
     * @param status
     * @param id
     */
    @PostMapping("/status/{status}")
    @ApiOperation("启用禁用员工账号")
    public Result startOrStop(@PathVariable Integer status,Long id){
        log.info("启用禁用员工账号:{},{}",status,id);
        employeeService.startOrStop(status,id);
        return Result.success();
    }

step2: service  

    /**
     * 启用禁用员工账号
     * @param status
     * @param id
     */
    void startOrStop(Integer status, Long id);

step3: Impl  

动态update,可复用

    /**
     * 启用禁用员工账号
     * @param status
     * @param id
     */
    @Override
    public void startOrStop(Integer status, Long id) {
        //本质:根据id修改status
        //update employee set status = ? where id = ?

//        Employee employee = new Employee();
//        employee.setStatus(status);
//        employee.setId(id);

        //Employee有@Builder注解,可用下面方式,作用同上,根据喜好选择
        Employee employee = Employee.builder()
                .status(status)
                .id(id)
                .build();

        employeeMapper.update(employee);

    }

 step4: mapper

    /**
     * 根据主键动态修改属性
     * @param employee
     */
    void update(Employee employee);

step5: 映射文件 

注意数据库并未驼峰命名且不区分大小写

    <update id="update" parameterType="Employee">
        update employee
        <set>
            <if test="name != null">name = #{name},</if>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="phone != null">phone = #{phone},</if>
            <if test="sex != null">sex = #{sex},</if>
            <if test="idNumber != null">id_Number = #{idNumber},</if>
            <if test="status != null">status = #{status},</if>
            <if test="updateTime != null">update_Time = #{updateTime},</if>
            <if test="updateUser != null">update_User = #{updateUser},</if>
        </set>
        where id = #{id}
    </update>

功能测试 

 

 commit push

 编辑员工

 

 根据id查询员工信息

 step1: controller
    /**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询员工信息")
    public Result<Employee> getById(@PathVariable Long id){
        Employee employee = employeeService.getById(id);
        return Result.success(employee);
    }
step2: service   
    /**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    Employee getById(Long id);
 step3: Impl  
    /**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    @Override
    public Employee getById(Long id) {
        Employee employee = employeeMapper.getById(id);
        employee.setPassword("****");
        return employee;
    }
step4: mapper 
    /**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    @Select("select  * from employee where id = #{id}")
    Employee getById(Long id);
 功能测试

​​​​​​​

 

 编辑员工信息

C
    /**
     * 编辑员工信息
     * @param employeeDTO
     * @return
     */
    @PutMapping
    @ApiOperation("编辑员工信息")
//    只有查询性操作才需要指定泛型,前端传的是json格式,所以加@RequestBody
    public Result update(@RequestBody EmployeeDTO employeeDTO){
        employeeService.update(employeeDTO);
        return Result.success();
    }
    /**
     * 编辑员工信息
     * @param employeeDTO
     * @return
     */
    void update(EmployeeDTO employeeDTO);
    /**
     * 编辑员工信息
     * @param employeeDTO
     * @return
     */
    @Override
    public void update(EmployeeDTO employeeDTO) {

        Employee employee = new Employee();

        若属性名不一致,一个一个设置
        //employee.setName(employeeDTO.getName());

        //对象属性拷贝
        //属性名一致,一次性拷贝属性
        BeanUtils.copyProperties(employeeDTO,employee);

        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(BaseContext.getCurrentId());

        employeeMapper.update(employee);
    }
 M
    /**
     * 根据主键动态修改属性
     * @param employee
     */
    void update(Employee employee);
    <update id="update" parameterType="Employee">
        update employee
        <set>
            <if test="name != null">name = #{name},</if>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="phone != null">phone = #{phone},</if>
            <if test="sex != null">sex = #{sex},</if>
            <if test="idNumber != null">idNumber = #{idNumber},</if>
            <if test="status != null">status = #{status},</if>
            <if test="updateTime != null">updateTime = #{updateTime},</if>
            <if test="updateUser != null">updateUser = #{updateUser},</if>
        </set>
        where id = #{id}
    </update>
报错:空指针问题,此处是因为查询没有的id 
2024-01-03 14:23:33.900 ERROR 8560 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
报错: 执行的SQL语句中引用了数据库表中不存在的列
2024-01-03 14:34:50.719 ERROR 8560 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: 
### Error updating database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'idNumber' in 'field list'

原因:数据库中并非驼峰命名,数据库不区分大小写 

    <update id="update" parameterType="Employee">
        update employee
        <set>
            <if test="name != null">name = #{name},</if>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="phone != null">phone = #{phone},</if>
            <if test="sex != null">sex = #{sex},</if>
            <if test="idNumber != null">id_Number = #{idNumber},</if>
            <if test="status != null">status = #{status},</if>
            <if test="updateTime != null">update_Time = #{updateTime},</if>
            <if test="updateUser != null">update_User = #{updateUser},</if>
        </set>
        where id = #{id}
    </update>

 最后2测试+CM

 导入分类管理功能代码

 

 

 代码导入

从后往前导(ctrlCV),以免报错MY SI C

导入代码可能不会自动编译,手动编译一下

 最后2测试+CM

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值