• 10.java项目-尚医通(10)


    前端代码:https://github.com/wyj41/yygh_html.git
    后端代码:https://github.com/wyj41/yygh_parent.git

    1.用户订单管理

    1.1 订单详情

    1.1.1 api接口

    操作模块:service_order

    1.添加service接口及实现类
    在OrderService类添加接口

    //根据订单id查询订单详情
    OrderInfo getOrder(String orderId);
    
    • 1
    • 2

    在OrderServiceImpl类添加接口实现

    @Override
    public OrderInfo getOrder(String orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
    
        return this.packOrderInfo(orderInfo);
    }
    private OrderInfo packOrderInfo(OrderInfo orderInfo){
        orderInfo.getParam().put("orderStatusString",OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus()));
        return orderInfo;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.添加controller
    在OrderApiController类添加方法

    //根据订单id查询订单详情
    @GetMapping("auth/getOrders/{orderId}")
    public Result getOrder(@PathVariable String orderId){
        OrderInfo orderInfo = orderService.getOrder(orderId);
        return Result.ok(orderInfo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.1.2 前端

    操作:yygh-site

    1.封装api请求
    在/api/orderInfo.js添加方法

    //订单详情
    getOrders(orderId){
        return request({
          url: `${api_name}/auth/getOrders/${orderId}`,
          method: 'get'
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.页面展示
    创建/pages/order/show.vue组件

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170

    1.2 订单列表

    操作模块:service_order

    1.2.1 api接口

    1.添加service接口及实现类
    在OrderService类添加接口

    //订单列表(条件查询带分页)
    IPage<OrderInfo> selectPage(Page<OrderInfo> pageParam, OrderQueryVo orderQueryVo);
    
    • 1
    • 2

    在OrderServiceImpl类添加接口实现

    //订单列表(条件查询带分页)
    @Override
    public IPage<OrderInfo> selectPage(Page<OrderInfo> pageParam, OrderQueryVo orderQueryVo) {
        //orderQueryVo获取条件
        String name = orderQueryVo.getKeyword(); //医院名称
        Long patientId = orderQueryVo.getPatientId(); //就诊人id
        String orderStatus = orderQueryVo.getOrderStatus(); //订单状态
        String reserveDate = orderQueryVo.getReserveDate();//安排时间
        String createTimeBegin = orderQueryVo.getCreateTimeBegin();
        String createTimeEnd = orderQueryVo.getCreateTimeEnd();
    
        //对条件值进行非空判断
        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",orderQueryVo.getUserId());
        if(!StringUtils.isEmpty(name)) {
            wrapper.like("hosname",name);
        }
        if(!StringUtils.isEmpty(patientId)) {
            wrapper.eq("patient_id",patientId);
        }
        if(!StringUtils.isEmpty(orderStatus)) {
            wrapper.eq("order_status",orderStatus);
        }
        if(!StringUtils.isEmpty(reserveDate)) {
            wrapper.le("reserve_date",reserveDate);
        }
        if(!StringUtils.isEmpty(createTimeBegin)) {
            wrapper.ge("create_time",createTimeBegin);
        }
        if(!StringUtils.isEmpty(createTimeEnd)) {
            wrapper.le("create_time",createTimeEnd);
        }
        //调用mapper的方法
        Page<OrderInfo> pages = baseMapper.selectPage(pageParam, wrapper);
        //编号变成对应封装
        pages.getRecords().stream().forEach(item -> {
            this.packOrderInfo(item);
        });
    
        return pages;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    2.添加controller
    在OrderApiController类添加方法

    //订单列表(条件查询带分页)
    @GetMapping("auth/{page}/{limit}")
    public Result list(@PathVariable Long page,
                       @PathVariable Long limit,
                       OrderQueryVo orderQueryVo,
                       HttpServletRequest request){
        //设置当前用户id
        orderQueryVo.setUserId(AuthContextHolder.getUserId(request));
        Page<OrderInfo> pageParam = new Page<>(page,limit);
        IPage<OrderInfo> pageModel = orderService.selectPage(pageParam,orderQueryVo);
        return Result.ok(pageModel);
    }
    
    @ApiOperation(value = "获取订单状态")
    @GetMapping("auth/getStatusList")
    public Result getStatusList() {
        return Result.ok(OrderStatusEnum.getStatusList());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.2.2 前端

    1.封装api请求
    在/api/orderInfo.js添加方法

    //订单列表
    getPageList(page,limit,searchObj){
        return request({
          url: `${api_name}/auth/${page}/${limit}`,
          method: 'get',
          params: searchObj
        })
    },
    //查询订单状态
    getStatusList() {
        return request({
          url: `${api_name}/auth/getStatusList`,
          method: 'get'
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.页面展示
    创建/pages/order/index.vue组件

    <template>
      <!-- header -->
      <div class="nav-container page-component">
        <!--左侧导航 #start -->
        <div class="nav left-nav">
          <div class="nav-item ">
            <span class="v-link clickable dark" onclick="javascript:window.location='/user'">实名认证 </span>
          </div>
          <div class="nav-item selected">
            <span class="v-link selected dark" onclick="javascript:window.location='/order'"> 挂号订单 </span>
          </div>
          <div class="nav-item ">
            <span class="v-link clickable dark" onclick="javascript:window.location='/patient'"> 就诊人管理 </span>
          </div>
          <div class="nav-item ">
            <span class="v-link clickable dark"> 修改账号信息 </span>
          </div>
          <div class="nav-item ">
            <span class="v-link clickable dark"> 意见反馈 </span>
          </div>
        </div>
        <!-- 左侧导航 #end -->
        <!-- 右侧内容 #start -->
        <div class="page-container">
          <div class="personal-order">
            <div class="title"> 挂号订单</div>
            <el-form :inline="true">
              <el-form-item label="就诊人:">
                <el-select v-model="searchObj.patientId" placeholder="请选择就诊人" class="v-select patient-select">
                  <el-option
                    v-for="item in patientList"
                    :key="item.id"
                    :label="item.name + '【' + item.certificatesNo + '】'"
                    :value="item.id">
                  </el-option>
                </el-select>
              </el-form-item>
              <el-form-item label="订单状态:" style="margin-left: 80px">
                <el-select v-model="searchObj.orderStatus" placeholder="全部" class="v-select patient-select" style="width: 200px;">
                  <el-option
                    v-for="item in statusList"
                    :key="item.status"
                    :label="item.comment"
                    :value="item.status">
                  </el-option>
                </el-select>
              </el-form-item>
              <el-form-item>
                <el-button type="text" class="search-button v-link highlight clickable selected" @click="fetchData()">
                  查询
                </el-button>
              </el-form-item>
            </el-form>
            <div class="table-wrapper table">
              <el-table
                :data="list"
                stripe
                style="width: 100%">
                <el-table-column
                  label="就诊时间"
                  width="120">
                  <template slot-scope="scope">
                    {{ scope.row.reserveDate }} {{ scope.row.reserveTime === 0 ? '上午' : '下午' }}
                  </template>
                </el-table-column>
                <el-table-column
                  prop="hosname"
                  label="医院"
                  width="100">
                </el-table-column>
                <el-table-column
                  prop="depname"
                  label="科室">
                </el-table-column>
                <el-table-column
                  prop="title"
                  label="医生">
                </el-table-column>
                <el-table-column
                  prop="amount"
                  label="医事服务费">
                </el-table-column>
                <el-table-column
                  prop="patientName"
                  label="就诊人">
                </el-table-column>
                <el-table-column
                  prop="param.orderStatusString"
                  label="订单状态">
                </el-table-column>
                <el-table-column label="操作">
                  <template slot-scope="scope">
                    <el-button type="text" class="v-link highlight clickable selected" @click="show(scope.row.id)">详情</el-button>
                  </template>
                </el-table-column>
              </el-table>
            </div>
            <!-- 分页 -->
            <el-pagination
              class="pagination"
              layout="prev, pager, next"
              :current-page="page"
              :total="total"
              :page-size="limit"
              @current-change="fetchData">
            </el-pagination>
          </div>
        </div>
        <!-- 右侧内容 #end -->
      </div>
      <!-- footer -->
    </template>
    <script>
    import '~/assets/css/hospital_personal.css'
    import '~/assets/css/hospital.css'
    import orderInfoApi from '@/api/orderInfo'
    import patientApi from '@/api/patient'
    export default {
      data() {
        return {
          list: [], // banner列表
          total: 0, // 数据库中的总记录数
          page: 1, // 默认页码
          limit: 10, // 每页记录数
          searchObj: {}, // 查询表单对象
          patientList: [],
          statusList: []
        }
      },
      created() {
        this.orderId = this.$route.query.orderId
        this.fetchData()
        this.findPatientList()
        this.getStatusList()
      },
      methods: {
        fetchData(page = 1) {
          this.page = page
          orderInfoApi.getPageList(this.page, this.limit, this.searchObj).then(response => {
            console.log(response.data);
            this.list = response.data.records
            this.total = response.data.total
          })
        },
        findPatientList() {
          patientApi.findList().then(response => {
            this.patientList = response.data
          })
        },
        getStatusList() {
          orderInfoApi.getStatusList().then(response => {
            this.statusList = response.data
          })
        },
        changeSize(size) {
          console.log(size)
          this.limit = size
          this.fetchData(1)
        },
        show(id) {
          window.location.href = '/order/show?orderId=' + id
        }
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165

    注:平台订单管理和用户订单管理接口基本一样,因此不再操作

    2.微信支付

    2.1 微信支付简介

    微信扫码支付申请

    微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。

    申请步骤:(了解)
    第一步:注册公众号(类型须为:服务号)
    请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。

    第二步:认证公众号
    公众号认证后才可申请微信支付,认证费:300元/年。

    第三步:提交资料申请微信支付
    登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。

    第四步:开户成功,登录商户平台进行验证
    资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。

    第五步:在线签署协议
    本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。

    开发文档
    微信支付接口调用的整体思路:
    按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。

    在线微信支付开发文档:
    https://pay.weixin.qq.com/wiki/doc/api/index.html

    1. appid:微信公众账号或开放平台APP的唯一标识
    2. mch_id:商户号 (配置文件中的partner)
    3. partnerkey:商户密钥
    4. sign:数字签名, 根据微信官方提供的密钥和一套算法生成的一个加密信息, 就是为了保证交易的安全性

    2.2 微信支付开发

    2.2.1 api接口

    场景:用户扫描商户展示在各种场景的二维码进行支付
    使用案例:
    线下:家乐福超市、7-11便利店、上品折扣线下店等
    线上:大众点评网站、携程网站、唯品会、美丽说网站等

    开发模式:
    模式一:商户在后台给你生成二维码,用户打开扫一扫
    模式二:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。
    微信支付:生成xml发送请求

    操作模块:service-order

    1.引入依赖

    <dependency>
        <groupId>com.github.wxpaygroupId>
        <artifactId>wxpay-sdkartifactId>
        <version>0.0.3version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.添加配置
    在application.yml中添加商户信息

    spring:
      redis:
        timeout: 1800000
        lettuce:
          pool:
            max-active: 20
            max-wait: -1
            max-idle: 5
            min-idle: 0
    weixin:
      appid: wx74862e0dfcf69954
      partner: 1558950191
      partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.引入工具类
    com.myproject.yygh.order.utils.ConstantPropertiesUtils

    package com.myproject.yygh.order.utils;
    
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ConstantPropertiesUtils implements InitializingBean {
    
        @Value("${weixin.appid}")
        private String appid;
    
        @Value("${weixin.partner}")
        private String partner;
    
        @Value("${weixin.partnerkey}")
        private String partnerkey;
    
        public static String APPID;
        public static String PARTNER;
        public static String PARTNERKEY;
        @Override
        public void afterPropertiesSet() throws Exception {
            APPID = appid;
            PARTNER = partner;
            PARTNERKEY = partnerkey;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    com.myproject.yygh.order.utils.HttpClient

    package com.myproject.yygh.order.utils;
    
    import org.apache.http.Consts;
    import org.apache.http.HttpEntity;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.*;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLContextBuilder;
    import org.apache.http.conn.ssl.TrustStrategy;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.ssl.SSLContexts;
    import org.apache.http.util.EntityUtils;
    
    import javax.net.ssl.SSLContext;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.security.KeyStore;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.text.ParseException;
    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    
    /**
     * http请求客户端
     */
    public class HttpClient {
        private String url;
        private Map<String, String> param;
        private int statusCode;
        private String content;
        private String xmlParam;
        private boolean isHttps;
        private boolean isCert = false;
        //证书密码 微信商户号(mch_id)
        private String certPassword;
        public boolean isHttps() {
            return isHttps;
        }
        public void setHttps(boolean isHttps) {
            this.isHttps = isHttps;
        }
        public boolean isCert() {
            return isCert;
        }
        public void setCert(boolean cert) {
            isCert = cert;
        }
        public String getXmlParam() {
            return xmlParam;
        }
        public void setXmlParam(String xmlParam) {
            this.xmlParam = xmlParam;
        }
        public HttpClient(String url, Map<String, String> param) {
            this.url = url;
            this.param = param;
        }
        public HttpClient(String url) {
            this.url = url;
        }
        public String getCertPassword() {
            return certPassword;
        }
        public void setCertPassword(String certPassword) {
            this.certPassword = certPassword;
        }
        public void setParameter(Map<String, String> map) {
            param = map;
        }
        public void addParameter(String key, String value) {
            if (param == null)
                param = new HashMap<String, String>();
            param.put(key, value);
        }
        public void post() throws ClientProtocolException, IOException {
            HttpPost http = new HttpPost(url);
            setEntity(http);
            execute(http);
        }
        public void put() throws ClientProtocolException, IOException {
            HttpPut http = new HttpPut(url);
            setEntity(http);
            execute(http);
        }
        public void get() throws ClientProtocolException, IOException {
            if (param != null) {
                StringBuilder url = new StringBuilder(this.url);
                boolean isFirst = true;
                for (String key : param.keySet()) {
                    if (isFirst)
                        url.append("?");
                    else
                        url.append("&");
                    url.append(key).append("=").append(param.get(key));
                }
                this.url = url.toString();
            }
            HttpGet http = new HttpGet(url);
            execute(http);
        }
        /**
         * set http post,put param
         */
        private void setEntity(HttpEntityEnclosingRequestBase http) {
            if (param != null) {
                List<NameValuePair> nvps = new LinkedList<NameValuePair>();
                for (String key : param.keySet())
                    nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
                http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
            }
            if (xmlParam != null) {
                http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
            }
        }
        private void execute(HttpUriRequest http) throws ClientProtocolException,
                IOException {
            CloseableHttpClient httpClient = null;
            try {
                if (isHttps) {
                    if(isCert) {
                        //TODO 需要完善
                        FileInputStream inputStream = new FileInputStream(new File(""));
                        KeyStore keystore = KeyStore.getInstance("PKCS12");
                        char[] partnerId2charArray = certPassword.toCharArray();
                        keystore.load(inputStream, partnerId2charArray);
                        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();
                        SSLConnectionSocketFactory sslsf =
                                new SSLConnectionSocketFactory(sslContext,
                                        new String[] { "TLSv1" },
                                        null,
                                        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
                        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
                    } else {
                        SSLContext sslContext = new SSLContextBuilder()
                                .loadTrustMaterial(null, new TrustStrategy() {
                                    // 信任所有
                                    public boolean isTrusted(X509Certificate[] chain,
                                                             String authType)
                                            throws CertificateException {
                                        return true;
                                    }
                                }).build();
                        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                                sslContext);
                        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
                                .build();
                    }
                } else {
                    httpClient = HttpClients.createDefault();
                }
                CloseableHttpResponse response = httpClient.execute(http);
                try {
                    if (response != null) {
                        if (response.getStatusLine() != null)
                            statusCode = response.getStatusLine().getStatusCode();
                        HttpEntity entity = response.getEntity();
                        // 响应内容
                        content = EntityUtils.toString(entity, Consts.UTF_8);
                    }
                } finally {
                    response.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                httpClient.close();
            }
        }
        public int getStatusCode() {
            return statusCode;
        }
        public String getContent() throws ParseException, IOException {
            return content;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184

    2.2.2 添加交易记录接口

    1.添加Mapper
    com.myproject.yygh.order.mapper.PaymentMapper

    package com.myproject.yygh.order.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.myproject.yygh.model.order.PaymentInfo;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface PaymentMapper extends BaseMapper<PaymentInfo> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.添加service接口与实现
    com.myproject.yygh.order.service.PaymentService

    package com.myproject.yygh.order.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.myproject.yygh.model.order.OrderInfo;
    import com.myproject.yygh.model.order.PaymentInfo;
    
    public interface PaymentService extends IService<PaymentInfo> {
        //向支付记录表添加信息
        void savePaymentInfo(OrderInfo orderInfo, Integer status);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    com.myproject.yygh.order.service.impl.PaymentServiceImpl

    package com.myproject.yygh.order.service.impl;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.myproject.yygh.enums.PaymentStatusEnum;
    import com.myproject.yygh.model.order.OrderInfo;
    import com.myproject.yygh.model.order.PaymentInfo;
    import com.myproject.yygh.order.mapper.PaymentMapper;
    import com.myproject.yygh.order.service.PaymentService;
    import org.joda.time.DateTime;
    import org.springframework.stereotype.Service;
    
    import java.util.Date;
    
    @Service
    public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, PaymentInfo> implements PaymentService {
        //向支付记录表添加信息
        @Override
        public void savePaymentInfo(OrderInfo orderInfo, Integer paymentType) {
            //根据订单id和支付类型,查询支付记录表是否存在相同订单
            QueryWrapper<PaymentInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("order_id",orderInfo.getId());
            wrapper.eq("payment_type",paymentType);
            Long count = baseMapper.selectCount(wrapper);
            if(count > 0){
                return;
            }
            //添加记录
            PaymentInfo paymentInfo = new PaymentInfo();
            paymentInfo.setCreateTime(new Date());
            paymentInfo.setOrderId(orderInfo.getId());
            paymentInfo.setPaymentType(paymentType);
            paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());
            paymentInfo.setPaymentStatus(PaymentStatusEnum.UNPAID.getStatus());
            String subject = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd")+"|"+orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle();
            paymentInfo.setSubject(subject);
            paymentInfo.setTotalAmount(orderInfo.getAmount());
            baseMapper.insert(paymentInfo);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    *3.添加支付service接口与实现
    com.myproject.yygh.order.service.WeixinService

    package com.myproject.yygh.order.service;
    
    import java.util.Map;
    
    public interface WeixinService {
        //生成微信支付扫描的二维码
        Map createNative(Long orderId);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    com.myproject.yygh.order.service.impl.WeixinServiceImpl

    package com.myproject.yygh.order.service.impl;
    
    import com.github.wxpay.sdk.WXPayUtil;
    import com.myproject.yygh.enums.PaymentTypeEnum;
    import com.myproject.yygh.model.order.OrderInfo;
    import com.myproject.yygh.order.service.OrderService;
    import com.myproject.yygh.order.service.PaymentService;
    import com.myproject.yygh.order.service.WeixinService;
    import com.myproject.yygh.order.utils.ConstantPropertiesUtils;
    import com.myproject.yygh.order.utils.HttpClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    
    @Service
    public class WeixinServiceImpl implements WeixinService {
        @Autowired
        private OrderService orderService;
    
        @Autowired
        private PaymentService paymentService;
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        //生成微信支付扫描的二维码
        @Override
        public Map createNative(Long orderId) {
            try {
                //从redis获取数据
                Map payMap = (Map) redisTemplate.opsForValue().get(orderId.toString());
                if(payMap != null){
                    return payMap;
                }
    
                //1.根据orderId获取订单信息
                OrderInfo orderInfo = orderService.getById(orderId);
                //2.向支付记录表添加信息
                paymentService.savePaymentInfo(orderInfo, PaymentTypeEnum.WEIXIN.getStatus());
    
                //3.设置参数
                //吧参数转换xml格式,使用商户key进行加密
                Map paramMap = new HashMap<>();
                paramMap.put("appid", ConstantPropertiesUtils.APPID);
                paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);
                paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
                String body = orderInfo.getReserveDate() + "就诊"+ orderInfo.getDepname();
                paramMap.put("body", body);
                paramMap.put("out_trade_no", orderInfo.getOutTradeNo());
                //paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");
                paramMap.put("total_fee", "1");//为了测试,统一携程这个值
                paramMap.put("spbill_create_ip", "127.0.0.1");
                paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
                paramMap.put("trade_type", "NATIVE");
    
                //4.调用微信生成二维码接口,httpclient调用
                HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
                //设置map参数
                client.setXmlParam(WXPayUtil.generateSignedXml(paramMap,ConstantPropertiesUtils.PARTNERKEY));
                client.setHttps(true);
                client.post();
    
                //6.返回相关数据
                String xml = client.getContent();
                //转换成map集合
                Map<String,String> resultMap = WXPayUtil.xmlToMap(xml);
                System.out.println("resultMap:" + resultMap);
    
                //4、封装返回结果集
                Map map = new HashMap<>();
                map.put("orderId", orderId);
                map.put("totalFee", orderInfo.getAmount());
                map.put("resultCode", resultMap.get("result_code"));
                map.put("codeUrl", resultMap.get("code_url"));//二维码地址
    
                //微信支付二维码2小时过期,可采取2小时未支付取消订单
                if(resultMap.get("resultCode") != null){
                    redisTemplate.opsForValue().set(orderId.toString(),map,120, TimeUnit.MINUTES);
                }
    
                return map;
    
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    4.添加controller方法
    com.myproject.yygh.order.api.WeiXinController

    package com.myproject.yygh.order.api;
    
    import com.myproject.yygh.common.result.Result;
    import com.myproject.yygh.order.service.WeixinService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Map;
    
    @RestController
    @RequestMapping("/api/order/weixin")
    public class WeiXinController {
        @Autowired
        private WeixinService weixinService;
    
        //生成微信支付扫描的二维码
        @GetMapping("createNative/{orderId}")
        public Result createNative(@PathVariable Long orderId){
            Map map = weixinService.createNative(orderId);
            return Result.ok(map);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.2 前端

    2.2.1 封装api请求

    添加/api/weixin.js文件

    //生成微信支付的二维码
    createNative(orderId) {
        return request({
          url: `/api/order/weixin/createNative/${orderId}`,
          method: 'get'
        })
    },
    //查询支付状态的接口
    queryPayStatus(orderId) {
        return request({
          url: `/api/order/weixin/queryPayStatus/${orderId}`,
          method: 'get'
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2.2 引入vue-qriously生成二维码

    安装vue-qriously

    npm install vue-qriously
    
    • 1

    引入
    在/plugins/myPlugin.js文件添加引入

    import VueQriously from 'vue-qriously'
    Vue.use(VueQriously)
    
    • 1
    • 2

    2.2.3 页面展示

    修改/pages/order/show.vue组件

    1.将104行的微信图片改为显示二维码

    
    
    • 1

    2.在script中引入api接口

    import weixinApi from '@/api/weixin'
    
    • 1

    3.添加方法

    //生成支付二维码
    pay(){
      //支付二维码弹框显示
      this.dialogPayVisible = true
      weixinApi.createNative(this.orderId)
        .then(response => {
          this.payObj = response.data
          if(this.payObj.codeUrl == ''){//生成失败
            this.dialogPayVisible = false
            this.$message.error('支付错误')
          }else{
            //每个3秒调用查询支付状态接口
            this.timer = setInterval(() => {
              this.queryPayStatus(this.orderId)
            },3000);
          }
        })
    },
    //查询支付状态的方法
    queryPayStatus(orderId) {
      weixinApi.queryPayStatus(orderId).then(response => {
        if(response.message == '支付中'){
          return;
        }
        //清除定时器
        clearInterval(this.timer)
        window.location.reload()
      })
    },
    closeDialog(){
      if(this.timer){
        clearInterval(this.timer);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    说明:我们只有轮询查看支付状态,接下来我们处理支付查询接口

    测试功能,查看订单点击“支付”后是否显示二维码

    2.3 处理支付结果

    操作模块:yygh_order

    2.3.1 支付查询

    1.添加service接口与实现
    在WeixinService类添加接口

    //调用微信接口实现支付状态查询
    Map<String, String> queryPayStatus(Long orderId);
    
    • 1
    • 2

    在WeixinServiceImpl类添加实现

    //调用微信接口实现支付状态查询
    @Override
    public Map<String, String> queryPayStatus(Long orderId) {
        try{
            //1.根据orderId获取订单信息
            OrderInfo orderInfo = orderService.getById(orderId);
    
            //2.封装提交参数
            Map paramMap = new HashMap<>();
            paramMap.put("appid", ConstantPropertiesUtils.APPID);
            paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);
            paramMap.put("out_trade_no", orderInfo.getOutTradeNo());
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
    
            //3.设置请求内容
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);
            client.post();
    
            //4.得到微信接口返回数据
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            System.out.println("支付状态resultMap:" + resultMap);
    
            //5.把接口数据返回
            return resultMap;
        }catch (Exception e){
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.3.2 处理支付成功逻辑

    1.添加service接口与实现
    在PaymentService类添加接口

    //更新订单状态
    void paySuccess(String out_trade_no, Map<String, String> resultMap);
    
    • 1
    • 2

    在PaymentServiceImpl类添加实现

    @Autowired
    private OrderService orderService;
    
    @Autowired
    private HospitalFeignClient hospitalFeignClient;
    
    //更新订单状态
    @Override
    public void paySuccess(String out_trade_no, Map<String, String> resultMap) {
        //1.根据订单编号得到支付记录
        QueryWrapper<PaymentInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("out_trade_no", out_trade_no);
        wrapper.eq("payment_type", PaymentTypeEnum.WEIXIN.getStatus());
        PaymentInfo paymentInfo = baseMapper.selectOne(wrapper);
    
        //2.更新支付记录信息
        paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getStatus());
        paymentInfo.setTradeNo(resultMap.get("transaction_id"));
        paymentInfo.setCallbackTime(new Date());
        paymentInfo.setCallbackContent(resultMap.toString());
        baseMapper.updateById(paymentInfo);
    
        //3.根据订单号得到订单信息
        //4.更新订单信息
        OrderInfo orderInfo = orderService.getById(paymentInfo.getOrderId());
        orderInfo.setOrderStatus(OrderStatusEnum.PAID.getStatus());
        orderService.updateById(orderInfo);
    
        //5.调用医院接口,更新订单信息
        SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(orderInfo.getHoscode());
        Map<String,Object> reqMap = new HashMap<>();
        reqMap.put("hoscode",orderInfo.getHoscode());
        reqMap.put("hosRecordId",orderInfo.getHosRecordId());
        reqMap.put("timestamp", HttpRequestHelper.getTimestamp());
        String sign = HttpRequestHelper.getSign(reqMap, signInfoVo.getSignKey());
        reqMap.put("sign", sign);
    
        JSONObject result = HttpRequestHelper.sendRequest(reqMap, signInfoVo.getApiUrl() + "/order/updatePayStatus");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    2.3.3 添加controller方法

    在WeixinController类添加方法

    @Autowired
    private PaymentService paymentService;
    
    //查询支付状态
    @GetMapping("queryPayStatus/{orderId}")
    public Result queryPayStatus(@PathVariable Long orderId){
        //调用微信接口实现支付状态查询
        Map<String,String> resultMap = weixinService.queryPayStatus(orderId);
        if(resultMap == null){
            return Result.fail().message("支付出错");
        }
        if("SUCCESS".equals(resultMap.get("trade_state"))){//支付成功
            //更新订单状态
            String out_trade_no = resultMap.get("out_trade_no");//订单编号
            paymentService.paySuccess(out_trade_no,resultMap);
            return Result.ok().message("支付成功");
        }
        return Result.ok().message("支付中");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.取消预约

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

    3.1 开发微信退款接口

    参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
    该接口需要使用证书,详情参考文档并下载证书

    3.1.1 配置证书

    请下载的证书放在service-order模块/resources/cert文件夹下
    (链接:https://pan.baidu.com/s/1xPL02-ncdEFSz2nyyjlOpA 提取码:odj8)

    1.在application.yml文件配置证书路径

    weixin:
      cert: service/service_order/src/main/java/com/myproject/yygh/order/cert/apiclient_cert.p12
    
    • 1
    • 2

    2.修改ConstantPropertiesUtils工具类

    package com.myproject.yygh.order.utils;
    
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ConstantPropertiesUtils implements InitializingBean {
    
        @Value("${weixin.appid}")
        private String appid;
    
        @Value("${weixin.partner}")
        private String partner;
    
        @Value("${weixin.partnerkey}")
        private String partnerkey;
    
        @Value("${weixin.cert}")
        private String cert;
    
        public static String APPID;
        public static String PARTNER;
        public static String PARTNERKEY;
        public static String CERT;
        @Override
        public void afterPropertiesSet() throws Exception {
            APPID = appid;
            PARTNER = partner;
            PARTNERKEY = partnerkey;
            CERT = cert;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    3.修改HttpClient工具类
    修改第131行

    FileInputStream inputStream = new FileInputStream(new File(ConstantPropertiesUtils.CERT));
    
    • 1

    3.1.2 添加获取支付记录接口

    退款我们是根据支付记录发起退款的

    1.在PaymentService类添加接口

    //获取支付记录
    PaymentInfo getPaymentInfo(Long orderId, Integer paymentType);
    
    • 1
    • 2

    2.在PaymentServiceImpl类添加实现

    //获取支付记录
    @Override
    public PaymentInfo getPaymentInfo(Long orderId, Integer paymentType) {
        QueryWrapper<PaymentInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("order_id",orderId);
        wrapper.eq("payment_type",paymentType);
        PaymentInfo paymentInfo = baseMapper.selectOne(wrapper);
        return paymentInfo;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.1.3 添加退款记录

    1.添加mapper
    com.myproject.yygh.order.mapper.RefundInfoMapper

    package com.myproject.yygh.order.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.myproject.yygh.model.order.RefundInfo;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface RefundInfoMapper extends BaseMapper<RefundInfo> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.添加service接口与实现
    添加service接口
    com.myproject.yygh.order.service.RefundInfoService

    package com.myproject.yygh.order.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.myproject.yygh.model.order.PaymentInfo;
    import com.myproject.yygh.model.order.RefundInfo;
    
    public interface RefundInfoService extends IService<RefundInfo> {
        //保存退款记录
        RefundInfo saveRefundInfo(PaymentInfo paymentInfo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    添加service接口实现
    com.myproject.yygh.order.service.impl.RefundInfoServiceImpl

    package com.myproject.yygh.order.service.impl;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.myproject.yygh.enums.RefundStatusEnum;
    import com.myproject.yygh.model.order.PaymentInfo;
    import com.myproject.yygh.model.order.RefundInfo;
    import com.myproject.yygh.order.mapper.RefundInfoMapper;
    import com.myproject.yygh.order.service.RefundInfoService;
    import org.springframework.stereotype.Service;
    
    import java.util.Date;
    
    @Service
    public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundInfo> implements RefundInfoService {
    
        //保存退款记录
        @Override
        public RefundInfo saveRefundInfo(PaymentInfo paymentInfo) {
            //判断是否有重复数据添加
            QueryWrapper<RefundInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("order_id", paymentInfo.getOrderId());
            wrapper.eq("payment_type", paymentInfo.getPaymentType());
            RefundInfo refundInfo = baseMapper.selectOne(wrapper);
            if(refundInfo != null){
                return refundInfo;
            }
            //添加记录
            refundInfo = new RefundInfo();
            refundInfo.setCreateTime(new Date());
            refundInfo.setOrderId(paymentInfo.getOrderId());
            refundInfo.setPaymentType(paymentInfo.getPaymentType());
            refundInfo.setOutTradeNo(paymentInfo.getOutTradeNo());
            refundInfo.setRefundStatus(RefundStatusEnum.UNREFUND.getStatus());
            refundInfo.setSubject(paymentInfo.getSubject());
            //paymentInfo.setSubject("test");
            refundInfo.setTotalAmount(paymentInfo.getTotalAmount());
            baseMapper.insert(refundInfo);
            return refundInfo;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    3.1.4 添加微信退款接口

    在WeixinService添加接口

    //退款
    Boolean refund(Long orderId);
    
    • 1
    • 2

    在WeixinServiceImpl添加实现

    @Autowired
    private RefundInfoService refundInfoService;
    
    //微信退款
    @Override
    public Boolean refund(Long orderId) {
        try {
            //获取支付记录信息
            PaymentInfo paymentInfo = paymentService.getPaymentInfo(orderId, PaymentTypeEnum.WEIXIN.getStatus());
            //添加信息到退款记录表
            RefundInfo refundInfo = refundInfoService.saveRefundInfo(paymentInfo);
            //判断当前订单数据是否已经退款
            if(refundInfo.getRefundStatus().intValue() == RefundStatusEnum.REFUND.getStatus().intValue()) {
                return true;
            }
            //调用微信接口实现退款
            //封装需要参数
            Map<String,String> paramMap = new HashMap<>(8);
            paramMap.put("appid",ConstantPropertiesUtils.APPID);       //公众账号ID
            paramMap.put("mch_id",ConstantPropertiesUtils.PARTNER);   //商户编号
            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");
            String paramXml = WXPayUtil.generateSignedXml(paramMap,ConstantPropertiesUtils.PARTNERKEY);
    
            //设置调用接口内容
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/secapi/pay/refund");
            client.setXmlParam(paramXml);
            client.setHttps(true);
            //设置整数信息
            client.setCert(true);
            client.setCertPassword(ConstantPropertiesUtils.PARTNER);
            client.post();
    
            //接受返回数据
            String xml = client.getContent();
            Map<String,String > resultMap = WXPayUtil.xmlToMap(xml);
            if (null != resultMap && WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get("result_code"))) {
                refundInfo.setCallbackTime(new Date());
                refundInfo.setTradeNo(resultMap.get("refund_id"));
                refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                refundInfo.setCallbackContent(JSONObject.toJSONString(resultMap));
                refundInfoService.updateById(refundInfo);
                return true;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    3.2 完成取消预约

    参考《尚医通API接口文档.docx》业务接口5.3.取消预约

    3.2.1 添加service接口与实现

    在OrderService添加接口

    //取消预约
    Boolean cancelOrder(Long orderId);
    
    • 1
    • 2

    在OrderServiceImpl添加实现

    @Autowired
    private WeixinService weixinService;
    
    //取消预约
    @Override
    public Boolean cancelOrder(Long orderId) {
        //获取订单信息
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        //判断是否取消
        DateTime quitTime = new DateTime(orderInfo.getQuitTime());
        if(quitTime.isBeforeNow()){
            throw new YyghException(ResultCodeEnum.CANCEL_ORDER_NO);
        }
        //调用医院接口实现预约取消
        SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(orderInfo.getHoscode());
        if(null == signInfoVo) {
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }
        Map<String, Object> reqMap = new HashMap<>();
        reqMap.put("hoscode",orderInfo.getHoscode());
        reqMap.put("hosRecordId",orderInfo.getHosRecordId());
        reqMap.put("timestamp", HttpRequestHelper.getTimestamp());
        String sign = HttpRequestHelper.getSign(reqMap, signInfoVo.getSignKey());
        reqMap.put("sign", sign);
    
        JSONObject result = HttpRequestHelper.sendRequest(reqMap, signInfoVo.getApiUrl()+"/order/updateCancelStatus");
    
        //根据医院接口返回数据
        if(result.getInteger("code") != 200){
            throw new YyghException(result.getString("message"), ResultCodeEnum.FAIL.getCode());
        }else {
            //判断当前订单是否可以取消
            if(orderInfo.getOrderStatus().intValue() == OrderStatusEnum.PAID.getStatus().intValue()) {
                //已支付 退款
                boolean isRefund = weixinService.refund(orderId);
                if(!isRefund) {
                    throw new YyghException(ResultCodeEnum.CANCEL_ORDER_FAIL);
                }
                //更新订单状态
                orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
                baseMapper.updateById(orderInfo);
    
                //发送mq更新预约数量
                OrderMqVo orderMqVo = new OrderMqVo();
                orderMqVo.setScheduleId(orderInfo.getScheduleId());
                //短信提示
                MsmVo msmVo = new MsmVo();
                msmVo.setPhone(orderInfo.getPatientPhone());
                String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
                Map<String,Object> param = new HashMap<String,Object>(){{
                    put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                    put("reserveDate", reserveDate);
                    put("name", orderInfo.getPatientName());
                }};
                msmVo.setParam(param);
                orderMqVo.setMsmVo(msmVo);
                rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);
                return true;
            }
        }
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    3.2.2 添加controller方法

    在OrderApiController添加方法

    //取消预约
    @GetMapping("auth/cancelOrder/{orderId}")
    public Result cancelOrder(@PathVariable Long orderId){
        Boolean isOrder = orderService.cancelOrder(orderId);
        return Result.ok(isOrder);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2.3 修改监听

    操作:service-hosp模块
    修改HospitalReceiver 类

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_ORDER, durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_ORDER),
            key = {MqConst.ROUTING_ORDER}
    ))
    public void receiver(OrderMqVo orderMqVo, Message message, Channel channel) throws IOException {
        if(orderMqVo.getAvailableNumber() != null){
            //下单成功更新预约数
            Schedule schedule = scheduleService.getScheduleById(orderMqVo.getScheduleId());
            schedule.setReservedNumber(orderMqVo.getReservedNumber());
            schedule.setAvailableNumber(orderMqVo.getAvailableNumber());
            scheduleService.update(schedule);
        }else {
            Schedule schedule = scheduleService.getScheduleById(orderMqVo.getScheduleId());
            int availableNumber = schedule.getAvailableNumber().intValue() + 1;
            schedule.setAvailableNumber(availableNumber);
            scheduleService.update(schedule);
        }
        //发送短信
        MsmVo msmVo = orderMqVo.getMsmVo();
        if(null != msmVo) {
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM, MqConst.ROUTING_MSM_ITEM, msmVo);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.3 前端

    3.3.1 封装api请求

    添加/api/orderInfo.js文件

    //取消订单
    cancelOrder(orderId) {
        return request({
          url: `${api_name}/auth/cancelOrder/${orderId}`,
          method: 'get'
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.3.2 页面展示

    修改/pages/order/show.vue组件

    //取消预约
    cancelOrder() {
      this.$confirm('确定取消预约吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => { // promise
        // 点击确定,远程调用
        return orderInfoApi.cancelOrder(this.orderId)
      }).then((response) => {
        this.$message.success('取消成功')
        this.init()
      }).catch(() => {
        this.$message.info('已取消取消预约')
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.就医提醒功能

    我们通过定时任务,每天8点执行,提醒就诊

    4.1 搭建定时任务模块service-task

    在service模块中创建service-task子模块

    1.修改配置pom.xml

    <dependencies>
        <dependency>
            <groupId>com.myprojectgroupId>
            <artifactId>rabbit_utilartifactId>
            <version>1.0version>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.添加配置文件
    application.yml

    server:
      port: 8207
    
    spring:
      application:
        name: service-task
      profiles:
        active: dev
      cloud:
        nacos:
          server-addr: 127.0.0.1:8848
      rabbitmq:
        host: 192.168.50.224
        port: 5672
        username: admin
        password: admin
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3. 添加启动类

    package com.myproject.yygh.task;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.context.annotation.ComponentScan;
    
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
    @EnableDiscoveryClient
    @ComponentScan(basePackages = {"com.myproject"})
    public class ServiceTaskApplication {
        public static void main(String[] args) {
            SpringApplication.run(ServiceTaskApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.添加常量配置
    在rabbit-util模块com.myproject.common.rabbit.constant.MqConst类添加

    public static final String EXCHANGE_DIRECT_TASK = "exchange.direct.task";
    public static final String ROUTING_TASK_8 = "task.8";
    //队列
    public static final String QUEUE_TASK_8 = "queue.task.8";
    
    • 1
    • 2
    • 3
    • 4

    5.添加定时任务
    com.myproject.yygh.task.scheduled.ScheduledTask

    package com.myproject.yygh.task.scheduled;
    
    import com.myproject.common.rabbit.constant.MqConst;
    import com.myproject.common.rabbit.service.RabbitService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    @Component
    @EnableScheduling
    public class ScheduledTask {
        @Autowired
        private RabbitService rabbitService;
    
        //每天8点执行方法,提醒就医
        //cron表达式,执行时间间隔
        //每天8点:0 0 8 * * ?
        @Scheduled(cron = "0/30 * * * * ?")
        public void taskPatient(){
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_TASK,MqConst.ROUTING_TASK_8,"");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4.2 添加就医提醒处理

    操作:service_order

    1.添加service接口和接口实现
    在OrderService类添加接口

    //就诊人通知
    void patientTips();
    
    • 1
    • 2

    在OrderServiceImpl类添加接口实现

    //就诊人通知
    @Override
    public void patientTips() {
        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("reserve_date",new DateTime().toString("yyy-MM-dd"));
        wrapper.ne("order_status",OrderStatusEnum.CANCLE.getStatus());
        List<OrderInfo> orderInfoList = baseMapper.selectList(wrapper);
        for (OrderInfo orderInfo : orderInfoList) {
            //短信提示
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(orderInfo.getPatientPhone());
            String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
            Map<String,Object> param = new HashMap<String,Object>(){{
                put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
            }};
            msmVo.setParam(param);
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM, MqConst.ROUTING_MSM_ITEM, msmVo);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.添加mq监听
    com.myproject.yygh.order.receiver.OrderReceiver

    package com.myproject.yygh.order.receiver;
    
    import com.myproject.common.rabbit.constant.MqConst;
    import com.myproject.yygh.order.service.OrderService;
    import com.rabbitmq.client.Channel;
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.rabbit.annotation.Exchange;
    import org.springframework.amqp.rabbit.annotation.Queue;
    import org.springframework.amqp.rabbit.annotation.QueueBinding;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    
    @Component
    public class OrderReceiver {
        @Autowired
        private OrderService orderService;
    
        @RabbitListener(bindings = @QueueBinding(
                value = @Queue(value = MqConst.QUEUE_TASK_8, durable = "true"),
                exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
                key = {MqConst.ROUTING_TASK_8}
        ))
        public void patientTips(Message message, Channel channel) throws IOException {
            orderService.patientTips();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5.预约统计

    我们统计医院每天的预约情况,通过图表的形式展示,统计的数据都来自订单模块,因此我们在该模块封装好数据,在统计模块通过feign的形式获取数据。

    我们为什么需要一个统计模块呢,因为在实际的生成环境中,有很多种各式统计,数据来源于各个服务模块,我们得有一个统计模块来专门管理

    5.1 ECharts

    简介
    ECharts是百度的一个项目,后来百度把Echart捐给apache,用于图表展示,提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。

    官方网站:https://echarts.apache.org/zh/index.html

    5.1.1 基本使用

    echarts.min.js(链接:https://pan.baidu.com/s/1s-Az_JMZl7uj3ggDgAfzLg 提取码:pyrg)

    1.引入ECharts

    
    <script src="../js/echarts.min.js">script>
    
    • 1
    • 2

    2.定义图表区域

    
    <div id="main" style="width: 600px;height:400px;">div>
    
    • 1
    • 2

    3.渲染图表(折线图)

    <script>
        var myChart = echarts.init(document.getElementById('main'));
        var option = {
            //x轴是类目轴(离散数据),必须通过data设置类目数据
            xAxis: {
                type: 'category',
                data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
            },
            //y轴是数据轴(连续数据)
            yAxis: {
                type: 'value'
            },
            //系列列表。每个系列通过 type 决定自己的图表类型
            series: [{
                //系列中的数据内容数组
                data: [820, 932, 901, 934, 1290, 1330, 1320],
                //折线图
                type: 'line'
            }]
        };
        myChart.setOption(option);
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.渲染图表(柱状图)

    <script>
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
        // 指定图表的配置项和数据
        var option = {
            title: {
                text: 'ECharts 入门示例'
            },
            tooltip: {},
            legend: {
                data:['销量']
            },
            xAxis: {
                data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
            },
            yAxis: {},
            series: [{
                name: '销量',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };
        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4.项目中集成ECharts
    在yygh-page中安装

    npm install --save echarts@4.1.0
    
    • 1

    5.2 获取医院每天平台预约数据接口

    操作模块:service-order

    5.2.1 添加Mapper接口

    1.在OrderMapper类添加接口

    //查询预约统计数据的方法
    List<OrderCountVo> selectOrderCount(@Param("vo") OrderCountQueryVo orderCountQueryVo);
    
    • 1
    • 2

    2.在OrderMapper.xml文件添加方法
    com/myproject/yygh/order/mapper/xml/OrderMapper.xml

    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.myproject.yygh.order.mapper.OrderMapper">
        <select id="selectOrderCount" resultType="com.myproject.yygh.vo.order.OrderCountVo">
            select reserve_date as reserveDate, count(reserve_date) as count
            from order_info
            <where>
                <if test="vo.hosname != null and vo.hosname != ''">
                    and hosname like CONCAT('%',#{vo.hosname},'%')
                if>
                <if test="vo.reserveDateBegin != null and vo.reserveDateBegin != ''">
                    and reserve_date >= #{vo.reserveDateBegin}
                if>
                <if test="vo.reserveDateEnd != null and vo.reserveDateEnd != ''">
                    and reserve_date <= #{vo.reserveDateEnd}
                if>
                and is_deleted = 0
            where>
            group by reserve_date
            order by reserve_date
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.添加application.yml配置

    mybatis-plus:
      mapper-locations: classpath:com/myproject/yygh/order/mapper/xml/*.xml
    
    • 1
    • 2

    4.在yygh_parent的pom.xml中添加

    
    <build>
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>falsefiltering>
            resource>
        resources>
    build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    5.2.2 添加service接口和接口实现

    在OrderService类添加接口

    //预约统计
    Map<String,Object> getCountMap(OrderCountQueryVo orderCountQueryVo);
    
    • 1
    • 2

    在OrderServiceImpl类添加实现

    //预约统计
    @Override
    public Map<String, Object> getCountMap(OrderCountQueryVo orderCountQueryVo) {
        //调用mapper方法得到数据
        List<OrderCountVo> orderCountVoList = baseMapper.selectOrderCount(orderCountQueryVo);
    
        //获取x需要数据,日期数据 list集合
        List<String> dateList = orderCountVoList.stream().map(OrderCountVo::getReserveDate).collect(Collectors.toList());
    
        //获取y需要数据,具体数量 list集合
        List<Integer> countList = orderCountVoList.stream().map(OrderCountVo::getCount).collect(Collectors.toList());
    
        Map<String, Object> map = new HashMap<>();
        map.put("dateList", dateList);
        map.put("countList", countList);
        return map;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5.2.3 添加controller方法

    在OrderApiController类添加方法

    //获取订单统计数据
    @PostMapping("inner/getCountMap")
    public Map<String, Object> getCountMap(@RequestBody OrderCountQueryVo orderCountQueryVo) {
        return orderService.getCountMap(orderCountQueryVo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.2.4 添加feign方法

    创建模块:service-order-client

    1.添加依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-openfeign-coreartifactId>
        dependency>
        <dependency>
            <groupId>com.myprojectgroupId>
            <artifactId>modelartifactId>
            <version>1.0version>
            <scope>compilescope>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.添加feign接口
    com.myproject.yygh.order.client.OrderFeignClient

    package com.myproject.yygh.order.client;
    
    import com.myproject.yygh.vo.order.OrderCountQueryVo;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Repository;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    
    import java.util.Map;
    
    @FeignClient(value = "service-order")
    @Repository
    public interface OrderFeignClient {
        /**
         * 获取订单统计数据
         */
        @PostMapping("/api/order/orderInfo/inner/getCountMap")
        Map<String, Object> getCountMap(@RequestBody OrderCountQueryVo orderCountQueryVo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    5.3 搭建service-statistics

    创建service_statistics子模块

    1.修改配置pom.xml

    <dependencies>
        <dependency>
            <groupId>com.myprojectgroupId>
            <artifactId>service_order_clientartifactId>
            <version>1.0version>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.添加配置文件
    application.yml

    server:
      port: 8208
    spring:
      application:
        name: service-statistics
      profiles:
        active: dev
      cloud:
        nacos:
          server-addr: 127.0.0.1:8848
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.添加启动类
    com.myproject.yygh.sta.ServiceStatisticsApplication

    package com.myproject.yygh.sta;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    import org.springframework.context.annotation.ComponentScan;
    
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
    @EnableDiscoveryClient
    @EnableFeignClients(basePackages = {"com.myproject"})
    @ComponentScan(basePackages = {"com.myproject"})
    public class ServiceStatisticsApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ServiceStatisticsApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.添加controller方法
    com.myproject.yygh.sta.controller.StatisticsController

    package com.myproject.yygh.sta.controller;
    
    import com.myproject.yygh.common.result.Result;
    import com.myproject.yygh.order.client.OrderFeignClient;
    import com.myproject.yygh.vo.order.OrderCountQueryVo;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import io.swagger.annotations.ApiParam;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Map;
    
    @Api(tags = "统计管理接口")
    @RestController
    @RequestMapping("/admin/statistics")
    public class StatisticsController {
    
        @Autowired
        private OrderFeignClient orderFeignClient;
    
       //获取预约统计数据
        @GetMapping("getCountMap")
        public Result getCountMap(OrderCountQueryVo orderCountQueryVo) {
            Map<String,Object> countMap = orderFeignClient.getCountMap(orderCountQueryVo);
            return Result.ok(countMap);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    5.添加网关

    - id: service-statistics
      uri: lb://service-statistics
      predicates:
        - name: Path
          args:
            - /*/statistics/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.4 前端展示

    操作:yygh_page

    1.添加路由
    在 src/router/index.js 文件添加路由

    {
        path: '/statistics',
        component: Layout,
        redirect: '/statistics/order/index',
        name: 'BasesInfo',
        meta: { title: '统计管理', icon: 'table' },
        alwaysShow: true,
        children: [
          {
            path: 'order/index',
            name: '预约统计',
            component: () =>import('@/views/statistics/order/index'),
            meta: { title: '预约统计' }
          }
        ]
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.封装api请求
    创建/api/sta.js

    import request from '@/utils/request'
    
    const api_name = '/admin/statistics'
    
    export default {
    
      getCountMap(searchObj) {
        return request({
          url: `${api_name}/getCountMap`,
          method: 'get',
          params: searchObj
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.添加组件
    创建/views/statistics/order/index.vue组件

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
  • 相关阅读:
    pandas 学习 第15篇:分组 groupby
    SAP 系统License查看申请及导入
    Golang 实现对配置文件加密
    数字图像处理(2)像素邻域、领接、通路与距离
    管道和重定向分号-连接符
    openjudge 1.5.3 均值
    Go与C/C++中的堆和栈比较
    如何改进企业旧式工时管理系统?
    【Shell】sh执行脚本报错Syntax error: “(“ unexpected
    Kettle——大数据ETL工具
  • 原文地址:https://blog.csdn.net/hutc_Alan/article/details/126220353