选课模块-01添加免费/收费选课

添加选课

界面原型

第一步:用户通过搜索课程、课程推荐等信息进入课程详情页面,点击马上学习进行学习

在这里插入图片描述

第二步:课程免费时可以直接加入我的课程表并且免费课程可以直接在线学习,免费课程默认一年有效期,到期需要申请续期

在这里插入图片描述

第三步:课程收费时需要下单支付,支付成功后会自动加入我的课程表

在这里插入图片描述

数据模型

选课记录表(xc_choose_couse):将课程添加到课程表时会先创建对应的选课记录,数据来源于课程发布表

  • 免费课程:选课状态默认为选课成功, 课程价格为0,有效期默认365天,开始服务时间为选课时间,结束服务时间为选课时间加1年后的时间

  • 收费课程:选课状态默认为待支付,课程价格为课程的现价,开始服务时间为支付成功时间,有效期由用户决定,结束服务时间为选课时间加有效期

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我的课程表(xc_couse_table): 记录了用户选课成功的课程(免费课程和已经支付的收费课程),我的课程表的数据来源于选课记录表

  • 选择免费课程: 创建完选课记录后同时向我的课程表添加选课信息

  • 选择收费课程: 创建完选课记录后需要下单且支付成功后会自动向我的课程表添加选课信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请求响应模型类

请求参数:课程id、当前用户id

响应模型类: 除了包含用户的选课记录信息还有用户的学习资格

@Data
@ToString
public class XcChooseCourseDto extends XcChooseCourse {

    //学习资格
    /* [{"code":"702001","desc":"正常学习"},
    	{"code":"702002","desc":"没有选课或选课后没有支付"},
    	{"code":"702003","desc":"已过期需要申请续期或重新支付"}]
    */
    public String learnStatus;

}

环境搭建

第一步: 在项目工程根目录下创建工程xuecheng_plus_learning及其对应的数据库xc_learning

第二步: 在本地创建bootstrap.yml文件

spring:
  application:
    name: learning-api
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      discovery:
        namespace: dev
        group: xuecheng-plus-project
      config:
        namespace: dev
        group: xuecheng-plus-project
        file-extension: yaml
        refresh-enabled: true
        extension-configs:
          - data-id: learning-service-${spring.profiles.active}.yaml
            group: xuecheng-plus-project
            refresh: true
        shared-configs:
          - data-id: swagger-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: logging-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: feign-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: rabbitmq-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
  profiles:
    active: dev

第三步: 在Nacos的dev环境下创建远程配置文件learning-api-dev.yamllearning-service-dev.yaml

server:
  servlet:
    context-path: /learning
  port: 63020
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/xc_learning?serverTimezone=UTC&userUnicode=true&useSSL=false&
    username: root
    password: 123456

第四步:在nacos上的gateway-dev.yaml添加路由到学习中心服务的配置

