Day214.课程评论功能、微信支付实现课程支付模块前后端 -谷粒学院

谷粒学院

课程评论功能

一、数据库设计

  • 数据库

edu_comment

  • 数据表
CREATE 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='评论';

#
# Data for table "edu_comment"
#

INSERT INTO `edu_comment` VALUES ('1194499162790211585','1192252213659774977','1189389726308478977','1','小三123','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','课程很好',0,'2019-11-13 14:16:08','2019-11-13 14:16:08'),('1194898406466420738','1192252213659774977','1189389726308478977','1','小三123','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','11',0,'2019-11-14 16:42:35','2019-11-14 16:42:35'),('1194898484388200450','1192252213659774977','1189389726308478977','1','小三123','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','111',0,'2019-11-14 16:42:53','2019-11-14 16:42:53'),('1195251020861317122','1192252213659774977','1189389726308478977','1',NULL,NULL,'2233',0,'2019-11-15 16:03:45','2019-11-15 16:03:45'),('1195251382720700418','1192252213659774977','1189389726308478977','1',NULL,NULL,'4455',0,'2019-11-15 16:05:11','2019-11-15 16:05:11'),('1195252819177570306','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','55',0,'2019-11-15 16:10:53','2019-11-15 16:10:53'),('1195252899448160258','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','55',0,'2019-11-15 16:11:13','2019-11-15 16:11:13'),('1195252920587452417','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','223344',0,'2019-11-15 16:11:18','2019-11-15 16:11:18'),('1195262128095559681','14','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','11',0,'2019-11-15 16:47:53','2019-11-15 16:47:53'),('1196264505170767874','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','666666',0,'2019-11-18 11:10:58','2019-11-18 11:10:58');


二、后端部分

1、在service-edu模块,生成课程评论代码

(1)使用mp代码生成器生成

@Test
public void run() {

    // 1、创建代码生成器
    AutoGenerator mpg = new AutoGenerator();

    // 2、全局配置
    GlobalConfig gc = new GlobalConfig();
    String projectPath = System.getProperty("user.dir");
    gc.setOutputDir("D:\\JavaStudy\\gulixueyuan\\guli_parent\\service\\service_edu" + "/src/main/java"); //输出目录

    gc.setAuthor("achang"); //作者名
    gc.setOpen(false); //生成后是否打开资源管理器
    gc.setFileOverride(false); //重新生成时文件是否覆盖

    gc.setServiceName("%sService");	//去掉Service接口的首字母I
    gc.setIdType(IdType.ID_WORKER_STR); //主键策略
    gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
    gc.setSwagger2(true);//开启Swagger2模式

    mpg.setGlobalConfig(gc);

    // 3、数据源配置
    DataSourceConfig dsc = new DataSourceConfig();
    dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
    dsc.setDriverName("com.mysql.cj.jdbc.Driver");
    dsc.setUsername("root");
    dsc.setPassword("00000");
    dsc.setDbType(DbType.MYSQL);
    mpg.setDataSource(dsc);

    // 4、包配置
    PackageConfig pc = new PackageConfig();

    //生成包:com.achang.eduservice
    pc.setModuleName("eduservice"); //模块名
    pc.setParent("com.achang");

    //生成包:com.achang.oss.controller
    pc.setController("controller");
    pc.setEntity("entity");
    pc.setService("service");
    pc.setMapper("mapper");
    mpg.setPackageInfo(pc);

    // 5、策略配置
    StrategyConfig strategy = new StrategyConfig();
    strategy.setInclude("edu_comment");//根据数据库哪张表生成,有多张表就加逗号继续填写

    strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
    strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀

    strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
    strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

    strategy.setRestControllerStyle(true); //restful api风格控制器
    strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

    mpg.setStrategy(strategy);


    // 6、执行
    mpg.execute();
}

2、在service-ucenter模块,创建接口

image-20210308115011622

  • UcenterMemberController
@RestController
@RequestMapping("/serviceUcenter/ucenter-member")
@CrossOrigin
public class UcenterMemberController {

    @Autowired
    private UcenterMemberService ucenterMemberService;

    //根据用户id查询用户信息
    @PostMapping("/getMemberInfoById/{memberId}")
    public UcenterMemberVo getMemberInfoById(@PathVariable String memberId){
        UcenterMember member = ucenterMemberService.getById(memberId);
        UcenterMemberVo memberVo = new UcenterMemberVo();
        BeanUtils.copyProperties(member,memberVo);

        return memberVo;
    }

}
  • application.properties

添加到nacos中,用于远程调用

# nacos
spring.cloud.nacos.discovery.server-addr= localhost:8848

3、封装返回给评论服务的对象

  • UcenterMemberVo

com.achang.commonutils.vo.UcenterMemberVo

用于返回给调用的edu评论entity

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="UcenterMember对象", description="会员表")
public class UcenterMemberVo implements Serializable {

    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)未删除")
    private Boolean isDeleted;

    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;

}

4、创建课程评论controller

  • EduCommentController

com.achang.eduservice.controller.EduCommentController

@RestController
@RequestMapping("/eduservice/edu-comment")
@CrossOrigin
public class EduCommentController {

    @Autowired
    private EduCommentService eduCommentService;

    @Autowired
    private UcenterClient ucenterClient;

