在线教育平台 课程相关的接口实现

一、分页查询我的课表

按照Restful风格,查询请求应该使用GET方式。

请求路径一般是资源名称,比如这里资源是课表,所以资源名可以使用lessons,同时这里是分页查询,可以在路径后跟一个/page,代表分页查询

请求参数,因为是分页查询,首先肯定要包含分页参数,一般有两个:

  • pageNo:页码

  • pageSize:每页大小

这里只有两个排序条件,没有过滤条件,因此加上两个排序字段即可:

  • sortBy:排序方式

  • isAsc:是否升序

定义好

  • 统一的分页请求Query实体

  • 统一的分页结果DTO实体

  • 课表分页VO实体

 @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.分页查询
        // select * from learning_lesson where user_id = #{userId} order by latest_learn_time limit 0, 5
        Page<LearningLesson> page = lambdaQuery()
                .eq(LearningLesson::getUserId, userId) // where user_id = #{userId}
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        // 3.查询课程信息
        Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(records);

        // 4.封装VO返回
        List<LearningLessonVO> list = new ArrayList<>(records.size());
        // 4.1.循环遍历,把LearningLesson转为VO
        for (LearningLesson r : records) {
            // 4.2.拷贝基础属性到vo
            LearningLessonVO vo = BeanUtils.copyBean(r, LearningLessonVO.class);
            // 4.3.获取课程信息,填充到vo
            CourseSimpleInfoDTO cInfo = cMap.get(r.getCourseId());
            vo.setCourseName(cInfo.getName());
            vo.setCourseCoverUrl(cInfo.getCoverUrl());
            vo.setSections(cInfo.getSectionNum());
            list.add(vo);
        }
        return PageDTO.of(page, list);
    }

    private Map<Long, CourseSimpleInfoDTO> queryCourseSimpleInfoList(List<LearningLesson> records) {
        // 3.1.获取课程id
        Set<Long> cIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        // 3.2.查询课程信息
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(cIds);
        if (CollUtils.isEmpty(cInfoList)) {
            // 课程不存在,无法添加
            throw new BadRequestException("课程信息不存在!");
        }
        // 3.3.把课程集合处理成Map,key是courseId,值是course本身
        Map<Long, CourseSimpleInfoDTO> cMap = cInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        return cMap;
    }

第一段代码是一个方法queryMyLessons,用于查询当前登录用户的学习课程信息,并返回封装好的LearningLessonVO对象列表。方法逻辑包括: 1. 获取当前登录用户的ID。 2. 进行分页查询学习课程记录。 3. 调用queryCourseSimpleInfoList方法查询学习课程对应的课程简要信息。 4. 封装学习课程记录和课程简要信息到LearningLessonVO对象列表中。

第二段代码实现了将学习课程记录和对应课程简要信息关联起来,并返回一个包含完整信息的LearningLessonVO对象列表的功能。

二、添加课程到课表

当用户支付完成或者报名免费课程后,应该立刻将课程加入到课表中。交易服务会通过MQ通知学习服务,我们需要查看交易服务的源码,查看MQ通知的消息格式,来确定监听消息的格式。

 private List<CourseSimpleInfoDTO> getOnShelfCourse(List<Long> courseIds) {
        // 1.查询课程
        List<CourseSimpleInfoDTO> courseInfos = courseClient.getSimpleInfoList(courseIds);
        LocalDateTime now = LocalDateTime.now();
        // 2.判断状态
        for (CourseSimpleInfoDTO courseInfo : courseInfos) {
            // 2.1.检查课程是否上架
            if(!CourseStatus.SHELF.equalsValue(courseInfo.getStatus())){
                throw new BizIllegalException(TradeErrorInfo.COURSE_NOT_FOR_SALE);
            }
            // 2.2.检查课程是否过期
            if(courseInfo.getPurchaseEndTime().isBefore(now)){
                throw new BizIllegalException(TradeErrorInfo.COURSE_EXPIRED);
            }
        }
        return courseInfos;
    }


    @Override
    @Transactional
    public PlaceOrderResultVO enrolledFreeCourse(Long courseId) {
        Long userId = UserContext.getUser();
        // 1.查询课程信息
        List<Long> cIds = CollUtils.singletonList(courseId);
        List<CourseSimpleInfoDTO> courseInfos = getOnShelfCourse(cIds);
        if (CollUtils.isEmpty(courseInfos)) {
            // 课程不存在
            throw new BizIllegalException(TradeErrorInfo.COURSE_NOT_EXISTS);
        }
        CourseSimpleInfoDTO courseInfo = courseInfos.get(0);
        if(!courseInfo.getFree()){
            // 非免费课程,直接报错
            throw new BizIllegalException(TradeErrorInfo.COURSE_NOT_FREE);
        }
        // 2.创建订单
        Order order = new Order();
        // 2.1.基本信息
        order.setUserId(userId);
        order.setTotalAmount(0);
        order.setDiscountAmount(0);
        order.setRealAmount(0);
        order.setStatus(OrderStatus.ENROLLED.getValue());
        order.setFinishTime(LocalDateTime.now());
        order.setMessage(OrderStatus.ENROLLED.getProgressName());
        // 2.2.订单id
        Long orderId = IdWorker.getId(order);
        order.setId(orderId);

        // 3.订单详情
        OrderDetail detail = packageOrderDetail(courseInfo, order, 0);

        // 4.写入数据库
        saveOrderAndDetails(order, CollUtils.singletonList(detail));

        // 5.发送MQ消息,通知报名成功
        rabbitMqHelper.send(
                MqConstants.Exchange.ORDER_EXCHANGE,
                MqConstants.Key.ORDER_PAY_KEY,
                OrderBasicDTO.builder()
                        .orderId(orderId)
                        .userId(userId)
                        .courseIds(cIds)
                        .finishTime(order.getFinishTime())
                        .build()
        );
        // 6.返回vo
        return PlaceOrderResultVO.builder()
                .orderId(orderId)
                .payAmount(0)
                .status(order.getStatus())
                .build();
    }