- id: learning-api
    uri: lb://learning-api
    predicates:
    - Path=/learning/**

选课流程

网站的课程有免费和收费两种,学生选课是将课程加入我的课程表的过程

  • 免费课程: 学生选课后可直接加入我的课程表直接学习
  • 收费课程: 学生需要下单且支付成功后会自动加入我的课程表

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

课程发布信息查询接口

第一步:在内容管理服务提供查询课程信息接口课程发布表查询课程最终的发布信息,此接口主要其它微服务远程调用所以不用授权(本项目标记此类接口统一以/r开头)

@Slf4j
@RestController
public class CoursePublishController {
    @Autowired
    private CoursePublishService coursePublishService;

    @ApiOperation("查询课程发布信息")
    @ResponseBody
    @GetMapping("/r/coursepublish/{courseId}")
    public CoursePublish getCoursepublish(@PathVariable("courseId") Long courseId) {
        CoursePublish coursePublish = coursePublishService.getCoursePublish(courseId);
        return coursePublish;
    }
}

第二步:定义CoursePublishService接口及其实现类查询课程发布信息

CoursePublish getCoursePublish(Long courseId);

@Slf4j
@Service
public class CoursePublishServiceImpl implements CoursePublishService {
    @Autowired
    private CoursePublishMapper coursePublishMapper;

    public CoursePublish getCoursePublish(Long courseId){
        CoursePublish coursePublish = coursePublishMapper.selectById(courseId);
        return coursePublish;
    }
}

第三步:由于是在网关处校验令牌的合法性,在微服务处不再校验令牌的合法性,修改内容管理服务content-api工程的ResouceServerConfig类屏蔽authenticated()

@Override
public void configure(HttpSecurity http) throws Exception {
    http.csrf().disable() // 禁用CSRF保护
        .authorizeRequests()// 配置对请求的授权策略
        // authenticated表示指定"/r/"和"/course/"这两个路径需要进行身份认证才能访问
        //.antMatchers("/r/**","/course/**").authenticated()
        .anyRequest().permitAll();// 允许除了上面指定的路径之外的其他请求都可以被访问,不需要进行身份认证
}

第四步:启动内容管理服务,使用httpclient测试查询课程发布信息的接口

### 查询课程发布信息
GET {{content_host}}/content/r/coursepublish/2

远程调用查询课程信息接口

第一步:在学习中心模块的启动类上添加@EnableFeignClients(basePackages={“com.xuecheng.*.feignclient”})

@EnableFeignClients(basePackages = {"com.xuecheng.*.feignclient"})
@SpringBootApplication
public class LearningApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(LearningApiApplication.class, args);
    }
}

第二步:在学习中心模块(learning)service工程中添加Feign接口feignclient/ContentServiceClient和降级方法feignclient/ContentServiceClientFallbackFactory,远程调用内容管理模块提供的查询课程发布信息的接口

@FeignClient(value = "content-api", fallbackFactory = ContentServiceClientFallbackFactory.class)
@RequestMapping("/content")
public interface ContentServiceClient {

    @GetMapping("/r/coursepublish/{courseId}")
    CoursePublish getCoursePublish(@PathVariable("courseId") Long courseId);
}
@Slf4j
@Component
public class ContentServiceClientFallbackFactory implements FallbackFactory<ContentServiceClient> {
    @Override
    public ContentServiceClient create(Throwable throwable) {
        return new ContentServiceClient() {
            @Override
            public CoursePublish getCoursePublish(Long courseId) {
                log.error("远程调用内容管理服务熔断异常:{}",throwable.getMessage());
                return new CoursePublish();
            }
        };
    }
}

第三步:在进行Feign远程调用时得到的是json字符串,还需要转化成CoursePublish类型的对象,将字符串的日期格式转成LocalDateTime类型的属性时需要指定时间格式

@Data
@TableName("course_publish")
public class CoursePublish implements Serializable {
    private static final long serialVersionUID = 1L;
    // ......
    /**
     * 发布时间
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createDate;

    /**
     * 上架时间
     */
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime onlineDate;

    /**
     * 下架时间
     */
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime offlineDate;
}

第四步:编写测试类FeignClientTest进行远程调用

/**
 * @description Feign接口测试类
 * @author Mr.M
 */
 @SpringBootTest
public class FeignClientTest {

  @Autowired
 ContentServiceClient contentServiceClient;

  @Test
  public void testContentServiceClient(){
   CoursePublish coursepublish = contentServiceClient.getCoursepublish(18L);
   Assertions.assertNotNull(coursepublish);
  }
}

添加免费/收费课程

第一步:在学习中心服务的接口工程中的MyCourseTablesController中提供根据课程Id添加选课(免费和收费)的接口

@Api(value = "我的课程表接口", tags = "我的课程表接口")
@Slf4j
@RestController
public class MyCourseTablesController {
    @Autowired
    MyCourseTablesService myCourseTablesService;


    @ApiOperation("添加选课")
    @PostMapping("/choosecourse/{courseId}")
    public XcChooseCourseDto addChooseCourse(@PathVariable("courseId") Long courseId) {
        SecurityUtil.XcUser user = SecurityUtil.getUser();
        if (user == null) {
            XueChengPlusException.cast("请登录后继续选课");
        }
        String userId = user.getId();
        return myCourseTablesService.addChooseCourse(userId, courseId);
    }
}