    //根据课程id_分页查询课程评论的方法
    @GetMapping("/getCommentPage/{page}/{limit}")
    public R getCommentPage(@PathVariable Long page,@PathVariable Long limit,String courseId){
        Page<EduComment> commentPage = new Page<>(page, limit);

        QueryWrapper<EduComment> wrapper = new QueryWrapper<>();

        //判断课程id是否为空
        if (!StringUtils.isEmpty(courseId)){
            wrapper.eq("course_id",courseId);
        }
        
        //按最新排序
        wrapper.orderByDesc("gmt_create");

        //数据会被封装到commentPage中
        eduCommentService.page(commentPage,wrapper);

        List<EduComment> commentList = commentPage.getRecords();
        long current = commentPage.getCurrent();//当前页
        long size = commentPage.getSize();//一页记录数
        long total = commentPage.getTotal();//总记录数
        long pages = commentPage.getPages();//总页数
        boolean hasPrevious = commentPage.hasPrevious();//是否有上页
        boolean hasNext = commentPage.hasNext();//是否有下页

        HashMap<String, Object> map = new HashMap<>();
        map.put("current",current);
        map.put("size",size);
        map.put("total",total);
        map.put("pages",pages);
        map.put("hasPrevious",hasPrevious);
        map.put("hasNext",hasNext);
        map.put("list",commentList);

        return R.ok().data(map);
    }

    //添加评论
    @PostMapping("/auth/addComment")
    public R addComment(HttpServletRequest request,@RequestBody EduComment eduComment){
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        //判断用户是否登录
        if (StringUtils.isEmpty(memberId)){
            throw new AchangException(20001,"请先登录");
        }
        eduComment.setMemberId(memberId);

        //远程调用ucenter根据用户id获取用户信息
        UcenterMemberVo memberVo = ucenterClient.getMemberInfoById(memberId);

        eduComment.setAvatar(memberVo.getAvatar());
        eduComment.setNickname(memberVo.getNickname());

        //保存评论
        eduCommentService.save(eduComment);

        return R.ok();
    }

}

5、Open Feign远程调用类

  • UcenterClient接口

com.achang.eduservice.client.UcenterClient

@Component
@FeignClient(name = "service-ucenter",fallback = UcenterClientImpl.class)
public interface UcenterClient {

    @PostMapping("/serviceUcenter/ucenter-member/getMemberInfoById/{memberId}")
    public UcenterMemberVo getMemberInfoById(@PathVariable String memberId);

}
  • UcenterClientImpl

兜底方法

com.achang.eduservice.client.impl.UcenterClientImpl

@Component
public class UcenterClientImpl implements UcenterClient {
    @Override
    public UcenterMemberVo getMemberInfoById(String memberId) {
        return null;
    }
}

三、前端部分

  • api方法_commonedu.js

guli-front\api\commonedu.js

import request from '@/utils/request'
export default {
    getPageList(page, limit, courseId) {
        return request({
            url: `/eduservice/edu-comment/getCommentPage/${page}/${limit}`,
            method: 'get',
            params: courseId
        })
    },
    addComment(comment) {
        return request({
            url: `/eduservice/edu-comment/auth/addComment`,
            method: 'post',
            data: comment
        })
    }
}

包含讲师页面的js代码

  • 页面js脚本
<script>
import courseApi from "@/api/course";
import comment from "@/api/commonedu";
export default {
  methods: {
    //获取课程详细信息
    getCourseInfo() {
      courseApi.getFrontCourseInfo(this.course.courseId).then((resp) => {
        this.chapterList = resp.data.data.chapterVideoList;
        this.course = resp.data.data.courseWebVo;
        this.course.courseId = resp.data.data.courseWebVo.id;
      });
    },
    initComment() {
      comment
        .getPageList(this.page, this.limit, this.course.courseId)
        .then((response) => {
          this.data = response.data.data;
          console.log(response.data.data);
        });
    },
    addComment() {
      this.comment.courseId = this.course.courseId;
      this.comment.teacherId = this.course.teacherId;
      comment.addComment(this.comment).then((response) => {
        if (response.data.success) {
          this.$message({
            message: "评论成功",
            type: "success",
          });
          this.comment.content = "";
          this.initComment();
        }
      });
    },
    gotoPage(page) {
      comment.getPageList(page, this.limit, this.courseId).then((response) => {
        this.data = response.data.data;
      });
    },
  },
  data() {
    return {
      chapterList: [],
      course: {
        courseId: "",
      },
      data: {},
      page: 1,
      limit: 4,
      total: 10,
      comment: {
        content: "",
        courseId: "",
        teacherId: "",
      },
      isbuyCourse: false,
    };
  },
  created() {
    this.course.courseId = this.$route.params.id;
    获取课程详细信息
    this.getCourseInfo();
    this.initComment();
  },
};
</script>

只包含评论和批量分页的html代码

  • 页面html代码
