7.1 支付模块 - 用户选课

支付模块 - 需求分析、添加选课

文章目录

  • 支付模块 - 需求分析、添加选课
  • 一、需求分析
    • 1.1 选课业务流程
    • 1.2 支付业务流程
    • 1.3 在线学习业务流程
    • 1.4 课程续期业务流程
  • 二、添加选课
    • 2.1 执行流程
    • 2.2 数据模型
      • 2.2.1 选课记录表 choose_course
      • 2.2.2 用户课程表 course_tables
    • 2.3 查询课程基本信息
      • 2.3.1 content服务 - 查询课程发布信息
      • 2.3.2 开启Feign - 内容管理远程接口
      • 2.3.3 课程发布表Dto - 特别处理
    • 2.4 选课
      • 2.4.0 扩展类
      • 2.4.1 MyCourseTablesController 接口
      • 2.4.2 MyCourseTablesServiceImpl 实现类
      • 2.4.3 测试
    • 2.5 查询学习资格

一、需求分析

实现了学生选课、下单支付、学习的整体流程

网站的课程有免费和收费两种,对于免费课程学生选课后可直接学习,对于收费课程学生需要下单且支付成功方可选课、学习

选课:是将课程加入我的课程表的过程

我的课程表:记录我在网站学习的课程,我的课程表中有免费课程和收费课程两种,对于免费课程可直接添加到我的课程表,对于收费课程需要下单、支付成功后自动加入我的课程表

模块整体流程

image-20240130214642073

1.1 选课业务流程

用户通过搜索课程、课程推荐等信息进入课程详情页面,点击“马上学习” 引导进入学习界面去学习

具体流程如下图所示

image-20240130215606307

  • 进入课程详情点击马上学习

image-20240130215633595

  • 课程免费时引导加入我的课程表、或进入学习界面

image-20240130215654234

  • 课程收费时引导去支付、或试学

image-20240130215713808

选课是将课程加入我的课程表的过程

对免费课程选课后可直接加入我的课程表,对收费课程选课后需要下单支付成功系统自动加入我的课程表

image-20240130215820130

1.2 支付业务流程

通过下面的图,我们就能发现在支付前的操作就是选课

image-20240130215846020

1.3 在线学习业务流程

选课成功用户可以在线学习,对于免费课程无需选课即可在线学习

image-20240130215924087

1.4 课程续期业务流程

免费课程加入我的课程表默认为1年有效期,到期用户可申请续期

image-20240130220006931

二、添加选课

新建learning工程完成选课操作

2.1 执行流程

选课是将课程加入我的课程表的过程,根据选课的业务流程进行详细分析,业务流程

image-20240130221055462

选课信息存入选课记录表

选课记录表:记录了什么人在什么时候选择了哪一门课程

如果选择的课程是免费的,那么在选课记录表中,选课状态就是成功,并且此课程已经加入到课表中了

如果选择的课程是收费的,那么在选课记录表中,选课状态就是待支付,等待支付成功后,此课程会加入到课表中

免费课程被选课除了进入选课记录表同时进入我的课程表

收费课程进入选课记录表后需要经过下单、支付成功才可以进入我的课程表

收费课程和免费课程的区别就是,收费课程多了一步付款而已


在学习引导处,可以直接将免费课程加入我的课程表,如下图

image-20240130222553557

对于收费课程先创建选课记录表,支付成功后,收到支付结果由系统自动加入我的课程表

执行流程如下

image-20240130222626920

2.2 数据模型

2.2.1 选课记录表 choose_course

order_type 选课类型:是免费还是收费

status 选课状态:此课程是选课成功还是待支付、选课删除

image-20240130221931552

此表的作用简单的说:什么人在什么时间选择了哪门课,并且选择课程的状态是选课成功还是待支付

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

对于收费课程:按课程的现价、有效期确定开始服务时间、结束服务时间,选课状态为待支付

收费课程的选课记录需要支付成功后选课状态为成功

