尚医通(二十四)微信退款(取消预约功能)

news2024/11/14 21:00:26

目录

  • 一、取消预约
    • 1、需求描述
    • 2、开发取消预约接口

一、取消预约

1、需求描述

取消订单分两种情况:
(1)未支付取消订单,直接通知医院更新取消预约状态
(2)已支付取消订单,先退款给用户,然后通知医院更新取消预约状态

2、开发取消预约接口

参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
该接口需要使用证书,详情参考文档并下载证书
(1)配置证书
请下载的证书放在service-order模块/resources/cert文件夹下
在这里插入图片描述
记得修改HttpClient里面的方法,改成证书所放的路径
在这里插入图片描述

(2)添加退款记录
添加RefundInfoMapper

public interface RefundInfoMapper extends BaseMapper<RefundInfo> {
}

添加RefundInfoService方法和实现

//RefundInfoService
public interface RefundInfoService extends IService<RefundInfo> {
    RefundInfo saveRefundInfo(PaymentInfo paymentInfo);
}

//RefundInfoServiceImpl
@Service
public class RefundInfoServiceImpl  extends ServiceImpl<RefundInfoMapper, RefundInfo> implements RefundInfoService {

    @Override
    public RefundInfo saveRefundInfo(PaymentInfo paymentInfo) {
        Long orderId = paymentInfo.getOrderId();
        QueryWrapper<RefundInfo> queryWrapper=new QueryWrapper<RefundInfo>();
        queryWrapper.eq("order_id",orderId);
        RefundInfo refundInfo1 = baseMapper.selectOne(queryWrapper);
        if(refundInfo1 != null){
            return refundInfo1;
        }

        RefundInfo refundInfo=new RefundInfo();
        refundInfo.setOutTradeNo(paymentInfo.getOutTradeNo());
        refundInfo.setOrderId(paymentInfo.getOrderId());
        refundInfo.setPaymentType(PaymentTypeEnum.WEIXIN.getStatus());
        refundInfo.setTotalAmount(paymentInfo.getTotalAmount());
        refundInfo.setSubject("想退款...");
        refundInfo.setRefundStatus(RefundStatusEnum.UNREFUND.getStatus());
        baseMapper.insert(refundInfo);
        return refundInfo;
    }
}

(3)添加微信退款方法
在WeixinService添加方法

    /***
     * 退款
     * @param orderId
     * @return
     */
    boolean refund(Long orderId);

在WeixinServiceImpl添加实现

    @Override
    public boolean refund(Long orderId) {

        QueryWrapper<PaymentInfo> queryWrapper=new QueryWrapper<PaymentInfo>();
        queryWrapper.eq("order_id",orderId);
        PaymentInfo paymentInfo = paymentService.getOne(queryWrapper);
        RefundInfo refundInfo=refundInfoService.saveRefundInfo(paymentInfo);
        //已退款
        if(refundInfo.getRefundStatus().intValue() == RefundStatusEnum.REFUND.getStatus().intValue()){
            return true;
        }
        //执行微信退款
        HttpClient httpClient=new HttpClient("https://api.mch.weixin.qq.com/secapi/pay/refund");

        Map<String,String> paramMap = new HashMap<>(8);

        paramMap.put("appid",weiPayProperties.getAppid());       //公众账号ID
        paramMap.put("mch_id",weiPayProperties.getPartner());   //商户编号
        paramMap.put("nonce_str",WXPayUtil.generateNonceStr());
        paramMap.put("transaction_id",paymentInfo.getTradeNo()); //微信支付订单号

        paramMap.put("out_trade_no",paymentInfo.getOutTradeNo()); //商户订单编号
        paramMap.put("out_refund_no","tk"+paymentInfo.getOutTradeNo()); //商户退款单号
        //       paramMap.put("total_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
        //       paramMap.put("refund_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");

        paramMap.put("total_fee","1");
        paramMap.put("refund_fee","1");
        try {
            String paramXml = WXPayUtil.generateSignedXml(paramMap,weiPayProperties.getPartnerkey());
            httpClient.setXmlParam(paramXml);
            httpClient.setHttps(true);
            httpClient.setCert(true); //设置证书支持
            httpClient.setCertPassword(weiPayProperties.getPartner()); //设置证书密码
            httpClient.post();

            String content = httpClient.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(content);
            if("SUCCESS".equals(resultMap.get("result_code"))){ //微信退款成功
                refundInfo.setTradeNo(resultMap.get("refund_id"));//微信退款交易号
                refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                refundInfo.setCallbackTime(new Date());
                refundInfo.setCallbackContent(JSONObject.toJSONString(resultMap));
                refundInfoService.updateById(refundInfo);
                return true;

            }
            return  false;

        }catch (Exception ex){
            ex.printStackTrace();
        }

        return false;
    }