<div class="mt50 commentHtml">
    <div>
        <h6 class="c-c-content c-infor-title" id="i-art-comment">
            <span class="commentTitle">课程评论</span>
        </h6>
        <section class="lh-bj-list pr mt20 replyhtml">
            <ul>
                <li class="unBr">
                    <aside class="noter-pic">
                        <img
                             width="50"
                             height="50"
                             class="picImg"
                             src="~/assets/img/avatar-boy.gif"
                             />
                    </aside>
                    <div class="of">
                        <section class="n-reply-wrap">
                            <fieldset>
                                <textarea
                                          name=""
                                          v-model="comment.content"
                                          placeholder="输入您要评论的文字"
                                          id="commentContent"
                                          ></textarea>
                            </fieldset>
                            <p class="of mt5 tar pl10 pr10">
                                <span class="fl"
                                      ><tt
                                           class="c-red commentContentmeg"
                                           style="display: none"
                                           ></tt
                                    ></span>
                                <input
                                       type="button"
                                       @click="addComment()"
                                       value="回复"
                                       class="lh-reply-btn"
                                       />
                            </p>
                        </section>
                    </div>
                </li>
            </ul>
        </section>
        <section class="">
            <section class="question-list lh-bj-list pr">
                <ul class="pr10">
                    <li v-for="comment in data.list" :key="comment.id">
                        <aside class="noter-pic">
                            <img
                                 width="50"
                                 height="50"
                                 class="picImg"
                                 :src="comment.avatar"
                                 />
                        </aside>
                        <div class="of">
                            <span class="fl">
                                <font class="fsize12 c-blue">{{ comment.nickname }}</font>
                                <font class="fsize12 c-999 ml5">评论:</font></span
                                >
                        </div>
                        <div class="noter-txt mt5">
                            <p>{{ comment.content }}</p>
                        </div>
                        <div class="of mt5">
                            <span class="fr"
                                  ><font class="fsize12 c-999ml5">{{
                                comment.gmtCreate
                                }}</font></span
                                >
                        </div>
                    </li>
                </ul>
            </section>
        </section>
        <!-- 公共分页 开始 -->
        <div class="paging">
            <!-- undisable这个class是否存在,取决于数据属性hasPrevious -->
            <a
               :class="{ undisable: !data.hasPrevious }"
               href="#"
               title="首页"
               @click.prevent="gotoPage(1)"
               ></a
                >
            <a
               :class="{ undisable: !data.hasPrevious }"
               href="#"
               title="前一页"
               @click.prevent="gotoPage(data.current - 1)"
               >&lt;</a
                >
            <a
               v-for="page in data.pages"
               :key="page"
               :class="{
                       current: data.current == page,
                       undisable: data.current == page,
                       }"
               :title="'' + page + ''"
               href="#"
               @click.prevent="gotoPage(page)"
               >{{ page }}</a
                >
            <a
               :class="{ undisable: !data.hasNext }"
               href="#"
               title="后一页"
               @click.prevent="gotoPage(data.current + 1)"
               >&gt;</a
                >
            <a
               :class="{ undisable: !data.hasNext }"
               href="#"
               title="末页"
               @click.prevent="gotoPage(data.pages)"
               ></a
                >
            <div class="clear" />
        </div>
        <!-- 公共分页 结束 -->
    </div>
</div>

四、测试

  • 刚进入页面显示

image-20210308143601578

  • 回复功能

image-20210308143620034


课程支付需求描述

1、课程支付说明

( 1 )课程分为免费课程和付费课程,如果是免费课程可以直接观看,如果是付费观看的课程,用户需下单支付后才可以观看

image-20210308144528543

(2)如果是免费课程,在用户选择课程,进入到课程详情页面时候,直接显示 “立即观看”,用户点击立即观看,可以切换到播放列表进行视频播放

image-20210308144555404

2、付费课程流程

(1)如果是付费课程,在用户选择课程,进入到课程详情页面时候,会显示 “立即购买”

image-20210308144624518

(2)点击“立即购买”,会生成课程的订单,跳转到订单页面

image-20210308144639243

(3)点击“去支付”,会跳转到支付页面,生成微信扫描的二维码

image-20210308144647697

(4)使用微信扫描支付后,会跳转回到课程详情页面,同时显示“立即观看”

image-20210308144716913


支付模块和开发订单接口

一、创建支付模块和环境准备

1、在service模块下创建子模块service_order

image-20210308145632290

2、在service_order模块中引入依赖

<dependencies>
    <dependency>
        <groupId>com.github.wxpay</groupId>
        <artifactId>wxpay-sdk</artifactId>
        <version>0.0.3</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
    </dependency>
</dependencies>

3、创建支付相关的表

image-20210308145736260

  • 两张表:t_order、t_pay_log
CREATE 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='订单';

#
# Data for table "t_order"
#

INSERT INTO `t_order` VALUES ('1195693605513891841','1195693605555834880','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-16 21:22:25','2019-11-16 21:22:25'),('1195694200178118657','1195694200186507264','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-16 21:24:47','2019-11-16 21:24:47'),('1196264007411744769','1196264005255872512','1192252213659774977','java基础课程:test','https://guli-file-190513.oss-cn-beijing.aliyuncs.com/cover/default.gif','晴天','1','小三1231','13700000001',1,NULL,1,0,'2019-11-18 11:09:00','2019-11-18 11:10:14'),('1196265495278174209','1196265495273979904','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-18 11:14:54','2019-11-18 11:14:54');

#
# Structure for table "t_pay_log"
#

CREATE 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='支付日志表';

#
# Data for table "t_pay_log"
#

INSERT INTO `t_pay_log` VALUES ('1194498446013001730','1194498300579704832','2019-11-13 14:13:17',1,'4200000469201911130676624386','SUCCESS',1,'{\"transaction_id\":\"4200000469201911130676624386\",\"nonce_str\":\"2Lc23ILl231It53M\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"5404850AA3ED0E844DE104651477F07A\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1194498300579704832\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191113141314\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-13 14:13:17','2019-11-13 14:13:17'),('1195253787449430017','1195253049260314624','2019-11-15 16:14:44',1,'4200000454201911150981874895','SUCCESS',1,'{\"transaction_id\":\"4200000454201911150981874895\",\"nonce_str\":\"MAM5UM4Xhv1lItvO\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"7DBDCAF4A078B30BB3EF073E6A238C20\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1195253049260314624\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191115161440\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-15 16:14:44','2019-11-15 16:14:44'),('1196264321397342210','1196264005255872512','2019-11-18 11:10:14',1,'4200000453201911184025781554','SUCCESS',1,'{\"transaction_id\":\"4200000453201911184025781554\",\"nonce_str\":\"D1dHexCLIFIxAAg2\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"C9F5CA1EE49EA7891736D73BEB423962\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1196264005255872512\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191118111011\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-18 11:10:14','2019-11-18 11:10:14');