@Data
@TableName("xc_choose_course")
public class XcChooseCourse implements Serializable {private static final long serialVersionUID = 1L;/*** 主键*/@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 课程id*/private Long courseId;/*** 课程名称*/private String courseName;/*** 用户id*/private String userId;/*** 机构id*/private Long companyId;/*** 选课类型*/private String orderType;/*** 添加时间*/@TableField(fill = FieldFill.INSERT)private LocalDateTime createDate;/*** 课程有效期(天)*/private Integer validDays;private Float coursePrice;/*** 选课状态*/private String status;/*** 开始服务时间*/private LocalDateTime validtimeStart;/*** 结束服务时间*/private LocalDateTime validtimeEnd;/*** 备注*/private String remarks;}

2.2.2 用户课程表 course_tables

课程表的数据来源于选课记录表

对于免费课程创建选课记录后同时向我的课程表添加记录

对于收费课程创建选课记录后需要下单支付成功后自动向我的课程表添加记录

choose_course_id字段其实就是某个选课记录choose_course表中的主键

image-20240130222041892

@Data
@TableName("xc_course_tables")
public class XcCourseTables implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 选课记录id*/private Long chooseCourseId;/*** 用户id*/private String userId;/*** 课程id*/private Long courseId;/*** 机构id*/private Long companyId;/*** 课程名称*/private String courseName;/*** 课程名称*/private String courseType;/*** 添加时间*/@TableField(fill = FieldFill.INSERT)private LocalDateTime createDate;/*** 开始服务时间*/private LocalDateTime validtimeStart;/*** 到期时间*/private LocalDateTime validtimeEnd;/*** 更新时间*/private LocalDateTime updateDate;/*** 备注*/private String remarks;}

2.3 查询课程基本信息

根据之前的流程,首先我们要查询一下课程信息,主要是想知道某个课程是收费的还是免费的

知道课程基本信息后,我们就能确定收费怎么做、免费怎么做

查询课程基本信息的操作我们要在学习中心learning服务远程调用内容管理content服务

之后content服务会查询数据库中课程发布表,看看此课程是否已经发布以及课程的收费规则

image-20240130225738663

2.3.1 content服务 - 查询课程发布信息

content服务中新增接口 - 查询发布表中某个课程基本信息

内容管理服务提供查询课程信息接口,此接口从课程发布表查询

此接口主要提供其它微服务远程调用,所以此接口不用授权,本项目标记此类接口统一以 /r开头

将来会在白名单中配置

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

2.3.2 开启Feign - 内容管理远程接口

在learning-service模块添加Feign

/*** @description 内容管理远程接口*/
@FeignClient(value = "content-api",fallbackFactory = ContentServiceClientFallbackFactory.class)
public interface ContentServiceClient {@ResponseBody@GetMapping("/content/r/coursepublish/{courseId}")public CoursePublish getCoursepublish(@PathVariable("courseId") Long courseId);}

做好熔断降级处理

@Slf4j
@Component
public class ContentServiceClientFallbackFactory implements FallbackFactory<ContentServiceClient> {@Overridepublic ContentServiceClient create(Throwable throwable) {return new ContentServiceClient() {@Overridepublic CoursePublish getCoursepublish(Long courseId) {log.error("调用内容管理服务发生熔断:{}", throwable.toString(),throwable);return null;}};}
}

可以使用下面的代码远程调用一下content服务中的查询课程发布信息接口

@SpringBootTest
public class FeignClientTest {@AutowiredContentServiceClient contentServiceClient;@Testpublic void testContentServiceClient() {CoursePublish coursepublish = contentServiceClient.getCoursepublish(18L);Assertions.assertNotNull(coursepublish);}
}

2.3.3 课程发布表Dto - 特别处理

在进行feign远程调用时会将字符串转成LocalDateTime,

在CoursePublish 类中LocalDateTime的属性上边添加如下代码:

@JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
import com.fasterxml.jackson.annotation.JsonFormat;@Data
@TableName("course_publish")
public class CoursePublish implements Serializable {private static final long serialVersionUID = 1L;/*** 主键*/private Long id;/*** 机构ID*/private Long companyId;/*** 公司名称*/private String companyName;/*** 课程名称*/private String name;/*** 适用人群*/private String users;/*** 标签*/private String tags;/*** 创建人*/private String username;/*** 大分类*/private String mt;/*** 大分类名称*/private String mtName;/*** 小分类*/private String st;/*** 小分类名称*/private String stName;/*** 课程等级*/private String grade;/*** 教育模式*/private String teachmode;/*** 课程图片*/private String pic;/*** 课程介绍*/private String description;/*** 课程营销信息,json格式*/private String market;/*** 所有课程计划,json格式*/private String teachplan;/*** 教师信息,json格式*/private String teachers;/*** 发布时间*/@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;/*** 发布状态*/private String status;/*** 备注*/private String remark;/*** 收费规则,对应数据字典--203*/private String charge;/*** 现价*/private Float price;/*** 原价*/private Float originalPrice;/*** 课程有效期天数*/private Integer validDays;}