(4)完成取消预约方法
在OrderService添加和实现

    /**
     * 取消订单
     * @param orderId
     */
    void cancelOrder(Long orderId);

   //实现方法
    @Override
    public void cancelOrder(Long orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        DateTime quitTime = new DateTime(orderInfo.getQuitTime());
        //1.确定当前取消预约的时间 和 挂号订单的取消预约截止时间 对比, 当前时间是否已经超过了 挂号订单的取消预约截止时间
        //1.1 如果超过了,直接抛出异常,不让用户取消
        if(quitTime.isBeforeNow()){
            throw  new YyghException(20001,"超过了退号的截止时间");
        }

        Map<String,Object>  hospitalParamMap=new HashMap<String,Object>();
        hospitalParamMap.put("hoscode",orderInfo.getHoscode());
        hospitalParamMap.put("hosRecordId",orderInfo.getHosRecordId());


        //2.从平台请求第三方医院,通知第三方医院,该用户已取消
        JSONObject jsonObject = HttpRequestHelper.sendRequest(hospitalParamMap, "http://localhost:9998/order/updateCancelStatus");
        //2.1 第三方医院如果不同意取消:抛出异常,不能取消
        if(jsonObject == null || jsonObject.getIntValue("code") != 200){
            throw  new YyghException(20001,"取消失败");
        }
        //3.判断用户是否对当前挂号订单是否已支付
        if(orderInfo.getOrderStatus() == OrderStatusEnum.PAID.getStatus()){
            //3.1.如果已支付,退款
            boolean flag= weixinService.refund(orderId);
            if(!flag){
                throw new YyghException(20001,"退款失败");
            }
        }

        //无论用户是否进了支付

        //4.更新订单的订单状态 及 支付记录表的支付状态
        orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
        baseMapper.updateById(orderInfo);

        UpdateWrapper<PaymentInfo> updateWrapper=new UpdateWrapper<PaymentInfo>();
        updateWrapper.eq("order_id",orderInfo.getId());
        updateWrapper.set("payment_status", PaymentStatusEnum.REFUND.getStatus());
        paymentService.update(updateWrapper);

        //5.更新医生的剩余可预约数信息

        OrderMqVo orderMqVo=new OrderMqVo();
        orderMqVo.setScheduleId(orderInfo.getScheduleId());
        SmsVo msmVo=new SmsVo();
        msmVo.setPhone(orderInfo.getPatientPhone());
        msmVo.setTemplateCode("xxxx.....");
        msmVo.setParam(null);
        orderMqVo.setSmsVo(msmVo);
        //6.给就诊人发送短信提示:
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,orderMqVo);
    }

(5)在WeixinService添加方法
在OrderInfoController添加方法

    @GetMapping("/cancel/{orderId}")
    public R cancelOrder(@PathVariable Long orderId){
        orderInfoService.cancelOrder(orderId);
        return R.ok();
    }

(6)修改监听器方法
service-hosp
修改OrderMqListener类

@Component
public class OrderMqListener {

    @Autowired
    private ScheduleService scheduleService;
    @Autowired
    private RabbitService rabbitService;

    @RabbitListener(bindings = {
            @QueueBinding(
                    value =@Queue(name = MqConst.QUEUE_ORDER,durable = "true"),//创建队列
                    exchange = @Exchange(name = MqConst.EXCHANGE_DIRECT_ORDER), //创建交换机
                    key=MqConst.ROUTING_ORDER
            )
    })
    //确认挂号:走该方法 -n
    //取消预约:走方法 : +1
    public void consume(OrderMqVo orderMqVo, Message message, Channel channel){
        String scheduleId = orderMqVo.getScheduleId();
        Integer availableNumber = orderMqVo.getAvailableNumber();
        SmsVo msmVo = orderMqVo.getSmsVo();
        if(availableNumber != null){
            boolean flag= scheduleService.updateAvailableNumber(scheduleId,availableNumber);

        }else{
            scheduleService.cancelSchedule(scheduleId);
        }

        if(msmVo != null){
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_SMS,MqConst.ROUTING_SMS_ITEM,msmVo);
        }


    }
}

