Day15 微信支付

新建微信支付模块

创建支付表数据

根据表信息生成代码

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;

/**
 * @author
 * @since 2018/12/13
 */
public class CodeGenerator {

    @Test
    public void run() {

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

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

        gc.setAuthor("zyfTest");
        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=Asia/Shanghai");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("636474");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
        //com.atguigu.eduservice
        pc.setModuleName("eduorder"); //模块名
        pc.setParent("com.atguigu");

        //com.atguigu.eduservice.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();
    }
}

配置文件

#项目端口号
server.port=8009
#项目名称
spring.application.name=service-order
# ?????dev(????)?test ?????prod ????  项目运行环境
spring.profiles.active=dev
# mysql?????
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.datasource.url=jdbc:mysql://localhost:3306/guli?characterEncoding=utf-8&useSSL=false
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=636474


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

# 设置日志级别
#logging.level.root=INFO
#mybatis??
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#??Swagger
spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER

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

# nacos服务地址  127.0.0.1表示本机运行,部署服务器需要更改ip
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848


#开启熔断机制
feign.hystrix.enabled=true
# 设置hystrix超时时间,默认1000ms
#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000

生成订单

controller

@RestController
@RequestMapping("/eduorder/t-order")
@Api(description = "订单管理")
@CrossOrigin
public class TOrderController {

    //注入订单管理service
    @Autowired
    private TOrderService tOrderService;

    //根据课程id生成订单,返回订单id
    @ApiOperation(value = "根据课程id生成订单")
    @GetMapping("createOrder/{courseId}")
    public R createOrder(@PathVariable String courseId, HttpServletRequest request){
        //生成订单需要课程信息和用户信息,远程调用获取,用户id从token中取
       String OrderId =  tOrderService.saveOrder(courseId, JwtUtils.getMemberIdByJwtToken(request));
       return R.ok().data("OrderId",OrderId);
    }
}

service

package com.atguigu.eduorder.service.impl;

import com.atguigu.commonutils.orderVo.CourseWebVoOrder;
import com.atguigu.commonutils.orderVo.UcenterMemberOrderVo;
import com.atguigu.eduorder.clint.CourseClint;
import com.atguigu.eduorder.clint.UcenterClint;
import com.atguigu.eduorder.entity.TOrder;
import com.atguigu.eduorder.mapper.TOrderMapper;
import com.atguigu.eduorder.service.TOrderService;
import com.atguigu.eduorder.utils.OrderNoUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author zyfTest
 * @since 2023-02-08
 */
@Service
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements TOrderService {

    //注入两个远程调用的接口
    @Autowired
    private CourseClint courseClint;

    @Autowired
    private UcenterClint ucenterClint;

    @Override
    public String saveOrder(String courseId, String memberId) {
        //根据课程id获取课程信息
        CourseWebVoOrder courseWebVoOrder = courseClint.selectCourseByIdIsOrder(courseId);
        //根据ucenterid获取用户信息
        UcenterMemberOrderVo ucenterMemberOrderVo = ucenterClint.selectUcenterInfoById(memberId);

        TOrder tOrder = new TOrder();
        tOrder.setOrderNo(OrderNoUtil.getOrderNo());//订单号
        tOrder.setCourseId(courseWebVoOrder.getId());//课程id
        tOrder.setCourseTitle(courseWebVoOrder.getTitle());//课程名称
        tOrder.setCourseCover(courseWebVoOrder.getCover());//课程封面
        tOrder.setTeacherName(courseWebVoOrder.getTeacherName());//讲师名称


        tOrder.setMemberId(ucenterMemberOrderVo.getId());//用户id
        tOrder.setNickname(ucenterMemberOrderVo.getNickname());//用户昵称
        tOrder.setMobile(ucenterMemberOrderVo.getMobile());//用户手机

        tOrder.setStatus(0);//订单状态 0 未支付 1 已支付
        tOrder.setPayType(1); //支付方式 1 微信支付 2 支付宝支付

        //将信息封装到TOrder对象插入数据库返回订单号
        baseMapper.insert(tOrder);
        String orderNo = tOrder.getOrderNo();
        return orderNo;
    }
}

远程调用接口获取用户信息和课程信息

获取课程信息

    //根据课程id查询课程信息
    @ApiOperation("根据课程id查询课程信息")
    @PostMapping("selectCourseByIdIsOrder/{id}")
    public CourseWebVoOrder selectCourseByIdIsOrder(@PathVariable String id){
        CourseWebVo courseWebVo = courseService.selectCourseById(id);
        CourseWebVoOrder courseWebVoOrder = new CourseWebVoOrder();
        BeanUtils.copyProperties(courseWebVo,courseWebVoOrder);
        return courseWebVoOrder;
    }