之前我们没使用上面的注解是因为使用Http请求的接口,但是我们已经把序列化和反序列化的相关配置都配置好了,如下图所示

使用的都是Jackson的方式

不管是序列化还是反序列化,我们的时间类型都是yyyy-MM-dd HH:mm:ss格式

但是Feign远程调用的时候,下面的配置就不会生效了

import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;@Configuration
public class LocalDateTimeConfig {/** 序列化内容*   LocalDateTime -> String* 服务端返回给客户端内容* */@Beanpublic LocalDateTimeSerializer localDateTimeSerializer() {return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));}/** 反序列化内容*   String -> LocalDateTime* 客户端传入服务端数据* */@Beanpublic LocalDateTimeDeserializer localDateTimeDeserializer() {return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));}// 配置@Beanpublic Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {return builder -> {builder.serializerByType(LocalDateTime.class, localDateTimeSerializer());builder.deserializerByType(LocalDateTime.class, localDateTimeDeserializer());};}}

2.4 选课

也就是下图中圈出来的这一步

image-20240130233801890

以及下面这一步

image-20240130234216457

2.4.0 扩展类

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

2.4.1 MyCourseTablesController 接口

    @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();// 添加选课XcChooseCourseDto xcChooseCourseDto = myCourseTablesService.addChooseCourse(userId, courseId);return xcChooseCourseDto;}

2.4.2 MyCourseTablesServiceImpl 实现类