第二步:编写MyCourseTablesService接口及其实现类

  • 选择免费课程:添加选课记录同时添加到我的课程表
  • 选择收费课程:只添加选课记录,下单且支付成功后再添加到我的课程表
public interface MyCourseTablesService {
    /**
     * 添加选课
     * @param userId    用户id
     * @param courseId  课程id
     */
    XcChooseCourseDto addChooseCourse(String userId, Long courseId);
}
@Slf4j
@Service
@Transactional
public class MyCourseTablesServiceImpl implements MyCourseTablesService {
    @Autowired
    ContentServiceClient contentServiceClient;
    
    @Autowired
    XcChooseCourseMapper chooseCourseMapper;

    @Autowired
    XcCourseTablesMapper courseTablesMapper;

    @Override
    @Transactional
    public XcChooseCourseDto addChooseCourse(String userId, Long courseId) {
        // 1. 调用内容管理服务提供的查询课程发布信息接口,查询课程收费规则
        CoursePublish coursePublish = contentServiceClient.getCoursePublish(courseId);
        if (coursePublish == null) {
            XueChengPlusException.cast("课程不存在");
        }
        // 获取收费规则
        String charge = coursePublish.getCharge();
        XcChooseCourse chooseCourse = null;
        if ("201000".equals(charge)) {
            // 2. 如果是免费课程,向选课记录表、我的课程表添加数据
            log.info("添加免费课程..");
            // 将免费课程添加到选课记录表,数据来源于课程发布表
            chooseCourse = addFreeCourse(userId, coursePublish);
            // 将免费课程添加到我的课程表,数据来源于选课记录表
            addCourseTables(chooseCourse);
        } else {
            // 3. 如果是收费课程,只添加选课记录,下单且支付成功后再添加到我的课程表
            log.info("添加收费课程");
            chooseCourse = addChargeCourse(userId, coursePublish);
        }

        // 4. 获取学生的学习资格
        XcCourseTablesDto courseTablesDto = getLearningStatus(userId, courseId);
        // 5. 封装返回值包含选课信息和用户的学习资格
        XcChooseCourseDto chooseCourseDto = new XcChooseCourseDto();
        BeanUtils.copyProperties(chooseCourse, chooseCourseDto);
        // 设置学习资格的状态
        chooseCourseDto.setLearnStatus(courseTablesDto.learnStatus);
        return chooseCourseDto;
    }
}

第三步:将免费课程加入到选课记录表,课程信息数据来源于课程发布表,避免用户重复添加课程的情况减少脏数据

/**
 * 将免费课程加入到选课表
 *
 * @param userId   用户id
 * @param coursePublish 课程发布信息
 * @return 选课记录
 */
@Transactional
public XcChooseCourse addFreeCourse(String userId, CoursePublish coursePublish) {
    // 1. 先判断是否已经存在对应的选课记录(免费且选课成功),因为数据库中没有约束,所以可能存在同一个人对同一门免费课程重复添加多条相同的选课记录
    LambdaQueryWrapper<XcChooseCourse> lambdaQueryWrapper = new LambdaQueryWrapper<XcChooseCourse>()
        .eq(XcChooseCourse::getUserId, userId)
        .eq(XcChooseCourse::getCourseId, coursePublish.getId())
        .eq(XcChooseCourse::getOrderType, "700001")  // 免费课程
        .eq(XcChooseCourse::getStatus, "701001");// 选课成功
    // 1.1 由于可能存在多条,所以这里用selectList
    List<XcChooseCourse> chooseCourses = chooseCourseMapper.selectList(lambdaQueryWrapper);
    // 1.2 如果已经存在对应的选课数据,返回一条即可
    if (!chooseCourses.isEmpty()) {
        return chooseCourses.get(0);
    }
    // 2. 数据库中不存在数据,添加选课信息,对照着数据库中的属性挨个set即可
    XcChooseCourse chooseCourse = new XcChooseCourse();
    chooseCourse.setCourseId(coursePublish.getId());
    chooseCourse.setCourseName(coursePublish.getName());
    chooseCourse.setUserId(userId);
    chooseCourse.setCompanyId(coursePublish.getCompanyId());
    chooseCourse.setOrderType("700001");
    chooseCourse.setStatus("701001");
    xcChooseCourse.setCoursePrice(0f);//免费课程价格为0
    chooseCourse.setValidDays(365);
    chooseCourse.setValidtimeStart(LocalDateTime.now());
    chooseCourse.setValidtimeEnd(LocalDateTime.now().plusDays(365));
    chooseCourse.setCreateDate(LocalDateTime.now());
    int insert = chooseCourseMapper.insert(chooseCourse);
     if (insert <= 0) {
            XueChengPlusException.cast("添加选课记录失败");
        }
    return chooseCourse;
}