取消预约了,把可预约人数加回去

    void cancelSchedule(String scheduleId);

    @Override
    public void cancelSchedule(String scheduleId) {
        Schedule schedule=scheduleRepository.findByHosScheduleId(scheduleId);
        schedule.setAvailableNumber(schedule.getAvailableNumber()+1);
        scheduleRepository.save(schedule);
    }
@Repository
public interface ScheduleRepository extends MongoRepository<Schedule,String> {
    Schedule findByHosScheduleId(String scheduleId);
}

3、开发微信退款前端
(1)添加wx.js添加方法

cancelOrder(orderId) {
  return request({
      url: `/api/order/orderInfo/cancel/${orderId}`,
      method: 'get'
  })
},

(2)修改show.vue组件

      //取消预约方法
      cancelOrder() {
        this.$confirm('确定取消预约吗, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          wxApi.cancelOrder(this.orderId).then(resp=>{
            this.$message.success("取消成功")
            this.init()
          })
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消操作'
          });          
        });
      },


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《程序是如何跑起来的》-----读书笔记篇

程序是如何跑起来的前言磁盘与内存的关系虚拟内存dll 文件运行环境从源文件到可执行文件前言 不得不说&#xff0c;在这个假期借助“微信阅读”读到了很多有意义的书。不仅是思想境界上的&#xff0c;还有专业方向的。这一次我是在周三还是周四的一个活动中淘到了这本书&#x…

Nginx的介绍、安装与常用命令

前言&#xff1a;传统结构上(如下图所示)我们只会部署一台服务器用来跑服务&#xff0c;在并发量小&#xff0c;用户访问少的情况下基本够用但随着用户访问的越来越多&#xff0c;并发量慢慢增多了&#xff0c;这时候一台服务器已经不能满足我们了&#xff0c;需要我们增加服务…

随机过程及应用

随机过程及应用一、概率论基础1. 三元体定义2. 随机变量及其分布1. 离散随机变量2. 连续型随机变量3. 常见的随机变量和分布1. 离散类2. 连续类4. 二维连续随机变量1. 二维离散2. 二维连续5. 随机变量函数的分布1. 离散&#xff08;可浅看&#xff09;2. 一维连续 r.v 函数分布…

JS学习第3天——Web APIs之DOM(什么是DOM,相关API【创建、增删改查、属性操作、事件操作API】)

目录一、Web APIs介绍1、API2、Web API二、DOM1、DOM树2、获取元素3、事件基础4、操作元素属性5、节点&#xff08;node&#xff09;操作三、DOM操作总结&#xff08;创建、增删改查、属性操作、事件操作API&#xff09;1、创建2、增3、删4、改5、查6、属性操作7、事件操作四、…

[QCustomPlot] QCPBar绘制柱状图并同时显示柱状图的值

前言 博主在使用 qcustomplot 绘制柱状图时&#xff0c;发现网络上的教程。大都忽略了一个重要的点就是。柱状图没有明显显示出当前的值。经过博主寻找后发现在 qcustomplot 论坛中已经有了对应的解决方案。所以记录一下。qcustomplot论坛 代码 void MainWindow::drawBars()…

【N32WB03x SDK使用指南】

【N32WB03x SDK使用指南】1. 简介1.1 产品简介1.2 主要资源1.3 典型应用2. SDK/开发固件文件目录结构2.1 doc2.2 firmware2.3 middleware2.4 utilities2.5 projects Projects3. 项目配置与烧录3.1 编译环境安装3.2 固件支持包安装3.3 编译环境配置3.4 编译与下载3.5 BLE工程目录…

Vue.js基础特性、生命周期及常用指令

目录 一、Vue构造选项 el根标签 data数据对象 methods定义方法 二、生命周期 三、常用指令 一、Vue构造选项 选项说明el唯一根标签&#xff0c;决定Vue实例会管理哪一个DOM节点dataVue实例对应的数据对象methods定义Vue实例的方法&#xff0c;可以在其他地方调用&#x…

慢SQL出现原因、优化、开启慢查询日志

文章目录慢SQL:出现原因&#xff1a;解决方式&#xff1a;开启慢查询日志&#xff1a;慢SQL: 出现原因&#xff1a; &#xff08;1&#xff09;数据库表索引设置不合理 &#xff08;2&#xff09;SQL语句有问题&#xff0c;需要优化 解决方式&#xff1a; &#xff08;1&am…

Java 抽象类和接口

文章目录一、抽象类1. 抽象类定义2. 抽象类成员特点二、接口1. 接口概述2. 接口成员特点3. 类和接口的关系4. 抽象类和接口的区别5. 接口案例三、形参和返回值一、抽象类 1. 抽象类定义 在 Java 中&#xff0c;一个没有方法体的方法应该定义为抽象方法&#xff0c;而类中如果…