/*** 选课相关操作*/
@Slf4j
@Service
public class MyCourseTablesServiceImpl implements MyCourseTablesService {@AutowiredXcChooseCourseMapper xcChooseCourseMapper;@AutowiredXcCourseTablesMapper xcCourseTablesMapper;@AutowiredContentServiceClient contentServiceClient;@Transactional@Overridepublic XcChooseCourseDto addChooseCourse(String userId, Long courseId) {// 1.Feign远程调用内容管理服务查询课程收费规则(从发布表中查询对应的课程是收费还是免费)CoursePublish coursepublish = contentServiceClient.getCoursepublish(courseId);if (coursepublish == null) {XueChengPlusException.cast("课程不存在");}// 课程收费规则(是否收费)String charge = coursepublish.getCharge();// 选课记录XcChooseCourse chooseCourse = null;// 2.免费课程:向选课记录表、我的课程表中写入数据(课程表的数据来源于选课记录表)if ("201000".equals(charge)) {// 向选课记录表中写入chooseCourse = addFreeCourse(userId, coursepublish);// 向课程表中写入XcCourseTables xcCourseTables = addCourseTables(chooseCourse);} else {// 3.收费课程:向炫酷记录表写入数据,等待用户支付完成后再向课程表中写入数据// 此模块不会向课程表中添加记录了chooseCourse = addChargeCourse(userId, coursepublish);}// 4.判断学生目前对此课程是否具有学习资格,并且要将此学习资格返回XcCourseTablesDto courseTablesDto = getLearningStatus(userId, courseId);// 构造返回值XcChooseCourseDto xcChooseCourseDto = new XcChooseCourseDto();BeanUtils.copyProperties(chooseCourse, xcChooseCourseDto);xcChooseCourseDto.setLearnStatus(courseTablesDto.getLearnStatus());return xcChooseCourseDto;}//添加免费课程,免费课程加入选课记录表public XcChooseCourse addFreeCourse(String userId, CoursePublish coursepublish) {// 不一定是添加,因为可能会有人多次点击“添加课程/学习课程”之类的按钮// 如果此课程已经被此用户选择了且选课的状态为成功,那就不允许用户再选择,直接返回结果即可LambdaQueryWrapper<XcChooseCourse> lqw = new LambdaQueryWrapper<>();// 哪一位用户lqw.eq(XcChooseCourse::getUserId, userId)// 课程id.eq(XcChooseCourse::getCourseId, coursepublish.getId())// 课程类型为免费课程.eq(XcChooseCourse::getOrderType, "700001")// 选课成功.eq(XcChooseCourse::getStatus, "701001");List<XcChooseCourse> xcChooseCourses = xcChooseCourseMapper.selectList(lqw);if (xcChooseCourses.size() > 0) {return xcChooseCourses.get(0);}// 运行到这里说明数据库中没有对应的选课记录,添加一份即可XcChooseCourse chooseCourse = new XcChooseCourse();chooseCourse.setCourseId(coursepublish.getId()); //课程idchooseCourse.setCourseName(coursepublish.getName()); //课程名称chooseCourse.setCoursePrice(coursepublish.getPrice());//免费课程价格为0chooseCourse.setUserId(userId); //用户名chooseCourse.setCompanyId(coursepublish.getCompanyId());//机构idchooseCourse.setOrderType("700001");//免费课程代码标识chooseCourse.setCreateDate(LocalDateTime.now()); //创建时间chooseCourse.setStatus("701001");//选课成功,选课状态标识chooseCourse.setValidDays(365);//免费课程默认365chooseCourse.setValidtimeStart(LocalDateTime.now());// 课程开始时间chooseCourse.setValidtimeEnd(LocalDateTime.now().plusDays(365)); //课程结束时间int insert = xcChooseCourseMapper.insert(chooseCourse);if (insert <= 0) {XueChengPlusException.cast("添加课程失败");}return chooseCourse;}//添加收费课程public XcChooseCourse addChargeCourse(String userId, CoursePublish coursepublish) {// 不一定是添加,因为可能会有人多次点击“添加课程/学习课程”之类的按钮// 查询选课表中,是否有此收费课程在选课记录表中的选课状态为待支付LambdaQueryWrapper<XcChooseCourse> lqw = new LambdaQueryWrapper<>();// 哪一位用户lqw.eq(XcChooseCourse::getUserId, userId)// 课程id.eq(XcChooseCourse::getCourseId, coursepublish.getId())// 课程类型为收费课程.eq(XcChooseCourse::getOrderType, "700002")// 状态不是选课成功,而是待支付.eq(XcChooseCourse::getStatus, "701002");List<XcChooseCourse> xcChooseCourses = xcChooseCourseMapper.selectList(lqw);if (xcChooseCourses.size() > 0) {return xcChooseCourses.get(0);}// 运行到这里说明数据库中没有对应的选课记录,添加一份即可XcChooseCourse chooseCourse = new XcChooseCourse();chooseCourse.setCourseId(coursepublish.getId()); //课程idchooseCourse.setCourseName(coursepublish.getName()); //课程名称chooseCourse.setCoursePrice(coursepublish.getPrice());//免费课程价格为0chooseCourse.setUserId(userId); //用户名chooseCourse.setCompanyId(coursepublish.getCompanyId());//机构idchooseCourse.setOrderType("700002");//收费课程代码标识chooseCourse.setCreateDate(LocalDateTime.now()); //创建时间chooseCourse.setStatus("701002");//选课成功,选课状态标识chooseCourse.setValidDays(365);//免费课程默认365chooseCourse.setValidtimeStart(LocalDateTime.now());// 课程开始时间chooseCourse.setValidtimeEnd(LocalDateTime.now().plusDays(365)); //课程结束时间int insert = xcChooseCourseMapper.insert(chooseCourse);if (insert <= 0) {XueChengPlusException.cast("添加课程失败");}return chooseCourse;}//添加到我的课程表(同一个人同一门课只会有同一条记录,因为这里我们已经在数据库添加约束了)public XcCourseTables addCourseTables(XcChooseCourse xcChooseCourse) {//选课记录完成且未过期可以添加课程到课程表String status = xcChooseCourse.getStatus();if (!"701001".equals(status)) {// 701001代表选课完成,其他状态都代表未完成XueChengPlusException.cast("选课未成功,无法添加到课程表");}XcCourseTables xcCourseTables = getXcCourseTables(xcChooseCourse.getUserId(), xcChooseCourse.getCourseId());if (xcCourseTables != null) {// 说明课程已经在课程表中了return xcCourseTables;}xcCourseTables = new XcCourseTables();xcCourseTables.setChooseCourseId(xcChooseCourse.getId()); // 选课表中的主键xcCourseTables.setUserId(xcChooseCourse.getUserId());xcCourseTables.setCourseId(xcChooseCourse.getCourseId());xcCourseTables.setCompanyId(xcChooseCourse.getCompanyId());xcCourseTables.setCourseName(xcChooseCourse.getCourseName());xcCourseTables.setCreateDate(LocalDateTime.now());xcCourseTables.setValidtimeStart(xcChooseCourse.getValidtimeStart());xcCourseTables.setValidtimeEnd(xcChooseCourse.getValidtimeEnd());xcCourseTables.setCourseType(xcChooseCourse.getOrderType());int insert = xcCourseTablesMapper.insert(xcCourseTables);if (insert <= 0) {XueChengPlusException.cast("课程添加到课程表失败");}return xcCourseTables;}/*** @param userId* @param courseId* @return com.xuecheng.learning.model.po.XcCourseTables* @description 根据课程和用户查询我的课程表中某一门课程*/public XcCourseTables getXcCourseTables(String userId, Long courseId) {LambdaQueryWrapper<XcCourseTables> lqw = new LambdaQueryWrapper<>();lqw.eq(XcCourseTables::getUserId, userId).eq(XcCourseTables::getCourseId, courseId);return xcCourseTablesMapper.selectOne(lqw);}/*** 查询课程表** @param userId* @param courseId* @return XcCourseTablesDto 学习资格状态 [{"code":"702001","desc":"正常学习"},{"code":"702002","desc":"没有选课或选课后没有支付"},{"code":"702003","desc":"已过期需要申请续期或重新支付"}]* @description 判断学习资格*/@Overridepublic XcCourseTablesDto getLearningStatus(String userId, Long courseId) {// 查询我的课程表XcCourseTables xcCourseTables = getXcCourseTables(userId, courseId);if (xcCourseTables == null) {// 如果查不到,说明没有选课或者选课后未支付XcCourseTablesDto xcCourseTablesDto = new XcCourseTablesDto();xcCourseTablesDto.setLearnStatus("702002");return xcCourseTablesDto;}//如果有记录,判断是否过期,如果过期了就不能学习,如果没过期可以正常学习XcCourseTablesDto xcCourseTablesDto = new XcCourseTablesDto();BeanUtils.copyProperties(xcCourseTables, xcCourseTablesDto);//是否过期,true过期,false未过期boolean isExpires = xcCourseTables.getValidtimeEnd().isBefore(LocalDateTime.now());if (!isExpires) {//正常学习xcCourseTablesDto.setLearnStatus("702001");return xcCourseTablesDto;} else {//已过期xcCourseTablesDto.setLearnStatus("702003");return xcCourseTablesDto;}}
}

2.4.3 测试

2.5 查询学习资格

我们只需要写一个Controller接口就行了,具体的实现其实在2.4.2中实现了

@ApiOperation("查询学习资格")
@PostMapping("/choosecourse/learnstatus/{courseId}")
public XcCourseTablesDto getLearnstatus(@PathVariable("courseId") Long courseId) {SecurityUtil.XcUser user = SecurityUtil.getUser();if (user == null) {XueChengPlusException.cast("用户未登录");}String userId = user.getId();return myCourseTablesService.getLearningStatus(userId, courseId);}boolean isExpires = xcCourseTables.getValidtimeEnd().isBefore(LocalDateTime.now());if (!isExpires) {//正常学习xcCourseTablesDto.setLearnStatus("702001");return xcCourseTablesDto;} else {//已过期xcCourseTablesDto.setLearnStatus("702003");return xcCourseTablesDto;}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R语言lavaan结构方程模型在复杂网络分析中的科研技术新趋势

此外&#xff0c;我们还将深入探讨R语言的基础知识、结构方程模型的基本原理、lavaan程序包的使用方法等内容。无论是潜变量分析、复合变量分析&#xff0c;还是非线性/非正态/缺失数据处理、分类变量分析、分组数据处理等复杂问题&#xff0c;我们都将一一为您解析。 希望通过…

线程安全之死锁

目录 一、概念 二、例子 三、死锁相关面试题目 一、概念 死锁主要发生在有多个依赖锁存在时,会在一个线程试图以另一个线程相反顺序锁住互斥量时发生 死锁使得一个或多个线程被挂起而无法继续执行,最糟糕的是,这种情况还不容易被发现。 在一个线程中对一个已经加锁的普通锁…

springboot-整合mybatis

1.导入依赖 <!--整合mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency><!--mysql--><dependen…

为什么不从独立服务器中转向云或其他方案呢?

传统的专用服务器&#xff0c;如香港服务器租赁、重庆服务器租赁等&#xff0c;是最强大、最稳定的业务托管类型之一。您将获得比任何其他托管计划更多的管理权限和卓越的性能&#xff0c;并且您可以控制整个服务器上的几乎所有内容。 当然&#xff0c;专用服务器也是在线业务…

多线程:线程池

线程池 认识线程池 什么是线程池 线程池就是一个可以复用线程的技术。 不使用线程池的问题 用户每发起一个请求&#xff0c;后台就需要创建一个新线程来处理&#xff0c;下次新任务来了肯定又要创建新线程处理的&#xff0c;而创建新线程的开销是很大的&#xff0c;并且请…

蜻蜓FM语音下载(mediadown)

一、介绍 蜻蜓FM语音下载&#xff08;mediadown&#xff09;&#xff0c;能够帮助你下载蜻蜓FM音频节目。如果你是蜻蜓FM会员&#xff0c;它还能帮你下载会员节目。 二、下载地址 本站下载&#xff1a;蜻蜓FM语音下载&#xff08;mediadown&#xff09; 百度网盘下载&#…

爬虫入门到精通_框架篇15(Scrapy框架安装)

1 Scrapy安装 Scrapy的安装有多种方式&#xff0c;它支持Python2.7版本及以上或Python3.3版本及以上。下面说明Python3环境下的安装。 Scrapy依赖的库比较多&#xff0c;至少需要依赖库有Twisted14.0,lxml 3.4,pyOpenSSL 0.14。而在不同平台环境又各不相同&#xff0c;所以在安…

【Flutter 面试题】dart是值传递还是引用传递?

【Flutter 面试题】dart是值传递还是引用传递&#xff1f; 文章目录 写在前面解答补充说明值传递示例引用传递示例总结 写在前面 关于我 &#xff0c;小雨青年 &#x1f449; CSDN博客专家&#xff0c;GitChat专栏作者&#xff0c;阿里云社区专家博主&#xff0c;51CTO专家博主…

视频编码中常用的测试YUV系列及说明

vcc最新规定的测试序列如下所示&#xff0c;对于RA和LD配置&#xff0c;所有序列的所有帧都需要测试&#xff0c;对于intra配置仅需测试前8帧。 每列含义如下&#xff1a; A1、A2测试序列在LD配置下编码时应编码帧数为帧率的三倍。 “M”表示在该配置下必须测试这条序列。 …

算法沉淀——动态规划之其它背包问题与卡特兰数(leetcode真题剖析)

算法沉淀——动态规划之其它背包问题与卡特兰数 二维费用的背包问题01.一和零02.盈利计划 似包非包组合总和 Ⅳ 卡特兰数不同的二叉搜索树 二维费用的背包问题 01.一和零 题目链接&#xff1a;https://leetcode.cn/problems/ones-and-zeroes/ 给你一个二进制字符串数组 strs…

登录校验-过滤器-拦截器

会话技术 会话&#xff1a;用户打开浏览器&#xff0c;访问Wb服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应。 会话跟踪&#xff1a;一种维护浏览器状态的方法&#xff0c;服务器需要识别多次请求…

【MySQL知识体系】第2章 数据库与表的创建(一)

第2章 数据库与表的创建 2.1 数据库操作 2.2 表操作 文章目录 第2章 数据库与表的创建2.1 数据库操作2.1.1 创建第一个数据库2.1.2 更新数据库名称&#xff08;数据库创建后无法修改名称&#xff09;2.1.3 删除数据库2.1.4 取个合适的数据库名称 第2章 数据库与表的创建 2.1 数…