第四步:将免费课程添加到我的课程表(用户Id和课程Id有唯一索引约束),课程信息数据来源于选课记录表

  • 如果我的课程表已经存在课程, 课程可能已经过期
  • 如果有新的选课记录,则需要更新我的课程表中的现有信息
/**
 * 添加到我的课程表
 *
 * @param chooseCourse 选课记录
 */
@Transactional
public XcCourseTables addCourseTables(XcChooseCourse chooseCourse) {
    // 选课成功的课程才可以添加到我的课程表
    String status = chooseCourse.getStatus();
    if (!"701001".equals(status)) {
        XueChengPlusException.cast("选课未成功,无法添加到课程表");
    }
    // 根据用户id和课程id查询我的课程表中的某一门课程
    XcCourseTables courseTables = getXcCourseTables(chooseCourse.getUserId(), chooseCourse.getCourseId());
    if (courseTables != null) {
        return courseTables;
    }
    courseTables = new XcCourseTables();
    BeanUtils.copyProperties(chooseCourse, courseTables);
    // 记录选课Id
    courseTables.setChooseCourseId(chooseCourse.getId());
	// 课程类型即选课类型
    courseTables.setCourseType(chooseCourse.getOrderType());
    // 课程过期后需要记录更新课程的时间
    courseTables.setUpdateDate(LocalDateTime.now());
    int insert = courseTablesMapper.insert(courseTables);
    if (insert <= 0) {
        XueChengPlusException.cast("添加我的课程表失败");
    }
    return courseTables;
}

/**
 * 根据用户id和课程id查询我的课程表中的某一门课程
 *
 * @param userId   用户id
 * @param courseId 课程id
 * @return 我的课程表中的课程
 */
public XcCourseTables getXcCourseTables(String userId, Long courseId) {
	// 用户Id和课程Id有唯一索引约束,对于同一门课程同一个用户只能添加一次,最终只能查到一条数据
    return courseTablesMapper.selectOne(new LambdaQueryWrapper<XcCourseTables>()
            .eq(XcCourseTables::getUserId, userId)
            .eq(XcCourseTables::getCourseId, courseId));
}

添加收费课程与添加免费课程几乎没有区别,只是选课状态和选课类型不同

  • 添加收费课程到选课记录同样需要避免用户重复添加课程的情况减少脏数据
  • 只添加选课记录,下单且支付成功后再添加到我的课程表
/**
 * 将付费课程加入到选课记录表
 *
 * @param userId        用户id
 * @param coursePublish 课程发布信息
 * @return 选课记录
 */