4、使用代码生成器生成相关代码

@Test
public void run() {

    // 1、创建代码生成器
    AutoGenerator mpg = new AutoGenerator();

    // 2、全局配置
    GlobalConfig gc = new GlobalConfig();
    String projectPath = System.getProperty("user.dir");
    gc.setOutputDir("D:\\JavaStudy\\gulixueyuan\\guli_parent\\service\\service_order" + "/src/main/java"); //输出目录

    gc.setAuthor("achang"); //作者名
    gc.setOpen(false); //生成后是否打开资源管理器
    gc.setFileOverride(false); //重新生成时文件是否覆盖

    gc.setServiceName("%sService");	//去掉Service接口的首字母I
    gc.setIdType(IdType.ID_WORKER_STR); //主键策略
    gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
    gc.setSwagger2(true);//开启Swagger2模式

    mpg.setGlobalConfig(gc);

    // 3、数据源配置
    DataSourceConfig dsc = new DataSourceConfig();
    dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
    dsc.setDriverName("com.mysql.cj.jdbc.Driver");
    dsc.setUsername("root");
    dsc.setPassword("00000");
    dsc.setDbType(DbType.MYSQL);
    mpg.setDataSource(dsc);

    // 4、包配置
    PackageConfig pc = new PackageConfig();

    //生成包:com.achang.eduservice
    pc.setModuleName("eduservice"); //模块名
    pc.setParent("com.achang");

    //生成包:com.achang.oss.controller
    pc.setController("controller");
    pc.setEntity("entity");
    pc.setService("service");
    pc.setMapper("mapper");
    mpg.setPackageInfo(pc);

    // 5、策略配置
    StrategyConfig strategy = new StrategyConfig();
    strategy.setInclude("t_order","t_pay_log");//根据数据库哪张表生成,有多张表就加逗号继续填写

    strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
    strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀

    strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
    strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

    strategy.setRestControllerStyle(true); //restful api风格控制器
    strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

    mpg.setStrategy(strategy);


    // 6、执行
    mpg.execute();
}

5、application.properties配置文件

# 服务端口
server.port=8007

# 服务名
spring.application.name=service-order

# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=00000

# 返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