由此,我们可以得知发送消息的Exchange、RoutingKey,以及消息体。消息体的格式是OrderBasicDTO,包含四个字段:

  • orderId:订单id

  • userId:下单的用户id

  • courseIds:购买的课程id集合

  • finishTime:支付完成时间

实体类

@Data
@Builder
public class OrderBasicDTO {
    /**
     * 订单id
     */
    private Long orderId;
    /**
     * 下单用户id
     */
    private Long userId;
    /**
     * 下单的课程id集合
     */
    private List<Long> courseIds;
    /**
     * 订单完成时间
     */
    private LocalDateTime finishTime;
}
 @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "learning.lesson.pay.queue", durable = "true"),
            exchange = @Exchange(name = MqConstants.Exchange.ORDER_EXCHANGE, type = ExchangeTypes.TOPIC),
            key = MqConstants.Key.ORDER_PAY_KEY
    ))
    public void listenLessonPay(OrderBasicDTO order){
        // 1.健壮性处理
        if(order == null || order.getUserId() == null || CollUtils.isEmpty(order.getCourseIds())){
            // 数据有误,无需处理
            log.error("接收到MQ消息有误,订单数据为空");
            return;
        }
        // 2.添加课程
        log.debug("监听到用户{}的订单{},需要添加课程{}到课表中", order.getUserId(), order.getOrderId(), order.getCourseIds());
        lessonService.addUserLessons(order.getUserId(), order.getCourseIds());
    }
 @Override
    @Transactional
    public void addUserLessons(Long userId, List<Long> courseIds) {
        // 1.查询课程有效期
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cInfoList)) {
            // 课程不存在,无法添加
            log.error("课程信息不存在,无法添加到课表");
            return;
        }
        // 2.循环遍历,处理LearningLesson数据
        List<LearningLesson> list = new ArrayList<>(cInfoList.size());
        for (CourseSimpleInfoDTO cInfo : cInfoList) {
            LearningLesson lesson = new LearningLesson();
            // 2.1.获取过期时间
            Integer validDuration = cInfo.getValidDuration();
            if (validDuration != null && validDuration > 0) {
                LocalDateTime now = LocalDateTime.now();
                lesson.setCreateTime(now);
                lesson.setExpireTime(now.plusMonths(validDuration));
            }
            // 2.2.填充userId和courseId
            lesson.setUserId(userId);
            lesson.setCourseId(cInfo.getId());
            list.add(lesson);
        }
        // 3.批量新增
        saveBatch(list);
    }

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计思路: 1. 确定需求:确定需要爬取的教育平台、爬取的数据类型、数据存储方式等。 2. 确定爬虫框架:选择合适的爬虫框架进行开发,如Scrapy、BeautifulSoup等。 3. 编写爬虫代码:根据需求,编写相应的爬虫代码,获取需要的数据。 4. 数据存储:将爬取到的数据存储到数据库中,如MySQL、MongoDB等。 5. 前后端交互:通过接口将数据传递给前端,在前端展示数据。 6. 安全性:对数据进行加密处理,保证数据安全。 实现步骤: 1. 确定需求:选择一到多个在线教育平台作为爬取对象,如Coursera、Udemy、edX等。确定需要爬取的数据类型,如课程名称、课程信息、教师信息、价格等。确定数据存储方式,如MySQL、MongoDB等。 2. 确定爬虫框架:选择Scrapy作为爬虫框架,因为它可以快速高效地爬取数据,并具有良好的可扩展性和灵活性。 3. 编写爬虫代码:根据需求,编写相应的爬虫代码。首先,需要确定爬虫的起始页面,然后根据页面结构编写相应的XPath或CSS选择器来定位需要的数据。接下来,需要编写代码来处理翻页、异步加载等问题。最后,将爬取到的数据保存到数据库中。 4. 数据存储:选择MongoDB作为数据库,因为它具有良好的性能、可扩展性和灵活性。可以使用pymongo库来连接MongoDB数据库,并将数据存储到数据库中。 5. 前后端交互:使用Flask框架来搭建Web应用程序,使用RESTful API将数据传递给前端。在前端使用AngularJS框架来展示数据。 6. 安全性:使用SSL证书对数据进行加密处理,确保数据传输的安全性。同时,使用机器学习算法对数据进行分类和过滤,避免恶意攻击。 最后,需要进行测试和优化,确保应用程序具有良好的性能和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值