现在转行做程序员的多吗?

曾经有一名程序员说&#xff0c;他在编写程序时&#xff0c;就像一个发明家在做实验&#xff1b;当他把程序编好可以运行时&#xff0c;他就已经是个发明家了。 程序员作为众多转行人员首选的职业&#xff0c;也是被大众熟知了。但我们需要知道的不仅是它作为一个职业的定义&a…

Spring超级全家桶,学完绝对是惊艳面试官的程度

前言Spring框架自2002年诞生以来一直备受开发者青睐&#xff0c;它包括SpringMVC、SpringBoot、Spring Cloud、Spring Cloud Dataflow等解决方案。有人亲切的称之为&#xff1a;Spring 全家桶。很多研发人员把spring看作心目中最好的java项目&#xff0c;没有之一。所以这是重点…

AtCoder Beginner Contest 290 G. Edge Elimination(思维题 枚举+贪心)

题目 T(T<100)组样例&#xff0c;每次给出一棵深度为d的k叉树&#xff0c; 其中&#xff0c;第i层深的节点个数为 保证k叉树的所有节点个数tot不超过1e18&#xff0c; 求在k叉树上构建一棵大小恰为x的连通块&#xff0c;所需要断开的最少的树边的条数(x<tot<1e18)…

VScode远程连接服务器-过程试图写入的管道不存在-could not establist connection to【已解决】

问题描述 使用服务器的过程中突然与服务器断连&#xff0c;报错如下&#xff1a;could not establist connection to [20:23:39.487] > ssh: connect to host 10.201.0.131 port 22: Connection timed out > [20:23:39.495] > 过程试图写入的管道不存在。 > [20…

hook与mixin

看完vue3就开始看vue3的源码&#xff0c;表示很懵~ 刚把rollup打包搞完&#xff0c;这不响应式就接着来了&#xff01;&#xff0c;还是写篇直接使用vue3的博客清清脑吧&#xff01; 什么是hook、mixin&#xff1f; mixin: Vue2中多个组件内存在重复JS业务逻辑&#xff0c;使…

JavaScript 闭包【自留】

闭包的概念理解 闭包的定义 ✅ 这里先来看一下闭包的定义&#xff0c;分成两个:在计算机科学中和在JavaScript中。 ✅ 在计算机科学中对闭包的定义(维基百科): 闭包(英语:Closure)&#xff0c;又称词法闭包(Lexical Closure)或函数闭包(function closures);是在支持头等函数…

QT之OpenGL模板测试

QT之OpenGL模板测试1. 概述2. 使用步骤及函数介绍3. Demo4. 参考1. 概述 当片段着色器处理完一个片段之后&#xff0c;模板测试(Stencil Test)会开始执行&#xff0c;和深度测试一样&#xff0c;它可能会丢弃片段。被保留下来的片段会进入深度测试。 一个模板缓冲中&#xff…

Web Spider Babel安装 Ast抽象语法 - 基本使用

文章目录一、资源地址二、遍历2.1 树结构遍历模式2.2 案例三、下载安装四、案例操作总结提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、资源地址 Ast反混淆语法在线网址&#xff1a;https://astexplorer.net Babel官方文档&#xff1a;https://ww…

玩转Linux内核进程调度,这一篇就够(所有的知识点)

一&#xff0c;进程的分类 在 CPU 的角度看进程行为的话&#xff0c;可以分为两类&#xff1a; CPU 消耗型&#xff1a;此类进程就是一直占用 CPU 计算&#xff0c;CPU 利用率很高IO 消耗型&#xff1a;此类进程会涉及到 IO&#xff0c;需要和用户交互&#xff0c;比如键盘输…

性能测试流程

性能测试实战一.资源指标分析1.判断CPU是否瓶颈的方法2.判断内存是否瓶颈的方法3.判断磁盘I/O是否瓶颈的方法4.判断网络带宽是否是瓶颈的方法二.系统指标分析三.性能调优四.性能测试案例1.项目背景2.实施规划&#xff08;1&#xff09;需求分析&#xff08;2&#xff09;测试方…

Faster-Rcnn修改转数据集文件

目录 学习python的一些基础知识 argparser assert关键字 让你秒懂Python 类特殊方法__getitem__ lxml.etree.fromstring的使用 统计一下json文件内的种类 正脸红外光 正脸-混合红外光 正脸-交叉偏振光 正脸-平行偏振光 正脸-紫外光 正脸-棕色光 调用mydataset可视化…