# 配置mapper xml文件路径
mybatis-plus.mapper-locations=classpath:com.achang.eduservice.mapper.xml/*.xml

# mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

# nacos注册中心
spring.cloud.nacos.discovery.server-addr= localhost:8848

# 开启熔断机制
feign.hystrix.enabled=true

# 设置hystrix超时时间,默认1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000

二、【生成订单】接口

image-20210308154014623

1、生成订单的接口

image-20210308155303206

2、远程调用、订单公共返回类Vo

用于远程接口调用后返回给订单接口的统一返回类

  • EduCourseVo
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class EduCourseVo implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "课程id")
    private String id;

    @ApiModelProperty(value = "课程标题")
    private String title;

    @ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")
    private BigDecimal price;

    @ApiModelProperty(value = "总课时")
    private Integer lessonNum;

    @ApiModelProperty(value = "课程封面图片路径")
    private String cover;

    @ApiModelProperty(value = "销售数量")
    private Long buyCount;

    @ApiModelProperty(value = "浏览数量")
    private Long viewCount;

    @ApiModelProperty(value = "课程简介")
    private String description;

    @ApiModelProperty(value = "讲师ID")
    private String teacherId;

    @ApiModelProperty(value = "讲师姓名")
    private String teacherName;

    @ApiModelProperty(value = "讲师资历,一句话说明讲师")
    private String intro;

    @ApiModelProperty(value = "讲师头像")
    private String avatar;

    @ApiModelProperty(value = "课程一级类别ID")
    private String subjectLevelOneId;

    @ApiModelProperty(value = "类别一级名称")
    private String subjectLevelOne;

    @ApiModelProperty(value = "课程二级类别ID")
    private String subjectLevelTwoId;

    @ApiModelProperty(value = "类别二级名称")
    private String subjectLevelTwo;

}
  • UcenterMemberVo
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="UcenterMember对象", description="会员表")
public class UcenterMemberVo implements Serializable {

    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)未删除")
    private Boolean isDeleted;

    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;


}

3、获取课程信息

  • EduCourseController
@RestController
@RequestMapping("/eduservice/edu-course")
@CrossOrigin //解决跨域问题
public class EduCourseController {

    @Autowired
    private EduCourseService eduCourseService;
	
    .......
        
    //根据课程id,查询课程信息【订单】
    @PostMapping("/getCourseInfoByIdOrder/{courseId}")
    public EduCourseVo getCourseInfoByIdOrder(@PathVariable String courseId){
        CourseWebVo courseInfo = eduCourseService.getBaseCourseInfo(courseId);

        EduCourseVo eduCourseVo = new EduCourseVo();
        BeanUtils.copyProperties(courseInfo,eduCourseVo);

        return eduCourseVo;

    }

}

4、获取用户信息

  • UcenterMemberController
@RestController
@RequestMapping("/serviceUcenter/ucenter-member")
@CrossOrigin
public class UcenterMemberController {

    @Autowired
    private UcenterMemberService ucenterMemberService;
    
    ........

    //根据用户id查询用户信息
    @PostMapping("/getMemberInfoById/{memberId}")
    public UcenterMemberVo getMemberInfoById(@PathVariable String memberId){
        UcenterMember member = ucenterMemberService.getById(memberId);
        UcenterMemberVo memberVo = new UcenterMemberVo();
        BeanUtils.copyProperties(member,memberVo);

        return memberVo;
    }

}

5、远程调用Client接口

com.achang.eduservice.client

  • ServiceEduClient接口

用于调用EduService模块的OpenFeign接口

@Component
@FeignClient(name = "service-edu")
public interface ServiceEduClient {

    //根据课程id,获取课程信息
    @PostMapping("/eduservice/edu-course/getCourseInfoByIdOrder/{courseId}")
    public EduCourseVo getCourseInfoByIdOrder(@PathVariable("courseId") String courseId);

}
  • ServiceUcenterClient接口

用于调用Ucenter模块的OpenFeign接口

@Component
@FeignClient(name = "service-ucenter")
public interface ServiceUcenterClient {

    //根据用户id,获取用户信息
    @PostMapping("/serviceUcenter/ucenter-member/getMemberInfoById/{memberId}")
    public UcenterMemberVo getMemberInfoById(@PathVariable("memberId") String memberId);

}

6、订单模块实现

  • TOrderController
@RestController
@CrossOrigin
@RequestMapping("/eduorder/t-order")
public class TOrderController {

    @Autowired
    private TOrderService tOrderService;

    //生成订单的方法
    @PostMapping("/createOrder/{courseId}")
    public R createOrder(@PathVariable String courseId, HttpServletRequest request){
        //从请求头中获取用户id
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        //判断是否登录
        if (StringUtils.isEmpty(memberId)){
            throw new AchangException(20001,"请登录");
        }

        //生成订单,并生成对应的订单号
        String orderNo = tOrderService.createOrders(courseId,memberId);

        return R.ok().data("orderNo",orderNo);
    }

}
  • service

接口

public interface TOrderService extends IService<TOrder> {
    //生成订单的方法
    String createOrders(String courseId, String memberId);
}

Impl

@Service
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements TOrderService {

    //远程调用serviceEdu
    @Autowired
    private ServiceEduClient serviceEduClient;

    //远程调用serviceUcenter
    @Autowired
    private ServiceUcenterClient serviceUcenterClient;

    //生成订单的方法
    @Override
    public String createOrders(String courseId, String memberId) {
        //根据用户id,获取用户信息
        UcenterMemberVo memberInfo = serviceUcenterClient.getMemberInfoById(memberId);

        //根据课程id,获取课程信息
        EduCourseVo courseInfo = serviceEduClient.getCourseInfoByIdOrder(courseId);

        TOrder tOrder = new TOrder();
        tOrder.setMobile(memberInfo.getMobile());
        tOrder.setNickname(memberInfo.getNickname());
        tOrder.setMemberId(memberId);
        tOrder.setCourseCover(courseInfo.getCover());
        tOrder.setCourseId(courseId);
        tOrder.setCourseTitle(courseInfo.getTitle());
        tOrder.setTeacherName(courseInfo.getTeacherName());
        tOrder.setTotalFee(courseInfo.getPrice());
        tOrder.setStatus(0);//支付状态:( 0:已支付,1:未支付 )
        tOrder.setPayType(1);//支付类型: 1:微信 , 2:支付宝
        tOrder.setOrderNo(OrderNoUtil.getOrderNo()); //订单号

        //保存订单
        baseMapper.insert(tOrder);

        //返回订单号
        return tOrder.getOrderNo();
    }
    
}

7、主启动类

需要

@EnableDiscoveryClient开启服务发现

@EnableFeignClients开启OpenFeign远程调用调用方需要配置

  • ServiceOrderMain8007
@SpringBootApplication
@EnableFeignClients
@MapperScan("com.achang.eduservice.mapper")
@ComponentScan("com.achang")
@EnableDiscoveryClient //服务发现功能
public class ServiceOrderMain8007 {
    public static void main(String[] args) {
        SpringApplication.run(ServiceOrderMain8007.class,args);
    }
}
  • Service_edu_Main8001
@SpringBootApplication
@EnableDiscoveryClient //服务发现功能
@ComponentScan(basePackages = "com.achang")
......
public class Service_edu_Main8001 {
    public static void main(String[] args) {
        SpringApplication.run(Service_edu_Main8001.class,args);
    }
}
  • serviceUcenterMain8006
@SpringBootApplication
@ComponentScan("com.achang")
@MapperScan("com.achang.serviceUcenter.mapper")
@EnableDiscoveryClient //开启服务发现
public class serviceUcenterMain8006 {
    public static void main(String[] args) {
        SpringApplication.run(serviceUcenterMain8006.class,args);
    }
}

8、配置类

都需要设置其注册中心nacos地址

# nacos
spring.cloud.nacos.discovery.server-addr= localhost:8848

三、【根据订单号查询订单信息】接口

  • TOrderController
    //根据订单号查询订单信息
    @GetMapping("/getOrderInfoById/{orderNo}")
    public R getOrderInfoById(@PathVariable String orderNo){
        QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no",orderNo);
        TOrder tOrder = tOrderService.getOne(wrapper);
        return R.ok().data("item",tOrder);
    }

微信支付接口

一、生成微信支付二维码

1、编写controller

TPayLogServiceImpl

@RestController
@RequestMapping("/eduorder/t-pay-log")
@CrossOrigin
public class TPayLogController {

    @Autowired
    private TPayLogService tPayLogService;

    //根据订单号,生成微信支付二维码的接口
    @GetMapping("/createWxQRcode/{orderNo}")
    public R createWxQRcode(@PathVariable String orderNo){
        //返回信息,包含二维码地址、其他信息
        Map<String, Object> map = tPayLogService.createWxQrcode(orderNo);
        return R.ok().data(map);
    }

}

2、编写service

  • 接口

TPayLogService

public interface TPayLogService extends IService<TPayLog> {

    //根据订单号,生成微信支付二维码的接口
    Map<String, Object> createWxQrcode(String orderNo);
}
  • Impl

TPayLogServiceImpl

@Service
public class TPayLogServiceImpl extends ServiceImpl<TPayLogMapper, TPayLog> implements TPayLogService {

    @Autowired
    private TOrderService tOrderService;

    //根据订单号,生成微信支付二维码的接口
    @Override
    public Map<String, Object> createWxQrcode(String orderNo) {
        try{
            //1、根据订单号查询订单信息
            QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
            wrapper.eq("order_no",orderNo);
            TOrder tOrder = tOrderService.getOne(wrapper);

            //2、使用map来设置生成二维码需要的参数
            HashMap<String, String> map = new HashMap<>();
            map.put("appid","wx74862e0dfcf69954");//支付id
            map.put("mch_id", "1558950191");//商户号
            map.put("nonce_str", WXPayUtil.generateNonceStr());//生成随机的字符串,让每次生成的二维码不一样
            map.put("body", tOrder.getCourseTitle());//生成二维码的名字
            map.put("out_trade_no", orderNo);//二维码唯一的标识
            map.put("total_fee", tOrder.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");//支付金额
            map.put("spbill_create_ip", "127.0.0.1");//现在进行支付的ip地址,实际项目使用项目的域名
            map.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");//支付后回调地址
            map.put("trade_type", "NATIVE");//支付类型,NATIVE:根据价格生成二维码

            //3、发送httpClient请求,传递参数【xml格式】,微信支付提供的固定地址
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            //参数1:要转换为xml格式的map
            //参数2:商户的key,用于加密二维码中的信息
            client.setXmlParam(WXPayUtil.generateSignedXml(map,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
            client.setHttps(true);//上面发送请求的是https。默认支持,需要设置为true支持
            //执行post请求发送
            client.post();

            //4、得到发送请求返回的结果
            //返回的结果是xml格式的
            String content = client.getContent();

            //把xml格式转换为map集合,他里面的数据不全
            Map<String, String> resultMap = WXPayUtil.xmlToMap(content);

            //最终返回数据封装
            HashMap hashMap = new HashMap<>();
            hashMap.put("out_trade_no",orderNo);
            hashMap.put("course_id",tOrder.getCourseId());
            hashMap.put("total_fee",tOrder.getTotalFee());
            hashMap.put("result_code",resultMap.get("result_code")); //二维码操作状态码
            hashMap.put("code_url",resultMap.get("code_url")); //二维码地址

            //微信支付二维码2小时过期,可采取2小时未支付取消订单
            //redisTemplate.opsForValue().set(orderNo, hashMap, 120,TimeUnit.MINUTES);

            return hashMap;


        }catch (Exception e){
            e.printStackTrace();
            throw new AchangException(20001,"生成二维码失败");
        }
        
    }

}

二、获取支付状态接口

1、编写controller

TPayLogController

@RestController
@RequestMapping("/eduorder/t-pay-log")
@CrossOrigin
public class TPayLogController {

    @Autowired
    private TPayLogService tPayLogService;
    
    .....

    //根据订单号查询订单支付状态
    @GetMapping("/queryPayStatus/{orderNo}")
    public R queryPayStatus(@PathVariable String orderNo){

        Map<String,String> map = tPayLogService.queryPayStatus(orderNo);
        if (map==null){
            return R.error().message("支付出错了");
        }
        //如果返回的map不为空,通过map获取订单的状态
        if (map.get("trade_state").equals("SUCCESS")){ //支付成功
            //添加记录到支付表里,并更新订单表的状态
            tPayLogService.updateOrderStatus(map);
            return R.ok().message("支付成功");
        }

        return R.ok().message("支付中");
    }

}

2、编写service

TPayLogService接口

public interface TPayLogService extends IService<TPayLog> {
	
    ........
    
    //根据订单号,查询支付的状态
    Map<String, String> queryPayStatus(String orderNo);

    //向支付表中添加支付记录,并更新订单表的订单状态
    void updateOrderStatus(Map<String, String> map);
}

TPayLogServiceImpl

package com.achang.eduservice.service.impl;

import com.achang.eduservice.entity.TOrder;
import com.achang.eduservice.entity.TPayLog;
import com.achang.eduservice.mapper.TPayLogMapper;
import com.achang.eduservice.service.TOrderService;
import com.achang.eduservice.service.TPayLogService;
import com.achang.eduservice.uitls.HttpClient;
import com.achang.servicebase.exceptionHandler.AchangException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 支付日志表 服务实现类
 * </p>
 *
 * @author achang
 * @since 2021-03-08
 */
