谷粒学院实战day09

您所在的位置:网站首页 谷粒学堂 谷粒学院实战day09

谷粒学院实战day09

2023-10-07 01:40| 来源: 网络整理| 查看: 265

1、课程评论功能分析

看看数据库表的设计

image-20200714105831440

首先是评论的分页查询,也就是以分页的形式显示所有的评论内容 关键是评论的添加功能,这里涉及的流程相对比较复杂的 一个是需要获取到课程的id和讲师的id,这个在课程详情页都可以获取到 还有就是需要考虑到评论需要现实用户的昵称,头像,这就要求获取到用户信息了,而且需要用户登陆后才能发表评论,那么这个用户信息该怎么获取到呢? 之前做过登陆模块的功能,应该还记得是将token放到header中,所以我们可以从header中获取到token 而在token中就可以获取到用户id 获取到用户id之后可以查询到用户信息 准备工作

新建数据库表

12345678910111213141516CREATE TABLE `edu_comment` ( `id` CHAR(19) NOT NULL COMMENT '讲师ID', `course_id` VARCHAR(19) NOT NULL DEFAULT '' COMMENT '课程id', `teacher_id` CHAR(19) NOT NULL DEFAULT '' COMMENT '讲师id', `member_id` VARCHAR(19) NOT NULL DEFAULT '' COMMENT '会员id', `nickname` VARCHAR(50) DEFAULT NULL COMMENT '会员昵称', `avatar` VARCHAR(255) DEFAULT NULL COMMENT '会员头像', `content` VARCHAR(500) DEFAULT NULL COMMENT '评论内容', `is_deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除', `gmt_create` DATETIME NOT NULL COMMENT '创建时间', `gmt_modified` DATETIME NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_course_id` (`course_id`), KEY `idx_teacher_id` (`teacher_id`), KEY `idx_member_id` (`member_id`)) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='评论'; TODO2、课程订单模块2.1 需求分析

image-20200714181406781

在课程详情页中,如果是免费的,则可立即观看,如果是收费的,则需要点击立即购买

点击立即购买后,则需要根据课程id生成一个订单,并返回订单号

然后带着生成的订单号跳转到订单详情页(如下),订单详情可根据刚携带的订单号查询出来

image-20200714181836338

点击去支付后,需要生成一个微信支付的二维码

image-20200714181914883

支付成功后,需要查询订单状态,如果为支付完成则继续等待,如果已支付完成,则跳转到课程详情页,付费课程编程可点击立即观看

img

2.2 开发流程分析

首先点击立即购买,需要生成一个订单,并返回订单号,这是一个接口

从数据库表中可以看到既有课程基本信息,又有用户基本信息,所以少不了去调用课程和用户这两个模块的方法了

img

接着跳转到订单页面的时候,需要查询订单详情,这是一个接口

点击去支付,需要生成微信支付二维码,这是一个接口

微信扫码支付后,需要查询订单状态,这是一个接口

2.3 准备数据库

t_order