@Transactional
public XcChooseCourse addChargeCourse(String userId, CoursePublish coursePublish) {
    // 1. 先判断是否已经存在对应的选课(收费且待支付的课程),因为数据库中没有约束,所以可能存在相同数据的选课
    LambdaQueryWrapper<XcChooseCourse> lambdaQueryWrapper = new LambdaQueryWrapper<XcChooseCourse>()
            .eq(XcChooseCourse::getUserId, userId)
            .eq(XcChooseCourse::getCourseId, coursePublish.getId())
            .eq(XcChooseCourse::getOrderType, "700002")  // 收费课程
            .eq(XcChooseCourse::getStatus, "701002");// 待支付
    // 1.1 由于可能存在多条,所以这里用selectList
    List<XcChooseCourse> chooseCourses = chooseCourseMapper.selectList(lambdaQueryWrapper);
    // 1.2 如果已经存在对应的选课数据,返回一条即可
    if (!chooseCourses.isEmpty()) {
        return chooseCourses.get(0);
    }
    // 2. 数据库中不存在数据,添加选课信息
    XcChooseCourse chooseCourse = new XcChooseCourse();
    // 课程发布Id
    chooseCourse.setCourseId(coursePublish.getId());
    chooseCourse.setCourseName(coursePublish.getName());
    chooseCourse.setUserId(userId);
    chooseCourse.setCompanyId(coursePublish.getCompanyId());
    chooseCourse.setOrderType("700002");
    chooseCourse.setStatus("701002");
    chooseCourse.setCoursePrice(coursePublish.getPrice());
    chooseCourse.setValidDays(365);
    chooseCourse.setValidtimeStart(LocalDateTime.now());
    chooseCourse.setValidtimeEnd(LocalDateTime.now().plusDays(365));
    chooseCourse.setCreateDate(LocalDateTime.now());
    int insert = chooseCourseMapper.insert(chooseCourse);
    if (insert<=0){
        XueChengPlusException.cast("添加选课记录失败");
    }
    return chooseCourse;
}

获取学习资格

我们点击马上学习时会通过查询用户的我的课程表判断用户对该课程的学习资格

@Api(value = "我的课程表接口", tags = "我的课程表接口")
@Slf4j
@RestController
public class MyCourseTablesController {
    @Autowired
    MyCourseTablesService myCourseTablesService;

    @ApiOperation("查询学习资格")
    @PostMapping("/choosecourse/learnstatus/{courseId}")
    public XcCourseTablesDto getLearnstatus(@PathVariable Long courseId) {
        SecurityUtil.XcUser user = SecurityUtil.getUser();
        if (user == null) {
            XueChengPlusException.cast("请登录后继续选课");
        }
        String userId = user.getId();
        return myCourseTablesService.getLearningStatus(userId, courseId);
    }
}

用户的学习资格是从我的课程表中某个课程中获取的,所以需要先判断课程表中对应的课程是否存

  • 如果查不到说明用户没有选课或选课后没有支付导致选课不成功,返回状态码为702002的对象
  • 如果查到了选课成功的记录还要判断课程是否过期,如果过期返回状态码为702003的对象**
public interface MyCourseTablesService {
    /**
 	* 获取学习资格
 	* @param userId        用户id
 	* @param courseId      课程id
 	* @return  学习资格状态
	*/
    XcCourseTablesDto getLearningStatus(String userId, Long courseId);
}
@Slf4j
@Service
@Transactional
public class MyCourseTablesServiceImpl implements MyCourseTablesService {
    /**
 	* 判断学习资格
 	* @param userId        用户idrrr
 	* @param courseId      课程id
 	* @return  学习资格状态:查询数据字典 [{"code":"702001","desc":"正常学习"},{"code":"702002","desc":"没有选课或选课后没有支付"},{"code":"702003","desc":"已过期需要申请续期或重新支付"}]
 	*/
    @Override
    public XcCourseTablesDto getLearningStatus(String userId, Long courseId) {
        // 1. 查询我的课程表
        XcCourseTablesDto courseTablesDto = null;
        XcCourseTables courseTables = getXcCourseTables(userId, courseId);
        // 2. 未查到,返回状态码"702002"表示没有选课或选课后没有支付
        if (courseTables == null) {
            courseTablesDto = new XcCourseTablesDto();
            courseTablesDto.setLearnStatus("702002");
            return courseTablesDto;
        }
        // 3. 查到了,判断是否过期
        boolean isExpires = LocalDateTime.now().isAfter(courseTables.getValidtimeEnd());
        // 3.1 已过期(当前时间在过期时间之后),返回状态码"702003"表示已过期需要申请续期或重新支付
        if (isExpires) {
            BeanUtils.copyProperties(courseTables, courseTablesDto);
            courseTablesDto.setLearnStatus("702003");
            return courseTablesDto;
        }else {  // 3.2 未过期,返回状态码"702001"表示正常学习
            BeanUtils.copyProperties(courseTables, courseTablesDto);
            courseTablesDto.setLearnStatus("702001");
            return courseTablesDto;
        }
    }
}