@Service
public class TPayLogServiceImpl extends ServiceImpl<TPayLogMapper, TPayLog> implements TPayLogService {

    @Autowired
    private TOrderService tOrderService;

	........

    //根据订单号,查询支付的状态
    @Override
    public Map<String, String> 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.setHttps(true);
        client.setXmlParam(WXPayUtil.generateSignedXml(m,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));//通过商户key加密
        client.post();

        //3、返回第三方的数据
        String xml = client.getContent();
        //4、转成Map
        Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);

        //5、返回
        return resultMap;

        }catch (Exception e){
            e.printStackTrace();
        }
        return null;

    }

    //向支付表中添加支付记录,并更新订单表的订单状态
    @Override
    public void updateOrderStatus(Map<String, String> map) {
        //获取前一步生成二维码中的订单号
        String orderNo = map.get("out_trade_no");
        //根据订单号,查询订单信息
        QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no",orderNo);
        TOrder tOrder = tOrderService.getOne(wrapper);


        //判断订单状态是否为1,为1就是支付过了
        if (tOrder.getStatus().intValue()==1){return;}

        //更新订单表中的叮当状态
        tOrder.setStatus(1);//1代表已支付
        tOrderService.updateById(tOrder);

        //向支付表里添加支付记录
        TPayLog tPayLog = new TPayLog();
        tPayLog.setOrderNo(orderNo);//支付订单号
        tPayLog.setPayTime(new Date());//支付时间
        tPayLog.setPayType(1);//支付类型
        tPayLog.setTotalFee(tOrder.getTotalFee());//总金额(分)
        tPayLog.setTradeState(map.get("trade_state"));//支付状态
        tPayLog.setTransactionId(map.get("transaction_id"));//订单流水号
        tPayLog.setAttr(JSONObject.toJSONString(map));
        baseMapper.insert(tPayLog);


    }

}