123456789101112131415161718192021CREATE TABLE `t_order` ( `id` char(19) NOT NULL DEFAULT '', `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号', `course_id` varchar(19) NOT NULL DEFAULT '' COMMENT '课程id', `course_title` varchar(100) DEFAULT NULL COMMENT '课程名称', `course_cover` varchar(255) DEFAULT NULL COMMENT '课程封面', `teacher_name` varchar(20) DEFAULT NULL COMMENT '讲师名称', `member_id` varchar(19) NOT NULL DEFAULT '' COMMENT '会员id', `nickname` varchar(50) DEFAULT NULL COMMENT '会员昵称', `mobile` varchar(11) DEFAULT NULL COMMENT '会员手机', `total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '订单金额(分)', `pay_type` tinyint(3) DEFAULT NULL COMMENT '支付类型(1:微信 2:支付宝)', `status` tinyint(3) DEFAULT NULL COMMENT '订单状态(0:未支付 1:已支付)', `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除', `gmt_create` datetime NOT NULL COMMENT '创建时间', `gmt_modified` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `ux_order_no` (`order_no`), KEY `idx_course_id` (`course_id`), KEY `idx_member_id` (`member_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单';

t_pay_log

123456789101112131415CREATE TABLE `t_pay_log` ( `id` char(19) NOT NULL DEFAULT '', `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号', `pay_time` datetime DEFAULT NULL COMMENT '支付完成时间', `total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '支付金额(分)', `transaction_id` varchar(30) DEFAULT NULL COMMENT '交易流水号', `trade_state` char(20) DEFAULT NULL COMMENT '交易状态', `pay_type` tinyint(3) NOT NULL DEFAULT '0' COMMENT '支付类型(1:微信 2:支付宝)', `attr` text COMMENT '其他属性', `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除', `gmt_create` datetime NOT NULL COMMENT '创建时间', `gmt_modified` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付日志表'; 2.4 新建service_order模块

导入相关依赖

12345678910111213 com.github.wxpay wxpay-sdk 0.0.3 com.alibaba fastjson

配置yml文件

提示:别忘了在nginx中配置8007端口

12345678910111213141516171819202122232425262728293031323334353637383940414243444546# 服务端口server: port: 8007spring: # 服务名 application: name: service-order # 环境设置 profiles: active: dev # mysql数据库连接 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8 username: root password: 123456 # 返回json的全局时间格式 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 redis: host: 203.195.160.231 port: 6379 database: 0 timeout: 1800000 lettuce: pool: max-active: 20 max-wait: 1 # 最大阻塞等待时间(负数表示没有限制) max-idle: 5 min-idle: 0# 配置mapper xml文件的路径mybatis-plus: mapper-locations: classpath:com/ryan/order/mapper/xml/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 开启hystrix服务feign: hystrix: enabled: true 2.5 mp生成代码

提示:生成以上两张表对应的代码,同时建议生成的代码中将前缀t_去掉

1strategy.setTablePrefix("t" + "_"); 2.6 完善生成的代码

提示:主要是逻辑删除和自动填充注解,以及配置逻辑删除和分页的bean

2.7 后端接口生成订单准备工作

因为订单信息中同时包含课程信息和用户信息,所以需要远程调用,在远程调用的过程中,应该返回公共的实体类数据,所以首先应该新建两个对应的公共实体类

CourseInfoOrder

12345678910111213141516171819202122232425262728293031@Datapublic class CourseInfoOrder { @ApiModelProperty(value = "课程ID") private String id; @ApiModelProperty(value = "课程讲师ID") private String teacherId; @ApiModelProperty(value = "课程专业ID") private String SubjectId; @ApiModelProperty(value = "课程专业父级ID") private String subjectParentId; @ApiModelProperty(value = "课程标题") private String title; @ApiModelProperty(value = "课程销售价格,设置为0则可免费观看") private BigDecimal price; @ApiModelProperty(value = "总课时") private Integer lessonNum; @ApiModelProperty(value = "课程封面图片路径") private String cover; @ApiModelProperty(value = "课程简介") private String description;}

UserInfoOrder

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849@Datapublic class UserInfoOrder { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "会员id") @TableId(value = "id", type = IdType.ID_WORKER_STR) private String id; @ApiModelProperty(value = "微信openid") private String openid; @ApiModelProperty(value = "手机号") private String mobile; @ApiModelProperty(value = "密码") private String password; @ApiModelProperty(value = "昵称") private String nickname; @ApiModelProperty(value = "性别 1 女,2 男") private Integer sex; @ApiModelProperty(value = "年龄") private Integer age; @ApiModelProperty(value = "用户头像") private String avatar; @ApiModelProperty(value = "用户签名") private String sign; @ApiModelProperty(value = "是否禁用 1(true)已禁用, 0(false)未禁用") private Boolean isDisabled; @ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除") @TableLogic private Boolean isDeleted; @ApiModelProperty(value = "创建时间") @TableField(fill = FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty(value = "更新时间") @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmtModified;}

在被调用方编写对应的controller接口

注意:如果该模块已经开启了熔断机制的话,则需要编写fallback函数,否则会报错:找不到fallback函数,我这里没有开启,所以写一个几口就行

EduCourseController

12345678910//根据课程id查询课程信息,因为是别的模块调用,所以这里返回的不能是CourseInfoVo,需要在公共模块中重新封装实体类//但是实体类的属性是一样的@GetMapping("/getCourseInfoOrder/{courseId}")public CourseInfoOrder getCourseInfoOrder(@PathVariable String courseId){ CourseInfoVo courseInfo = eduCourseService.getCourseInfo(courseId); CourseInfoOrder courseInfoOrder = new CourseInfoOrder(); BeanUtils.copyProperties(courseInfo, courseInfoOrder); return courseInfoOrder;}

MemberController

12345678//根据用户id查询用户信息,注意这里是要被其他模块调用,所以不能直接返回Member,而是返回公共模块的封装实体类M@GetMapping("/getUserInfoOder/{id}")public UserInfoOrder getUserInfoOder(@PathVariable String id){ Member member = memberService.getById(id); UserInfoOrder userInfoOrder = new UserInfoOrder(); BeanUtils.copyProperties(member, userInfoOrder); return userInfoOrder;}

在调用方新建两个对应的Feign接口

CourseInfoClient

123456@Component@FeignClient(name = "service-edu")public interface CourseInfoClient { @GetMapping("/eduservice/edu-course/getCourseInfoOrder/{courseId}") public CourseInfoOrder getCourseInfoOrder(@PathVariable("courseId") String courseId);}

UserInfoClient

12345678@Component@FeignClient(name = "service-ucenter")public interface UserInfoClient { @GetMapping("/ucenter/member/getUserInfoOder/{id}") public UserInfoOrder getUserInfoOder(@PathVariable("id") String id);}

注意:

feign接口别忘了添加Component接口 不一定要编写实现类,也就是回调函数 feign接口中对应的参数一定要在注解后面写上对应的参数 OrderController1234567891011121314151617@RestController@RequestMapping("/eduorder/order")@CrossOriginpublic class OrderController { @Autowired private OrderService orderService; //根据课程id和用户id生成订单 @PostMapping("/createOrder/{courseId}") public R createOrder(@PathVariable String courseId, HttpServletRequest request){ String orderId = orderService.createOrder(courseId, JwtUtils.getMemberIdByJwtToken(request)); return R.ok().data("orderId",orderId); }} OrderService

接口

1234public interface OrderService extends IService { String createOrder(String courseId, String memberIdByJwtToken);}

实现类

12345678910111213141516171819202122232425262728293031323334353637@Servicepublic class OrderServiceImpl extends ServiceImpl implements OrderService { @Autowired private CourseInfoClient courseInfoClient; @Autowired private UserInfoClient userInfoClient; //根据课程id和用户id生成订单 @Override public String createOrder(String courseId, String memberId) { //根据课程id获取课程信息 CourseInfoOrder courseInfo = courseInfoClient.getCourseInfoOrder(courseId); //根据用户id获取用户信息 UserInfoOrder userInfo = userInfoClient.getUserInfoOder(memberId); //使用工具类生成订单号 String orderNo = OrderNoUtil.getOrderNo(); //配置订单信息 Order order = new Order(); order.setOrderNo(orderNo); order.setCourseId(courseId); order.setCourseTitle(courseInfo.getTitle()); order.setCourseCover(courseInfo.getCover()); order.setTeacherName("ryan"); order.setMemberId(memberId); order.setNickname(userInfo.getNickname()); order.setMobile(userInfo.getMobile()); order.setTotalFee(courseInfo.getPrice()); order.setPayType(1);//微信支付 order.setStatus(0);//未支付状态 //保存,返回订单号 baseMapper.insert(order); return order.getOrderNo(); }}

注意:返回订单号是为了方便后面查询订单详情的接口

查询订单OrderController12345678//根据订单号查询订单信息@GetMapping("/getOrderInfo/{orderNo}")public R getOrderInfo(@PathVariable String orderNo){ QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("order_no", orderNo); Order order = orderService.getOne(wrapper); return R.ok().data("order",order);} 生成微信支付二维码PayLogController1234567891011121314151617@RestController@RequestMapping("/eduorder/pay")@CrossOriginpublic class PayLogController { @Autowired private PayLogService payLogService; //点击去支付,根据订单号生成微信支付二维码 @GetMapping("/createNative/{orderNo}") public R createNative(@PathVariable String orderNo){ //返回map是为了方便取值,因为二维码中还是包含挺多支付信息的 Map map = payLogService.createNative(orderNo); System.out.println(map); return R.ok().data(map); }} PayLogService

接口

1Map createNative(String orderNo);

实现类

提示:

一方面,这里只支持认证过的企业收款账户,暂时不支持个人的,里面相关账户都是尚硅谷官方提供的 很多参数和地址都是腾讯微信官方提供的,流程也比较固定 因为最后返回和需要用的数据比较多,所以建议最好返回的是一个map集合 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748@Servicepublic class PayLogServiceImpl extends ServiceImpl implements PayLogService { @Autowired private OrderService orderService; //根据订单号生成微信支付二维码 @Override public Map createNative(String orderNo) { try{ //根据orderNo查询订单信息 QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("order_no", orderNo); Order order = orderService.getOne(wrapper); //设置支付参数 Map m = new HashMap(); m.put("appid","wx74862e0dfcf69954");//微信公众号appid m.put("mch_id", "1558950191");//商户号id m.put("nonce_str", WXPayUtil.generateNonceStr());//微信工具类生成的随机签名,保证每一个订单都不一样 m.put("body", order.getCourseTitle()); //课程标题 m.put("out_trade_no", orderNo); //订单号 m.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+""); m.put("spbill_create_ip", "127.0.0.1"); m.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n"); m.put("trade_type", "NATIVE"); //HTTPClient来根据URL访问第三方接口并且传递参数微信支付提供的固定的地址 HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder"); //client设置参数,并发送请求,将map集合转换为xml格式,并且需要商户key client.setXmlParam(WXPayUtil.generateSignedXml(m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb")); client.setHttps(true);//支持Https,默认是不支持的 client.post();//发送请求 //获取返回数据,注意返回的数据也是xml数据,后面还需要转换为map String xml = client.getContent(); Map resultMap = WXPayUtil.xmlToMap(xml); //封装结果集 Map map = new HashMap(); map.put("out_trade_no", orderNo); map.put("course_id", order.getCourseId()); map.put("total_fee", order.getTotalFee()); map.put("result_code", resultMap.get("result_code")); //返回二维码操作状态码 map.put("code_url", resultMap.get("code_url")); //二维码地址 return map; }catch(Exception e){ throw new GuliException(20001, "支付失败"); } }} 查询订单支付状态PayLogController

提示:

支付中返回25000是为了配合前端的拦截器 这里涉及到两个方法,一个是查询返回数据的方法,一个是更新状态的方法 123456789101112131415//根据订单号查询支付状态的接口@GetMapping("/queryPayStatus/{orderNo}")public R queryPayStatus(@PathVariable String orderNo){ Map map = payLogService.queryPayStatus(orderNo); if(map == null)return R.error().message("支付出错"); //如果map不为空,则要么支付中,要么支付成功 if(map.get("trade_state").equals("SUCCESS")){ //如果成功,则更新支付状态 payLogService.updatePayStatus(map); System.out.println(map); return R.ok().message("支付成功"); } //其他情况,则属于支付中 return R.ok().code(25000).message("支付中,请稍后");} PayLogService

接口

123Map queryPayStatus(String orderNo);void updatePayStatus(Map map);

实现类

查询支付状态的方法和生成二维码的流程非常像,比较固定的流程 而更新订单状态的方法,则是根据查询状态方法返回的数据进一步判断和操作 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556//查询支付状态@Overridepublic Map queryPayStatus(String orderNo) { try { //1、封装参数 Map m = new HashMap(); m.put("appid", "wx74862e0dfcf69954"); m.put("mch_id", "1558950191"); m.put("out_trade_no", orderNo); m.put("nonce_str", WXPayUtil.generateNonceStr()); //2 发送httpclient HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery"); client.setXmlParam(WXPayUtil.generateSignedXml(m,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb")); client.setHttps(true); client.post(); //3 得到请求返回内容 String xml = client.getContent(); Map resultMap = WXPayUtil.xmlToMap(xml); //6、转成Map再返回 return resultMap; }catch(Exception e) { return null; }}//更新支付状态@Overridepublic void updatePayStatus(Map map) { //获取订单号 String orderNo = map.get("out_trade_no"); //首先根据订单号查询支付订单信息 QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("order_no", orderNo); Order order = orderService.getOne(wrapper); //判断支付状态值 if(order.getStatus().intValue() == 1){return;}//如果状态值为1,则表示已支付,不需要往下执行了 //如果不是1则设置支付状态1 order.setStatus(1); orderService.updateById(order); //记录支付日志 //向支付表添加支付记录 PayLog payLog = new PayLog(); payLog.setOrderNo(orderNo); //订单号 payLog.setPayTime(new Date()); //订单完成时间 payLog.setPayType(1);//支付类型 1微信 payLog.setTotalFee(order.getTotalFee());//总金额(分) payLog.setTradeState(map.get("trade_state"));//支付状态 payLog.setTransactionId(map.get("transaction_id")); //流水号 payLog.setAttr(JSONObject.toJSONString(map)); baseMapper.insert(payLog);}

因为这个配合前端测试效果才会比较明显,所以这里就先不用swagger测试了,先整合前端再最后测试

2.8 前端接口

提示:找到立即观看的页面,调用对应的接口和页面跳转即可

准备工作

复制粘贴覆盖原先assets下的静态资源,并在defaul.js中重新引入对应的资源

因为需要生成二维码,需要借助vue的一个工具——vue-qriously,执行下面的命令安装即可

1npm install vue-qriously

然后在plugin中引入和使用

12import VueQriously from 'vue-qriously'Vue.use(VueQriously)

另外需要一个针对登陆状态和订单支付状态的拦截器,在request.js中添加此response拦截器

提示:不要忘了引入element-ui的MessageBox

1234567891011121314151617181920212223242526272829import { MessageBox, Message } from 'element-ui'// http response 拦截器service.interceptors.response.use( response => { //debugger if (response.data.code == 28004) { console.log("response.data.resultCode是28004") // 返回 错误代码-1 清除ticket信息并跳转到登录页面 //debugger window.location.href="/login" return }else{ if (response.data.code !== 20000) { //25000:订单支付中,不做任何提示 if(response.data.code != 25000) { Message({ message: response.data.message || 'error', type: 'error', duration: 5 * 1000 }) } } else { return response; } } }, error => { return Promise.reject(error.response) // 返回接口返回的错误信息}); api

order.js

12345678910111213141516171819202122232425262728293031import request from "@/utils/request.js"export default { //生成订单 createOrder(courseId, ){ return request({ url: `/eduorder/order/createOrder/${courseId}`, method: 'post' }) }, //查询订单 getOrder(orderNo){ return request({ url: `/eduorder/order/getOrderInfo/${orderNo}`, method: 'get' }) }, //生成微信支付二维码 createNative(orderNo){ return request({ url: `/eduorder/pay/createNative/${orderNo}`, method: 'get' }) }, //查询订单支付状态 queryPayStatus(orderNo){ return request({ url: `/eduorder/pay/queryPayStatus/${orderNo}`, method: 'get' }) }} course下的_id.vue

提示:

编写点击时间对应的方法即可 生成订单后应携带生成的订单号跳转值订单详情页 1234567891011methods:{ //生成订单 createOrders() { orderApi.createOrder(this.courseId) .then(response => { //获取返回订单号 //生成订单之后,跳转订单显示页面 this.$router.push({path:'/order/'+response.data.data.orderId}) }) }} order下的_oid.vue

提示:

注意:在编写异步调用的时候,不要忘了参数是需要加{}的 点击去支付后应该携带订单号跳转至二维码界面 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 订单确认 商品 原价 价格 讲师:{{order.teacherName}} {{order.courseTitle}}

¥{{order.totalFee}}

¥{{order.totalFee}}

共 1 件商品,合计¥{{order.totalFee}}

我已阅读并同意《谷粒学院购买协议》

返回课程详情页

共 1 件商品,合计¥{{order.totalFee}}

去支付 import orderApi from '@/api/order'export default { asyncData({ params, error }) { return orderApi.getOrder(params.oid) .then(response => { return { order: response.data.data.order } }) }, methods:{ //点击去支付,生成微信支付二维码,此二维码应该在新的窗口打开 toPay(){ console.log(this.order.orderNo) this.$router.push({path:"/pay/" + this.order.orderNo}) } }} pay下的_pid.vue

提示:

跳转至支付页面的时候,应该每隔一段时间去查询订单支付状态,如已支付则跳转回到课程详情页 使用定时器查询订单状态,但是别忘了如果已支付的话需要清理这个定时器 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 订单提交成功,请您及时付款!订单号:{{payObj.out_trade_no}} 应付金额:¥{{payObj.total_fee}} 微信支付

请使用微信扫一扫。

请使用微信扫一扫

扫描二维码支付

import orderApi from "@/api/order"export default { //异步请求 asyncData({params, error}){ return orderApi.createNative(params.pid) .then(response=>{ console.log(response.data.data) return{ payObj: response.data.data } }) }, data(){ return{ timer1:"" } }, mounted(){//页面渲染之后执行 //每个三秒查询一次订单支付状态 this.timer1 = setInterval(()=>{ this.queryPayStatus(this.payObj.out_trade_no) },3000) }, methods:{ //查询订单支付状态 queryPayStatus(orderNo){ orderApi.queryPayStatus(orderNo) .then(response=>{ console.log(response.data) //判断是否成功支付 if(response.data.success){ //提示支付成功,先停止计时器,然后提示信息并跳转 clearInterval(this.timer1) this.$message({ type: 'success', message: '支付成功!' }) //跳转至课程详情页面 this.$router.push({path: "/course/" + this.payObj.course_id}) } }) } }} 2.9 支付流程测试

启动nacos,确保服务注册

image-20200715093224741

点击立即购买

image-20200715093000712

生成订单详情页,并向订单表中添加了一条记录

image-20200715095105475

点击去支付

image-20200715095122952

扫码支付后提示信息并成功跳转,并向支付记录表中添加了一条记录

image-20200715095153179

但是你可以注意到支付跳转后,立即购买应该编程立即观看才对,所以下面就针对这些细节问题改善一下,整个支付流程已经走通的了

2.10 完善细节

根据刚刚说到的问题,应该怎么解决呢?

如果课程价格为0,也就是免费,应该显示立即观看按钮 如果价格不为0,也就是收费,但是用户已经购买,应该显示立即观看按钮 如果价格不为0,也就是收费,但是用户没有购买,应该显示立即购买按钮

如果这个问题解决了,那么也可以解决一个课程只需要购买一次的问题了

根据以上三个条件做判断即可,具体开发流程如下:

在进入课程详情界面的时候,首先应该判价格是否为0或者用户是否购买,判断价格比较容易,关键是需要判断用户是否购买怎么做呢?

只需要从订单数据库中根据courseId,memberId,status是否=1来查询即可,如果有数据,则表示已经购买,如果没有的则表示还没购买

。另外查询数据应该是在order模块中开发接口的,但是课程详情页应该是在edu模块中的,所以这里也少不了远程调用

提示:确保被调用方和调用方都开启了服务注册和服务发现

后端查询订单

OrderController

12345678910111213141516//根据课程id和用户id进行订单查询,因为只需要判断,不需要数据,所以这里返回布尔类型即可,也方便前端的调用@GetMapping("/isBuyCourse/{courseId}/{memberId}")public boolean isBuyCourse(@PathVariable String courseId, @PathVariable String memberId){ QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("course_id", courseId); wrapper.eq("member_id", memberId); wrapper.eq("status", 1); int count = orderService.count(wrapper); if(count > 0){ //有数据,则表示已经购买 return true; }else { //没有数据,则没有购买 return false; }}

eduservice中的feign接口

OrderClient

123456@Component@FeignClient(name = "service-order")public interface OrderClient { @GetMapping("/eduorder/order/isBuyCourse/{courseId}/{memberId}") public boolean isBuyCourse(@PathVariable("courseId") String courseId, @PathVariable("memberId") String memberId);}

修改之前的课程详情接口

ClientCourseController

123456789101112131415161718//根据课程id查询课程详情、章节和小节信息//补充,查询用户是否已经购买课程,因为要获取到用户id,所以还需要传request参数@GetMapping("/getCourseDetail/{courseId}")public R getCourseDetail(@PathVariable String courseId, HttpServletRequest request){ //课程详情 ClientCourseDetailVo courseDetailVo = eduCourseService.getCourseDetail(courseId); //章节信息,直接调用之前的api List chapterVoList = chapterService.getCourseChapters(courseId); //查询用户是否已经购买该课程 boolean isBuy = false; String memberId = JwtUtils.getMemberIdByJwtToken(request); if(!StringUtils.isEmpty(memberId)){ //已经登陆 isBuy = orderClient.isBuyCourse(courseId, memberId); } //返回数据 return R.ok().data("courseDetailVo",courseDetailVo).data("chapterVoList",chapterVoList).data("isBuy", isBuy);} 前端接口完善

找到课程详情页_id.vue

接口调用和数据返回

1234567891011121314151617181920212223242526272829303132333435363738394041import courseApi from '@/api/course'import orderApi from '@/api/order'export default { asyncData({ params, error }) { return { courseId: params.id } }, data(){ return{ courseWebVo:[], chapterVideoList:[], isBuy:false } }, created(){ this.initCourseInfo() }, methods:{ //初始化课程详情 initCourseInfo(){ courseApi.getCourseDetail(this.courseId) .then(response => { this.courseWebVo=response.data.data.courseDetailVo, this.chapterVideoList=response.data.data.chapterVoList, this.isBuy = response.data.data.isBuy }) }, //生成订单 createOrders() { orderApi.createOrder(this.courseId) .then(response => { //获取返回订单号 //生成订单之后,跳转订单显示页面 this.$router.push({path:'/order/'+response.data.data.orderId}) }) } }};

按钮显示判断

提示:记得去掉点击立即观看的生成订单事件

123456 立即观看 立即购买 测试

支付前

image-20200715113041498

支付后

image-20200715113057032



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3