返回的实体类

package com.atguigu.commonutils.orderVo;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;

/**
 * 根据id查询课程详细信息的实体类对象
 * 用于封装查询到的数据到这个实体类返回给前端
 */
@Data
public class CourseWebVoOrder implements Serializable {

    private static final long serialVersionUID = 1L;

    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;
}

远程调用的接口


@Component   //交给spring管理
//name="service-vod" 调用注册中心的哪个微服务   fallback=ClintVideoImpl.class  熔断后调用的实现类,执行实现类中的方法

@FeignClient(name="service-edu")
public interface CourseClint {
    //根据课程id查询课程信息
    @ApiOperation("根据课程id查询课程信息")
    @PostMapping("/eduservice/coursefront/selectCourseByIdIsOrder/{id}")
    public CourseWebVoOrder selectCourseByIdIsOrder(@PathVariable("id") String id);
}

获取用户信息


    //根据用户id 查询用户信息
    @ApiOperation("根据用户id 查询用户信息")
    //这里需要进行远程调用,所有前端传过来的是一堆json数据所以用post,对json数据进行拆分请求
    //或者是作为被调用端用post请求更合适,因为调用端是get请求,不能再通过get再次进行请求
    @PostMapping("selectUcenterInfoById/{id}")
    public UcenterMemberOrderVo selectUcenterInfoById(@PathVariable String id){
        UcenterMember ucenterMember = memberService.getById(id);
        UcenterMemberOrderVo ucenterMemberOrderVo = new UcenterMemberOrderVo();
        BeanUtils.copyProperties(ucenterMember,ucenterMemberOrderVo);
        return ucenterMemberOrderVo;
    }

返回的实体类,调用端和被调用端都从这个实体类中拿数据,以免造成数据的不一致性

package com.atguigu.commonutils.orderVo;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.Date;

/**
 * <p>
 * 会员表
 * </p>
 *
 * @author zyfTest
 * @since 2023-01-29
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="UcenterMember对象", description="会员表")
public class UcenterMemberOrderVo 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;

    @TableLogic  //逻辑删除
    @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;


}

远程调用接口


@Component   //交给spring管理
//name="service-vod" 调用注册中心的哪个微服务   fallback=ClintVideoImpl.class  熔断后调用的实现类,执行实现类中的方法

@FeignClient(name="service-ucenter")
public interface UcenterClint {
    //根据用户id 查询用户信息
    @ApiOperation("根据用户id 查询用户信息")
    //这里需要进行远程调用,所有前端传过来的是一堆json数据所以用post,对json数据进行拆分请求
    //或者是作为被调用端用post请求更合适,因为调用端是get请求,不能再通过get再次进行请求
    @PostMapping("/educenter/ucenter/selectUcenterInfoById/{id}")
    public UcenterMemberOrderVo selectUcenterInfoById(@PathVariable("id") String id);
}

远程调用报 404 异常一定要检查远程接口路径是否正确

卡了3个小时

这里的 远程调用为什么使用 post请求

或者也可以理解为,get对应的是 查询 post 新增 delete 删除 update 修改,这里我们需要往数据库新增一条数据,就需要使用post请求了 。

前端api

当地址栏已经存在我们需要的id等数据,就需要考虑发送post请求了,发送psot请求,后端仍可以使用pathVvriable进行接收,但是当前端使用 post 请求发送很多数据 就需要使用@RequestParam进行接收了,当前端给调用者的是post,被调用着等接口和方法都应该与之保持一致。

查询订单

根据订单id查询订单信息

    //根据订单id查询订单信息
    @ApiOperation("根据订单id查询订单信息")
    @GetMapping("getOrderInfo/{id}")
    public R getOrderInfo(@PathVariable String id){
        //不是使用表id而是字段 订单 id 所以不能用 id去查
        //TOrder tOrder = tOrderService.getById(id);
        QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no",id);
        TOrder one = tOrderService.getOne(wrapper);
        return R.ok().data("item",one);
    }

根据订单号生成微信支付二维码

controller

 */
@RestController
@RequestMapping("/eduorder/paylog")
@CrossOrigin
@Api(description = "订单支付管理")
public class TPayLogController {

    @Autowired
    private TPayLogService tPayLogService;

    //根据订单号生成订单二维码
    @ApiOperation("根据订单号生成订单二维码")
    @GetMapping("creatNative/{orderNo}")
    public R saveTwoCode(@PathVariable String orderNo){
        //返回信息有二维码还有其他信息所以使用map进行接收
        Map map = tPayLogService.creatTwoCode(orderNo);
        return R.ok().data(map);
    }

service

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