整合生成订单页面

一、页面样式修改

1、复制样式文件到assets

image-20210308190323669

2、修改default.vue页面

import '~/assets/css/reset.css'
import '~/assets/css/theme.css'
import '~/assets/css/global.css'
import '~/assets/css/web.css'
import '~/assets/css/base.css'
import '~/assets/css/activity_tab.css'
import '~/assets/css/bottom_rec.css'
import '~/assets/css/nice_select.css'
import '~/assets/css/order.css'
import '~/assets/css/swiper-3.3.1.min.css'
import "~/assets/css/pages-weixinpay.css"

image-20210308190400300

二、课程支付前端

1、在api文件夹下创建order.js文件

import request from '@/utils/request'

export default{
    //生成订单
    createOrder(courseId){
        return request({
            url: '/eduorder/t-order/createOrder/'+courseId,
            method: 'post'
        })
    },
    //根据订单号查询订单信息
    getOrderInfoByNo(orderNo){
        return request({
            url: `/eduorder/t-order/getOrderInfoById/${orderNo}`,
            method: 'get'
        })
    },
    //根据订单号,生产二维码
    createWxQRcode(orderNo){
        return request({
            url: `/eduorder/t-pay-log//createWxQRcode/${orderNo}`,
            method: 'get'
        })
    },
    //根据订单号,查询订单支付状态
    getPayStatus(orderNo){
        return request({
            url: `/eduorder/t-pay-log//queryPayStatus/${orderNo}`,
            method: 'get'
        })
    },
}

2、在课程详情页面中添加创建订单方法

在“立即购买”位置添加事件

image-20210308232111478

//生成订单的方法
createOrders(){
    orderApi.createOrder(this.course.courseId).then(resp=>{
        //获取返回订单号
        resp.data.data.orderNo
        //跳转订单显示页面
        this.$router.push({path:'/orders/'+resp.data.data.orderNo})
    })
},

3、创建订单页面,显示订单信息

在pages下面创建order文件夹,创建_oid.vue页面

image-20210308232211133

在_orderNo.vue页面调用方法,获取订单信息
(1)页面部分

<template>
    <div class="Page Confirm">
        <div class="Title">
            <h1 class="fl f18">订单确认</h1>
            <img src="~/assets/img/cart_setp2.png" class="fr" />
            <div class="clear"></div>
        </div>
        <form name="flowForm" id="flowForm" method="post" action="">
            <table class="GoodList">
                <tbody>
                    <tr>
                        <th class="name">商品</th>
                        <th class="price">原价</th>
                        <th class="priceNew">价格</th>
                    </tr>
                </tbody>
                <tbody>
                    <!-- <tr>