测试

对于免费课程点击加入我的课程表会自动跳转至学习页面,同时数据库中有我的课程表记录和选课记录

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于付费课程会显示支付页面,点击微信支付/支付宝支付仅会将数据加入到选课记录表中且选课状态为701002(待支付)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/452715.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

148个Chatgpt关键词汇总-有爱AI实战教程(二)

演示站点&#xff1a; https://ai.uaai.cn 技能模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 导读&#xff1a;在使用 ChatGPT 时&#xff0c;当你给的指令越精确&#xff0c;它的回答会越到位&#xff0c;举例来说&#xff0c;假如你要请它帮忙写文案&#xff0c;如…

C语言经典算法学习-4

文章目录 21.最大访客数22.中序式转后序式&#xff08;前序式&#xff09;23.后序式的运算24.洗扑克牌&#xff08;乱数排列&#xff09;25.Craps赌博游戏 21.最大访客数 说明&#xff1a;现将举行一个餐会&#xff0c;让访客事先填写到达时间与离开时间&#xff0c;为了掌握座…

2024年Vue3 面试题小总结

Vue3 面试题小总结 1. OptionsAPI 与 CompositionAPI 的区别&#xff1f; OptionsAPI&#xff1a; 选项式API&#xff0c;通过定义data、computed、watch、method等属性与方法&#xff0c;共同处理页面逻辑&#xff1b;缺点&#xff1a; 当组件变得复杂的时候&#xff0c;导致…

视频分割软件,到底哪一款才适合你?

在当今充满创意的数字时代&#xff0c;视频编辑已成为许多人表达想法、分享故事的重要手段。而在视频编辑的过程中&#xff0c;分割视频是一项关键而常见的任务&#xff0c;它能够让我们更精细地处理内容&#xff0c;使得最终的作品更为生动和引人入胜。然而&#xff0c;要想高…

揭秘财务数据分析的五力分析,轻松实现从会计财务到管理财务的华丽转身

在这个信息爆炸的时代&#xff0c;财务数据分析已经成为了企业和个人成功的关键。今天&#xff0c;就让我们一起揭开财务数据分析的神秘面纱&#xff0c;让你轻松掌握财务秘籍&#xff0c;成为财务高手&#xff01; 一、财务数据分析&#xff0c;为何如此重要&#xff1f; 财…

访客到了官网就跳走,概率是官网颜值和体验出了问题。

很多小伙伴反馈官网ip不错&#xff0c;但是pv太少了&#xff0c;停留时间更少&#xff0c;这大概率是网站颜值和体验出问题了。 如果访客到了官网后就跳走&#xff0c;有可能是因为官网的颜值和用户体验出了问题。这种情况可能会导致访客对网站的第一印象不佳&#xff0c;从而选…

【spring】使用阿里Spring Initailiz创建项目

网络原因使用Spring Initailiz会出现超时。 那我们就换成阿里的 先看看spring官网的 网址&#xff1a;https://start.spring.io 使用一下阿里的 网址&#xff1a;https://start.aliyun.com/ 填写信息 都是java开发者&#xff0c;具体信息部介绍了。 选择组件 lombok spri…

OKHttpRetrofit

完成一个get请求 1.导入依赖 implementation("com.squareup.okhttp3:okhttp:3.14.")2.开启viewBinding android.buildFeatures.viewBinding true 3.加网络权限 和 http明文请求允许配置文件 <?xml version"1.0" encoding"utf-8"?> &l…

Kotlin:内联类(inline class)

点击查询内联类中文文档 点击查询内联类英文文档 简介 提醒&#xff1a;内联类仅在 Kotlin 1.3 之后版本可用 有时候&#xff0c;业务逻辑需要围绕某种类型创建包装器。然而&#xff0c;由于额外的堆内存分配问题&#xff0c;它会引入运行时的性能开销。此外&#xff0c;如果…

