- 复习SpringCloud
- 后台开发-订单管理模块
- 整合网关Gateway
- 网关bug
- 网关Gateway解决跨域
- 修改前端配置文件
- 后台开发-订单管理模块
- 需求概述
- 基础环境搭建
- 开发订单列表接口
- 集成订单前端页面
- 启动测试
- 后台开发-营销管理模块
- 基础环境搭建
- 创建用户模块
- 创建远程调用模块
- 开发相关接口
- 整合营销管理前端页面
- 微信公众号
- 测试公众号(重要)
- 开发业务介绍
- 后台管理系统-公众号菜单管理
- 需求分析
- 管理页面
- 后端基础环境搭建
- 公众号管理接口开发
- 公众号菜单同步
- 同步菜单(获取access_token)
- 文档查看
- service_wechat添加配置
- 添加工具类
- 复制相关工具类
- 添加Menucontroller方法
- 同步菜单(功能实现)
- 添加配置类
- 同步方法编写
- 整合前端
- 测试
- 公众号普通消息
- 需求分析
- 后台消息接入
- 消息接入
- 后端接口开发
- 消息校验接口
- 消息接收接口
- 配置内网穿透
- 何为内网穿透
- 配置内网穿透ngrok(不好用)
- 免费内网穿透花生壳(用这个)
- 内网穿透测试
- 后台开发-消息业务实现
- 业务流程
- 开发接口
- 远程调用模块构建
- 公众号模块引入远程调用
- 测试
- 公众号模板消息
- 正式公众号模板申请
- 查看模板样式
- 公众号测试号申请模板消息
- 公众号后台设置
- 后端相应接口
- 测试
- 微信授权登陆
- 需求分析
- 授权登录介绍
- 公众号正式号配置
- 公众号测试号配置
- 部署前端页面
- 前端处理
- 授权登录接口开发
- 测试
- Token(重要)
- JWT介绍
- JWT原理
- 看看Token长什么样子
- 整合JWT
- 公众号点播课程
- 需求分析
- 分页展示接口开发
- 整合前端
- 点播视频播放接口开发
- 整合前端
- 付费观看点播课程接口
- Feign创建获取课程信息接口
- Feign创建获取优惠卷操作接口
- 获取当前用户id
- 生成订单
也是这里篇幅比较多,我单独开一篇去复习
复习SpringCloud
SpringCloud系列学习:
Spring Cloud 上
Spring Cloud 中
Spring Cloud 下 (SpringCloud alibaba)
引入Nacos,把之前的注解打开
配置Nacos客户端的pom依赖
提前引入微服务相关依赖,具体的版本控制:
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-openfeign
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
配置service_vod
配置application.properties,在客户端微服务中添加注册Nacos服务的配置信息
# nacos服务地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
添加Nacos客户端注解
在service_vod微服务启动类中添加注解
@EnableDiscoveryClient
再次启动服务就会发现,新的服务已经注册进入Nacos了
网关也是一种服务,所以这里也要建设一个模块,整合网关服务
POM文件
com.cc service_utils 0.0.1-SNAPSHOT org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
创建启动类并且添加注解
网关bug网关启动类要和跨域配置类放在一个包下,而且启动网关一定要提前启动nacos,不然启动不起来
把配置文件写一下,application.properties
# 服务端口
server.port=8333
# 服务名
spring.application.name=service-gateway
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#使用服务发现路由
spring.cloud.gateway.discovery.locator.enabled=true
#service-vod模块配置
#设置路由id,也就是Nacos里面注册的服务名字
spring.cloud.gateway.routes[0].id=service-vod
#设置路由的uri lb:load Balance 负载均衡
spring.cloud.gateway.routes[0].uri=lb://service-vod
#设置路由断言,代理servicerId为auth-service的/auth/路径
#实际上断言就是路径匹配 /a/vod/b/c/d...
spring.cloud.gateway.routes[0].predicates= Path=orderactivity
@SneakyThrows
@Override
public void syncMenu() {
List menuVoList = this.findMenuInfo();
//菜单,创建一个JSON格式数组,这里为什么用了JSON数组,是因为这里直接对接公众号
//不再有Controller的JSON转换解析,所以这里直接做成JSON
JSONArray buttonList = new JSONArray();
//一级菜单
for(MenuVo oneMenuVo : menuVoList) {
JSONObject one = new JSONObject();
//name是一级菜单的固定名称
one.put("name", oneMenuVo.getName());
//二级菜单
JSONArray subButton = new JSONArray();
for(MenuVo twoMenuVo : oneMenuVo.getChildren()) {
JSONObject view = new JSONObject();
//type是二级菜单的固定名称
view.put("type", twoMenuVo.getType());
//根据type的值进行判断,是按钮还是关键词触发返回信息
if(twoMenuVo.getType().equals("view")) {
view.put("name", twoMenuVo.getName());
view.put("url", "http://ggkt2.vipgz1.91tunnel.com/#"
+twoMenuVo.getUrl());
} else {
view.put("name", twoMenuVo.getName());
view.put("key", twoMenuVo.getMeunKey());
}
subButton.add(view);
}
//封装二级菜单,固定叫sub_button
one.put("sub_button", subButton);
//封装一级菜单
buttonList.add(one);
}
//菜单整体封装,一级+二级 一起叫button
JSONObject button = new JSONObject();
button.put("button", buttonList);
this.wxMpService.getMenuService().menuCreate(button.toJSONString());
}
整合前端
src -> router -> index.js添加路由
src -> api -> wechat -> menu.js定义接口
创建views -> wechat -> menu -> list.vue CV页面
最终样式
关注测试公众号,电脑登陆公众号,就可以进行测试了
数据就同步上来了
如果更换了配置信息,就要在这里同步一下,不同步菜单的话数据上不来,因为微信公众号是不会主动请求更新的,需要咱们主动数据推送
输入特定词语,返回消息
1、“硅谷课堂”公众号实现根据关键字搜索相关课程,如:输入“java”,可返回java相关的一个课程;
2、“硅谷课堂”公众号点击菜单“关于我们”,返回关于我们的介绍
3、关注或取消关注等
后台消息接入 消息接入参考文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
接入微信公众平台开发,开发者需要按照如下步骤完成:
1、填写服务器配置
2、验证服务器地址的有效性
3、依据接口文档实现业务逻辑
公众号服务器配置
在测试管理 -> 接口配置信息,点击“修改”按钮,填写服务器地址(URL)和Token,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
说明:本地测试,url改为内网穿透地址,这个内网穿透在后面会仔细讲解
验证来自微信服务器消息
(1)概述
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:
| 参数 | 描述 |
|---|---|
| signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
| timestamp | 时间戳 |
| nonce | 随机数 |
| echostr | 随机字符串 |
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
1、将token、timestamp、nonce三个参数进行字典序排序
2、将三个参数字符串拼接成一个字符串进行sha1加密
3、开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
消息接收流程
实际上就是接收四个参数,而且使用HttpRequest对象来接收
接口位置
定义好Token,方便和微信后台设置的内容校验
两个方法名
public String verifyToken(HttpServletRequest request) {
private boolean checkSignature(String signature, String timestamp, String nonce)
完成之后,我们的校验接口就算是开发完成了。接下来就可以开发消息接收接口了。
消息接收接口接下来我们来开发消息接收接口,消息接收接口和上面的服务器校验接口地址是一样的,都是我们一开始在公众号后台配置的地址。只不过消息接收接口是一个 POST 请求。
在公众号后台配置的时候,消息加解密方式选择了明文模式,这样在后台收到的消息直接就可以处理了。微信服务器给我发来的普通文本消息格式如下:
1348831860 1234567890123456
| 参数 | 描述 |
|---|---|
| ToUserName | 开发者微信号 |
| FromUserName | 发送方帐号(一个OpenID) |
| CreateTime | 消息创建时间 (整型) |
| MsgType | 消息类型,文本为text |
| Content | 文本消息内容 |
| MsgId | 消息id,64位整型 |
看到这里,大家心里大概就有数了,当我们收到微信服务器发来的消息之后,我们就进行 XML 解析,提取出来我们需要的信息,去做相关的查询操作,再将查到的结果返回给微信服务器。
这里我们先来个简单的,我们将收到的消息解析并打印出来:
还是在同一个Controller里
@PostMapping
public String receiveMessage(HttpServletRequest request) throws Exception {
解析XML明文信息
private Map parseXml(HttpServletRequest request) throws Exception {
配置内网穿透
何为内网穿透
前面已经说过了,要微信服务器和本地的服务器进行通信。
我们可以找到微信服务器,但是微信服务器找不到我们的本地主机
这个时候就需要内网穿透帮忙了
所谓的开通一个隧道,就是申请一个域名,让微信服务器可以通过这个域名(隧道)访问到你本地
注册用户
网址:https://ngrok.cc/login/register
实名认证
(1)注册成功之后,登录系统,进行实名认证,认证费2元,认证通过后才能开通隧道
开通隧道
(1)选择隧道管理 -> 开通隧道
最后一个是免费服务器,建议选择付费服务器,10元/月,因为免费服务器使用人数很多,经常掉线
(2)点击立即购买 -> 输入相关信息
(3)开通成功后,查看开通的隧道
这里开通了两个隧道,一个用于后端接口调用,一个用于公众号前端调用
启动隧道
(1)下载客户端工具
(2)选择windows版本
(3)解压,找到bat文件,双击启动
(4)输入隧道id,多个使用逗号隔开,最后回车就可以启动
tnnd,连不上
正常如果连上了应该是这个样子
花生壳官网 花生壳打钱!
注册下载什么的我就不说了,安装好配置一下
这样就可以使用了!
一共要生成两个,一个给前端做隧道,另一个给后端做穿透
毕竟两个项目都在本地跑,想让外网访问不用隧道穿透肯定是不行的
但是如果想用HTTP服务必须花钱,再开一个,如果是学生的话可以免费开通
我认证了一下,就可以免费用两个HTTP了,TCP连前端连不上
快说,谢谢花生壳~
回到公众号这里,再来捋一遍,先看看自己的公众测试号
微信公众平台测试号后台
URL就不说了,就是配置好的内网穿透地址+后端服务器有效性验证 的路径
Token可以看后端代码
向测试公众号里发一个消息,看看后台是否可以接收到
可以看到,已经接受到了结构化的数据了
根据课程名称返回课程信息,就是根据课程名称进行模糊查询
service_vod模块创建接口
(1)创建CourseApiController方法,根据课程关键字查询课程信息
创建模块定义接口
service_client下创建子模块service_course_client,改Pom之类的就不多说了
把课程查询的远程调用整合到一个单独的远程调用子模块中
主启动类
service_wechat引入更改写好的远程调用依赖
service_course_client com.courseFeginService 0.0.1-SNAPSHOT
创建消息处理接口
将微信模块的MessageController方法进行更改,根据关键字内容进行校验
具体的消息结果处理在MessageServiceImpl,根据关键字返回比较固定,把这个基本代码改改就可以用了
把Nacos、内网隧道、服务都启动
(1)点击个人 -> 关于我们,返回关于我们的介绍
(2)在公众号输入关键字,返回搜索的课程信息
这种是非测试公众号的申请,测试公众号则不需要
实现目标
购买课程支付成功微信推送消息
模板消息实现
接口文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html
申请模板消息
首先我们需要知道,模板消息是需要申请的。
但是我们在申请时还是有一些东西要注意,这个在官方的文档有非常详细的说明。
https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Operation_Specifications.html
这个大家好好看看。选择行业的时候可要谨慎些,因为这个一个月只可以修改一次。
下面看看在哪里申请,硅谷课堂已经申请过,忽略
申请之后就耐心等待,审核通过之后就会出现“广告与服务”模板消息的菜单。
添加模板消息
审核通过之后,我们就可以添加模板消息,进行开发了。
我们点击模板消息进入后,直接在模板库中选择你需要的消息模板添加就可以了,添加之后就会在我的模板中。会有一个模板id,这个模板id在我们发送消息的时候会用到。
模板消息如下:
模板样式在一个word里面,下载下来
这里是截取一部分的内容
模板消息长这个样子
点击新增模板
从上面的模板示例中,拿到需要使用的模板,填写进去
添加成功就是这个样子
MessageController
添加方法
@GetMapping("/pushPayMessage")
public Result pushPayMessage() throws WxErrorException {
messageService.pushPayMessage(1L);
return Result.ok();
}
service接口
MessageService
void pushPayMessage(Long orderId);
service接口实现
(1)MessageServiceImpl类
@Autowired
private WxMpService wxMpService;
//TODO 暂时写成固定值测试,后续完善
@SneakyThrows
@Override
public void pushPayMessage(long orderId) {
String openid = "微信后台添加的自定义模板的openId";
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(openid)//要推送的用户openid
.templateId("模板的ID")//模板id
.url("http://ggkt2.vipgz1.91tunnel.com/#/pay/"+orderId)//点击模板消息要访问的网址
.build();
//3,如果是正式版发送消息,,这里需要配置你的信息
templateMessage.addData(new WxMpTemplateData("first", "亲爱的用户:您有一笔订单支付成功。", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword1", "1314520", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword2", "java基础课程", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword3", "2022-01-11", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword4", "100", "#272727"));
templateMessage.addData(new WxMpTemplateData("remark", "感谢你购买课程,如有疑问,随时咨询!", "#272727"));
String msg = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
System.out.println(msg);
}
(2)openid值和模板Id
访问一下这个接口,直接让他在公众号里推送模板信息
测试通过,推送成功
根据流程图通过菜单进入的页面都要授权登录
接口文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
说明:
1、严格按照接口文档实现;
2、应用授权作用域scope:scope为snsapi_userinfo
配置授权回调域名
公众号正式号配置这个是有企业注册了公众号的情况,个人是看不到这个页面的,只能拿本地测试玩玩
在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“设置与开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;
本地测试配置内网穿透地址
公众号测试号配置
这样的话就可以把本地服务器上运行的前端页面在公网进行访问了,也就是公众号开启新页面,可以访问
直接复制进工程中,资料里的前端页面叫
修改访问路径到你的内网隧道
启动公众号页面项目 这个直接运行就可以了 npm run serve
如果启动的时候报错看看这个文章
https://blog.csdn.net/m0_37629753/article/details/125199185
跑起来的样子
(1)全局处理授权登录,处理页面:/src/App.vue
说明1:访问页面时首先判断是否有token信息,如果没有跳转到授权登录接口
说明2:通过localStorage存储token信息
在HTML5中,加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间很小,只有几K),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。它只能存储字符串格式的数据,所以最好在每次存储时把数据转换成json格式,取出的时候再转换回来。
(2)前端代码实现
wechatLogin() {
// 处理微信授权登录
let token = this.getQueryString('token') || '';
if(token != '') {
window.localStorage.setItem('token', token);
}
// 所有页面都必须登录,两次调整登录,这里与接口返回208状态
token = window.localStorage.getItem('token') || '';
if (token == '') {
let url = window.location.href.replace('#', 'guiguketan')
window.location = 'http://glkt.atguigu.cn/api/user/wechat/authorize?returnUrl=' + url
}
console.log('token2:'+window.localStorage.getItem('token'));
},
授权登录接口开发
操作模块:service-user
引入微信依赖
com.github.binarywang weixin-java-mp 2.7.0 dom4j dom4j 1.1 com.aliyun aliyun-java-sdk-core
在配置文件中添加配置
#公众号id和秘钥 # 硅谷课堂微信公众平台appId wechat.mpAppId: wx09f201e9013e81d8 ## 硅谷课堂微信公众平台api秘钥 wechat.mpAppSecret: 6c999765c12c51850d28055e8b6e2eda # 授权回调获取用户信息接口地址 wechat.userInfoUrl: http://内网隧道地址/api/user/wechat/userInfo
添加了一个工具类来读取配置文件的账户密码信息
另一个是微信的配置类
我这里实在连不上内网穿透了…
没做出来…
希望有小伙伴能留言给我讲讲QAQ
通过token传递用户信息
JWT介绍JWT工具
JWT(Json Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上
JWT最重要的作用就是对 token信息的防伪作用。
JWT原理一个JWT由三个部分组成:公共部分、私有部分、签名部分。最后由这三者组合进行base64编码得到JWT。
(1)公共部分
主要是该JWT的相关配置参数,比如签名的加密算法、格式类型、过期时间等等。
(2)私有部分
用户自定义的内容,根据实际需要真正要封装的信息。
userInfo{用户的Id,用户的昵称nickName}
(3)签名部分
SaltiP: 当前服务器的Ip地址!{linux 中配置代理服务器的ip}
主要用户对JWT生成字符串的时候,进行加密{盐值}
base64编码,并不是加密,只是把明文信息变成了不可见的字符串。但是其实只要用一些工具就可以把base64编码解成明文,所以不要在JWT中放入涉及私密的信息。
看看Token长什么样子一个长Token一共三部分,每部分都用点.来分割
(1)在service_utils模块添加依赖
org.apache.httpcomponents httpclient io.jsonwebtoken jjwt joda-time joda-time
(2)添加JWT工具类
由于从微信用不了了,所以我这里就用本地前端工程做了
需求分析
点击 去看看,进入课程详情页面
在VOD模块下
创建CourseApiController
编写CourseService
编写CourseServiceImpl
这里用到了Mapper,因为要多表查询
值得注意的点:
拿一个左外连接查询来说,这种重复性的SQL是可以进行复用的
course c
LEFT JOIN teacher t ON c.teacher_id = t.id
LEFT JOIN subject s1 ON c.subject_parent_id = s1.id
LEFT JOIN subject s2 ON c.subject_id = s2.id
用一个标签来进行复用
course c LEFT JOIN teacher t ON c.teacher_id = t.id LEFT JOIN subject s1 ON c.subject_parent_id = s1.id LEFT JOIN subject s2 ON c.subject_id = s2.id
怎么用?
在需要使用复用标签的地方加入这个标签
整合前端
修改路由
创建js文件定义接口
编写页面
还是在VOD模块下,新建VodApi控制器,来接收前端的请求
在配置文件中加入
application.properties添加tencent.video.appid=腾讯云播放器ID
如图找ID
整合点播视频播放前端
创建js定义接口
courseInfo.vue修改play方法
play(video) {
let videoId = video.id;
let isFree = video.isFree;
this.$router.push({ path: '/play/'+this.courseId+'/'+videoId })
},
index.html引入文件
把播放器样式引入
创建play.vue页面
提交订单接口
创建Controller对象
编写OrderInfoService和OrderInfoServiceImpl
OrderInfoService
//生成点播课程订单 Long submitOrder(OrderFormVo orderFormVo);
不过这里得等等,因为下订单这个操作需要联动优惠卷和课程ID,所以还需要远程调用一下接口 一个是根据当前课程ID来拿到课程
另一个是操作优惠卷的(获取+优惠卷使用更新)
先获取课程信息
操作service_vod模块
添加课程信息获取方法
@ApiOperation("根据ID查询课程")
@GetMapping("inner/getById/{courseId}")
public Course getById(
@ApiParam(value = "课程ID", required = true)
@PathVariable Long courseId){
return courseService.getById(courseId);
}
远程调用接口你把上面的根据ID拿课程的接口定义好
操作service_activity模块
提前把要操作的接口准备好
对应的Service,更改为1就代表这个优惠卷已经被使用了
@Override
public void updateCouponInfoUseStatus(Long couponUseId, Long orderId) {
CouponUse couponUse = new CouponUse();
couponUse.setId(couponUseId);
couponUse.setOrderId(orderId);
couponUse.setCouponStatus("1");
couponUse.setUsingTime(new Date());
couponUseService.updateById(couponUse);
}
为上面的优惠卷接口创建一个远程调用接口,建Module过程我就不多说了,一遍又一遍的没必要多说了
到这里远程调用就算结束了,一会会回到上面的课程接口信息
获取当前用户id(1)common模块引入依赖
org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 2.6.0
拷贝相关工具类
之前不是说过,LocalStorage是前端的一种存储方式,所以就可以从前端的LocalStorage里拿到对象ID
前端实现方式
// http request 拦截器
service.interceptors.request.use(config => {
//获取localStorage里面的token值
let token = window.localStorage.getItem('token') || '';
if (token != '') {
//把token值放到header里面
config.headers['token'] = token;
}
return config
},
err => {
return Promise.reject(err);
})
生成订单
回到订单模块
所有准备工作完成,准备接上面的内容,调用相关远程服务接口生成微服务
service_order引入依赖
这三个都是远程Feign调用的
com.courseFeginService service_course_client 0.0.1-SNAPSHOT com.feginService service_user_client 0.0.1-SNAPSHOT com.activityFeginService service_activity_client 0.0.1-SNAPSHOT
订单业务具体工程实现 OrderInfoServiceImpl
@Autowired
private CourseFeignClient courseFeignClient;
@Autowired
private UserInfoFeignClient userInfoFeignClient;
@Autowired
private CouponInfoFeignClient couponInfoFeignClient;
//生成点播课程订单
@Override
public Long submitOrder(OrderFormVo orderFormVo) {
Long userId = AuthContextHolder.getUserId();
Long courseId = orderFormVo.getCourseId();
Long couponId = orderFormVo.getCouponId();
//查询当前用户是否已有当前课程的订单
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderDetail::getCourseId, courseId);
queryWrapper.eq(OrderDetail::getUserId, userId);
OrderDetail orderDetailExist = orderDetailService.getOne(queryWrapper);
if(orderDetailExist != null){
return orderDetailExist.getId(); //如果订单已存在,则直接返回订单id
}
//查询课程信息
Course course = courseFeignClient.getById(courseId);
if (course == null) {
throw new GlktException(ResultCodeEnum.DATA_ERROR.getCode(),
ResultCodeEnum.DATA_ERROR.getMessage());
}
//查询用户信息
UserInfo userInfo = userInfoFeignClient.getById(userId);
if (userInfo == null) {
throw new GlktException(ResultCodeEnum.DATA_ERROR.getCode(),
ResultCodeEnum.DATA_ERROR.getMessage());
}
//优惠券金额
BigDecimal couponReduce = new BigDecimal(0);
if(null != couponId) {
CouponInfo couponInfo = couponInfoFeignClient.getById(couponId);
couponReduce = couponInfo.getAmount();
}
//创建订单
OrderInfo orderInfo = new OrderInfo();
orderInfo.setUserId(userId);
orderInfo.setNickName(userInfo.getNickName());
orderInfo.setPhone(userInfo.getPhone());
orderInfo.setProvince(userInfo.getProvince());
orderInfo.setOriginAmount(course.getPrice());
orderInfo.setCouponReduce(couponReduce);
orderInfo.setFinalAmount(orderInfo.getOriginAmount().subtract(orderInfo.getCouponReduce()));
orderInfo.setOutTradeNo(OrderNoUtils.getOrderNo());
orderInfo.setTradeBody(course.getTitle());
orderInfo.setOrderStatus("0");
this.save(orderInfo);
OrderDetail orderDetail = new OrderDetail();
orderDetail.setOrderId(orderInfo.getId());
orderDetail.setUserId(userId);
orderDetail.setCourseId(courseId);
orderDetail.setCourseName(course.getTitle());
orderDetail.setCover(course.getCover());
orderDetail.setOriginAmount(course.getPrice());
orderDetail.setCouponReduce(new BigDecimal(0));
orderDetail.setFinalAmount(orderDetail.getOriginAmount().subtract(orderDetail.getCouponReduce()));
orderDetailService.save(orderDetail);
//更新优惠券状态
if(null != orderFormVo.getCouponUseId()) {
couponInfoFeignClient.updateCouponInfoUseStatus(orderFormVo.getCouponUseId(), orderInfo.getId());
}
return orderInfo.getId();
}
到这里期内容就结束了~
来我主页看看下一部分把~
硅谷课堂笔记下