<td colspan="3" class="Title red f18 fb"><p>限时折扣</p></td>
</tr> -->
                    <tr>
                        <td colspan="3" class="teacher">讲师:{{ order.teacherName }}</td>
                    </tr>
                    <tr class="good">
                        <td class="name First">
                            <a
                               target="_blank"
                               :href="'https://localhost:3000/course/' + order.courseId"
                               >
                                <img :src="order.courseCover"
                                     /></a>
                            <div class="goodInfo">
                                <input type="hidden" class="ids ids_14502" value="14502" />
                                <a
                                   target="_blank"
                                   :href="'https://localhost:3000/course/' + order.courseId"
                                   >{{ order.courseTitle }}</a
                                    >
                            </div>
                        </td>
                        <td class="price">
                            <p><strong>{{ order.totalFee }}</strong>
                            </p>
                            <!-- <span class="discName red">限时8折</span> -->
                        </td>
                        <td class="red priceNew Last"><strong>{{ order.totalFee }}</strong>
                        </td>
                    </tr>
                    <tr>
                        <td class="Billing tr" colspan="3">
                            <div class="tr">
                                <p><strong class="red">1</strong> 件商品,合计<span
                                                                                 class="red f20"
                                                                                 ><strong>{{ order.totalFee }}</strong></span
                                    >
                                </p>
                            </div>
                        </td>
                    </tr>
                </tbody>
            </table>
            <div class="Finish">
                <div class="fr" id="AgreeDiv">
                    <label for="Agree"
                           ><p class="on">
                        <input type="checkbox" checked="checked" />我已阅读并同意<a
                                                                             href="javascript:"
                                                                             target="_blank"
                                                                             >《谷粒学院购买 协议》</a
                        >
                        </p></label
                        >
                </div>
                <div class="clear"></div>
                <div class="Main fl">
                    <div class="fl">
                        <a :href="'/course/' + order.courseId">返回课程详情页</a>
                    </div>
                    <div class="fr">
                        <p><strong class="red">1</strong> 件商品,合计<span
                                                                         class="red f20"
                                                                         ><strong id="AllPrice">{{ order.totalFee }}</strong></span
                            >
                        </p>
                    </div>
                </div>
                <input name="score" value="0" type="hidden" id="usedScore" />
                <button class="fr redb" type="button" id="submitPay" @click="toPay()">
                    去支 付
                </button>
                <div class="clear"></div>
            </div>
        </form>
    </div>
</template>

(2)js脚本部分

<script>
    import orderApi from '@/api/order'
export default {
    created() {
        this.orderNo = this.$route.params.orderNo
        this.getOrderInfoByOrderNo()
    },
    methods: {
        getOrderInfoByOrderNo(){
            orderApi.getOrderInfoByNo(this.orderNo).then(resp=>{
                this.order = resp.data.data.item
            })
        },
        toPay(){
            orderApi.createWxQRcode().then(resp=>{
                this.$router.push({path:'/pay/'+this.orderNo})
            })
        }

    },
    data() {
        return {
            order:{},
            orderNo:'',

        }
    },
}
</script>

4、创建支付页面,生成二维码完成支付

image-20210308232406702

(1)页面部分

<template>
  <div class="cart py-container">
    <!--主内容-->
    <div class="checkout py-container pay">
      <div class="checkout-tit">
        <h4 class="fl tit-txt">
          <span class="success-icon"></span
          ><span class="success-info"
            >订单提交成功,请您及时付款!订单 号:{{
              payObj.out_trade_no
            }}</span
          >
        </h4>
        <span class="fr"
          ><em class="sui-lead">应付金额:</em
          ><em class="orange money">¥{{ payObj.total_fee }}</em></span
        >
        <div class="clearfix"></div>
      </div>
      <div class="checkout-steps">
        <div class="fl weixin">微信支付</div>
        <div class="fl sao">
          <p class="red">请使用微信扫一扫。</p>
          <div class="fl code">
            <!-- <img id="qrious" src="~/assets/img/erweima.png" alt=""> -->
            <!-- <qriously value="weixin://wxpay/bizpayurl?pr=R7tnDpZ":size="338"/> -->
            <!--code_url为二维码地址,这里需要通过一个vue的组件【qriously】显示-->
            <!--执行npm install vue-qriously下载组件-->
            <qriously :value="payObj.code_url" :size="338" />
            <div class="saosao">
              <p>请使用微信扫一扫</p>
              <p>扫描二维码支付</p>
            </div>
          </div>
        </div>
        <div class="clearfix"></div>
        <!-- <p><a href="pay.html" target="_blank">> 其他支付方式</a></p> -->
      </div>
    </div>
  </div>
</template>

(2)js脚本部分

<script>
    import orderApi from "@/api/order";
export default {
    created() {
        this.orderNo = this.$route.params.orderNo;
        //生成二维码
        this.createQRcode();
    },
    methods: {
        //生成二维码
        createQRcode() {
            orderApi.createWxQRcode(this.orderNo).then((resp) => {
                this.payObj = resp.data.data;
            });
        },
        //根据订单号,查询支付的状态
        queryPayStatus(out_trade_no) {
            orderApi.getPayStatus(out_trade_no).then((resp) => {
                if (resp.data.success) {
                    //如果支付成功,清除定时器
                    clearInterval(this.timer1);
                    //提示
                    this.$message({
                        type: "success",
                        message: "支付成功!",
                    });
                    //跳转到课程详细页面观看视频
                    this.$router.push({ path: "/course/" + this.payObj.course_id });
                }
            });
        },
    },
    data() {
        return {
            orderNo: "",
            payObj: {},
            timer1: "",
        };
    },
    //每个三秒去调用一次查询订单状态的方法
    //在页面渲染之后调用
    mounted() {
        this.timer1 = setInterval(() => {
            this.queryPayStatus(this.payObj.out_trade_no);
        }, 3000);
    },
};
</script>

(3)拦截器

  • guli-front\utils\request.js

拦截响应,看在是否有25000代码,如果有就不做任何动作

image-20210308232838438

// 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) // 返回接口返回的错误信息
    });

三、测试

  • 开启nacos,开启8001/8006/8007

image-20210308233614373

image-20210308233656985

image-20210308232944681

image-20210308233039362

image-20210308233156121

image-20210308233234651

image-20210308233302141

image-20210308233407644

image-20210308233534464

测试完成

  • 19
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 38
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿昌喜欢吃黄桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值