【嵌入式——QT】标准对话框

【嵌入式——QT】标准对话框 文件对话框颜色对话框字体对话框输入对话框消息框代码示例 文件对话框 QFileDialog 常用静态函数 getOpenFileName&#xff1a;选择打开一个文件&#xff1b;getOpenFileNames&#xff1a;选择打开多个文件&#xff1b;getSaveFileName&#xff1…

如何使用ArcGIS Pro生成带计曲线等高线

等高线作为常见的地图要素经常会被使用到&#xff0c;一般情况下生成的等高线是不带计曲线的&#xff0c;在某些情况下我们需要带计曲线的等高线&#xff0c;这里为大家介绍一下ArcGIS Pro生成带计曲线等高线的方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数…

13、设计模式之模板模式(Template)

一、什么是模板模式 模板模式是一种基于继承实现的设计模式&#xff0c;它是行为型的模式。 主要思想是将定义的算法抽象成一组步骤&#xff0c;在抽象类种定义算法的骨架&#xff0c;把具体的操作留给子类来实现。 通俗地说&#xff0c;模板模式就是将某一行为制定一个框架&…

vue3 实现一个tab切换组件

一. 效果图 二. 代码 文件 WqTab.vue: <template><div ref"wqTabs" class"wq-tab"><template v-for"tab in tabs" :key"tab"><div class"tab-item" :class"{ ac: tabActive tab.key }" c…

LeetCode102题:二叉树的层序遍历(python3)

代码思路&#xff1a;使用队列先进先出的特性&#xff0c;queue[]不为空进入for循环&#xff0c;tmp存储每层的节点&#xff0c;将结果添加至res[]中。 python中使用collections中的双端队列deque()&#xff0c;其popleft()方法可达到O(1)时间复杂度。 class Solution:def lev…

uni-app开发特点和开发流程

uni-app是一个基于Vue.js框架的跨平台应用开发框架&#xff0c;通过一套代码可以同时运行在多个平台上&#xff0c;包括iOS、Android、H5等。它采用了基于流布局的页面渲染机制&#xff0c;可以自动适配不同平台的屏幕尺寸和分辨率。uniapp官网&#xff1a;https://uniapp.dclo…

概率与常见的概率分布

概率是数据分析、机器学习中最基础的知识。也是在生活中最实用的一门学科&#xff0c;学了很多大道理不一定能过好一生&#xff0c;学好概率则有一定概率会变得更好。为大概率坚持&#xff0c;为小概率备份。 概率与分布 要想了解概率&#xff0c;首先得搞清楚概率和概率分布的…

2024蓝桥杯每日一题(区间合并)

一、第一题&#xff1a;挤牛奶 解题思路&#xff1a;区间合并 区间合并模板题 【Python程序代码】 n int(input()) a [] for i in range(n):l,r map(int,input().split())a.append([l,r]) def cmp(x):return x[0],x[1] a.sort(keycmp) res1,res20,0 st,ed a[0][0…

SQLiteC/C++接口详细介绍之sqlite3类(五)

快速跳转文章列表&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;四&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;六&#xff09;&#xff08;未发表&#xff09; 14.sqlite3_busy_handle…

猫咪挑食不吃猫粮是为什么?适口性好、普口性价的主食冻干推荐

现在咱养猫人个个吧自家的小猫咪当成宝贝宠着&#xff0c;宠着宠着一些坏习惯就出来。 然而&#xff0c;这种宠爱有时也会导致猫咪养成挑食的不良习惯。那么&#xff0c;当猫咪拒绝吃猫粮时&#xff0c;我们应该如何应对呢&#xff1f;今天跟大家一起来分析分析猫咪挑食不吃猫…

Claude3相较于GPT4有哪些优点?

Claude 最实在的一点是即使是普通用户&#xff0c;也能用到上传文件、上传图片这些功能&#xff08;只是用的模型比付费版性能差一些&#xff0c;对普通用户开放的是 Sonnet 版本&#xff0c;付费用户是 Opus 版本&#xff09;。 但是 ChatGPT 就不行&#xff0c;免费的 GPT-3…