    @Autowired
    private TOrderService tOrderService;
    @Override
    public Map creatTwoCode(String orderNo) {
        try {
            //1 根据订单号查询订单信息
            QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
            wrapper.eq("order_no",orderNo);
            TOrder order = tOrderService.getOne(wrapper);

            //2 使用map设置生成二维码需要参数
            Map m = new HashMap();
            m.put("appid","wx74862e0dfcf69954"); //关联的公众号appid
            m.put("mch_id", "1558950191"); //商户号
            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", "192.168.0.104"); //本机ip
            m.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n"); //商户key
            m.put("trade_type", "NATIVE");

            //3 发送httpclient请求,传递参数xml格式,微信支付提供的固定的地址
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            //设置xml格式的参数
            client.setXmlParam(WXPayUtil.generateSignedXml(m,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
            client.setHttps(true);
            //执行post请求发送
            client.post();

            //4 得到发送请求返回结果
            //返回内容,是使用xml格式返回
            String xml = client.getContent();

            //把xml格式转换map集合,把map集合返回
            Map<String,String> 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,"生成二维码失败");
        }

    }

根据订单号查询支付状态

controller

   //查询订单支付状态
    //参数:订单号,根据订单号查询 支付状态
    @ApiOperation("查询订单支付状态")
    @GetMapping("queryPayStatus/{orderNo}")
    public R queryPayStatus(@PathVariable String orderNo) {
        Map<String,String> map = tPayLogService.queryPayStatus(orderNo);
        System.out.println("*****查询订单状态map集合:"+map);
        if(map == null) {
            return R.error().message("支付出错了");
        }
        //如果返回map里面不为空,通过map获取订单状态
        if(map.get("trade_state").equals("SUCCESS")) {//支付成功
            //添加记录到支付表,更新订单表订单状态
            tPayLogService.updateOrdersStatus(map);
            return R.ok().message("支付成功");
        }
        return R.ok().code(25000).message("支付中");
    }

service

   //查询订单支付状态
    @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.setXmlParam(WXPayUtil.generateSignedXml(m,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
            client.setHttps(true);
            client.post();

            //3 得到请求返回内容
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            //6、转成Map再返回
            return resultMap;
        }catch(Exception e) {
            return null;
        }
    }

    //添加支付记录和更新订单状态
    @Override
    public void updateOrdersStatus(Map<String, String> map) {
        //从map获取订单号
        String orderNo = map.get("out_trade_no");
        //根据订单号查询订单信息
        QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no",orderNo);
        TOrder order = tOrderService.getOne(wrapper);

        //更新订单表订单状态
        if(order.getStatus().intValue() == 1) { return; }
        order.setStatus(1);//1代表已经支付
        tOrderService.updateById(order);

        //向支付表添加支付记录
        TPayLog payLog = new TPayLog();
        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);
    }

判断用户是否购买该课程


    @ApiOperation("根据课程 id和用户 id判断是否购买了该课程")
    @GetMapping("isBuyCourse/{courseId}/{memberId}")
    public Boolean isBuyCourse(@PathVariable String courseId,@PathVariable String memberId){
        QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("course_id",courseId);
        wrapper.eq("member_id",memberId);
        wrapper.eq("status",1);
        int count = tOrderService.count(wrapper);
        if(count>0){
            return true;
        }else {
            return false;
        }
    }

在前台课程某块远程调用这个方法

远程调用接口

@Component
@FeignClient(name="service-order")
public interface OrdersClint {

    @ApiOperation("根据课程 id和用户 id判断是否购买了该课程")
    @GetMapping("/eduorder/order/isBuyCourse/{courseId}/{memberId}")
    public Boolean isBuyCourse(@PathVariable String courseId, @PathVariable String memberId);
}

注入接口并对调用者进行修改

修改后

    @Autowired
    private OrdersClint ordersClint;

 //根据课程id查询课程详情信息
    @ApiOperation(value = "根据课程id查询课程详情信息")
    @GetMapping("queryCourseById/{id}")
    public R queryCourseById(@PathVariable String id, HttpServletRequest request){
        //根据id查询课程详细信息
        CourseWebVo courseWebVo = eduCourseService.selectCourseById(id);

        //根据id查询课程章节信息
        List<ChapterVo> courseChapter = eduChapterService.getCourseChapter(id);


        String memberIdByJwtToken = JwtUtils.getMemberIdByJwtToken(request);
        //如果获取不到用户的 id 直接判没有购买该课程
        Boolean buyCourse = false;
        if(!StringUtils.isEmpty(memberIdByJwtToken)){
            //根据课程 id和用户 id判断是否购买了该课程
             buyCourse = ordersClint.isBuyCourse(id,memberIdByJwtToken);
        }


        return R.ok().data("courseWebVo",courseWebVo).data("chapterVideoList",courseChapter).data("isBuy",buyCourse);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值