前端代码:https://github.com/wyj41/yygh_html.git
后端代码:https://github.com/wyj41/yygh_parent.git
操作模块:service_order
1.添加service接口及实现类
在OrderService类添加接口
//根据订单id查询订单详情
OrderInfo getOrder(String orderId);
在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;
}
2.添加controller
在OrderApiController类添加方法
//根据订单id查询订单详情
@GetMapping("auth/getOrders/{orderId}")
public Result getOrder(@PathVariable String orderId){
OrderInfo orderInfo = orderService.getOrder(orderId);
return Result.ok(orderInfo);
}
操作:yygh-site
1.封装api请求
在/api/orderInfo.js添加方法
//订单详情
getOrders(orderId){
return request({
url: `${api_name}/auth/getOrders/${orderId}`,
method: 'get'
})
}
2.页面展示
创建/pages/order/show.vue组件
操作模块:service_order
1.添加service接口及实现类
在OrderService类添加接口
//订单列表(条件查询带分页)
IPage<OrderInfo> selectPage(Page<OrderInfo> pageParam, OrderQueryVo orderQueryVo);
在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;
}
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.封装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'
})
}
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>
注:平台订单管理和用户订单管理接口基本一样,因此不再操作
微信扫码支付申请
微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
申请步骤:(了解)
第一步:注册公众号(类型须为:服务号)
请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。
第二步:认证公众号
公众号认证后才可申请微信支付,认证费:300元/年。
第三步:提交资料申请微信支付
登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。
第四步:开户成功,登录商户平台进行验证
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。
第五步:在线签署协议
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。
开发文档
微信支付接口调用的整体思路:
按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。
在线微信支付开发文档:
https://pay.weixin.qq.com/wiki/doc/api/index.html
场景:用户扫描商户展示在各种场景的二维码进行支付
使用案例:
线下:家乐福超市、7-11便利店、上品折扣线下店等
线上:大众点评网站、携程网站、唯品会、美丽说网站等
开发模式:
模式一:商户在后台给你生成二维码,用户打开扫一扫
模式二:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。
微信支付:生成xml发送请求
操作模块:service-order
1.引入依赖
<dependency>
<groupId>com.github.wxpaygroupId>
<artifactId>wxpay-sdkartifactId>
<version>0.0.3version>
dependency>
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
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;
}
}
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.添加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> {
}
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);
}
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);
}
}
*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);
}
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);
}
}
}
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);
}
}
添加/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'
})
}
安装vue-qriously
npm install vue-qriously
引入
在/plugins/myPlugin.js文件添加引入
import VueQriously from 'vue-qriously'
Vue.use(VueQriously)
修改/pages/order/show.vue组件
1.将104行的微信图片改为显示二维码
2.在script中引入api接口
import weixinApi from '@/api/weixin'
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);
}
}
说明:我们只有轮询查看支付状态,接下来我们处理支付查询接口
测试功能,查看订单点击“支付”后是否显示二维码
操作模块:yygh_order
1.添加service接口与实现
在WeixinService类添加接口
//调用微信接口实现支付状态查询
Map<String, String> queryPayStatus(Long orderId);
在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.添加service接口与实现
在PaymentService类添加接口
//更新订单状态
void paySuccess(String out_trade_no, Map<String, String> resultMap);
在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");
}
在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、已支付取消订单,先退款给用户,然后通知医院更新取消预约状态
参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
该接口需要使用证书,详情参考文档并下载证书
请下载的证书放在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
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;
}
}
3.修改HttpClient工具类
修改第131行
FileInputStream inputStream = new FileInputStream(new File(ConstantPropertiesUtils.CERT));
退款我们是根据支付记录发起退款的
1.在PaymentService类添加接口
//获取支付记录
PaymentInfo getPaymentInfo(Long orderId, Integer paymentType);
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.添加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> {
}
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);
}
添加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;
}
}
在WeixinService添加接口
//退款
Boolean refund(Long orderId);
在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;
}
参考《尚医通API接口文档.docx》业务接口5.3.取消预约
在OrderService添加接口
//取消预约
Boolean cancelOrder(Long orderId);
在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;
}
在OrderApiController添加方法
//取消预约
@GetMapping("auth/cancelOrder/{orderId}")
public Result cancelOrder(@PathVariable Long orderId){
Boolean isOrder = orderService.cancelOrder(orderId);
return Result.ok(isOrder);
}
操作: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);
}
}
添加/api/orderInfo.js文件
//取消订单
cancelOrder(orderId) {
return request({
url: `${api_name}/auth/cancelOrder/${orderId}`,
method: 'get'
})
}
修改/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('已取消取消预约')
})
}
我们通过定时任务,每天8点执行,提醒就诊
在service模块中创建service-task子模块
1.修改配置pom.xml
<dependencies>
<dependency>
<groupId>com.myprojectgroupId>
<artifactId>rabbit_utilartifactId>
<version>1.0version>
dependency>
dependencies>
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
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);
}
}
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";
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,"");
}
}
操作:service_order
1.添加service接口和接口实现
在OrderService类添加接口
//就诊人通知
void patientTips();
在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);
}
}
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();
}
}
我们统计医院每天的预约情况,通过图表的形式展示,统计的数据都来自订单模块,因此我们在该模块封装好数据,在统计模块通过feign的形式获取数据。
我们为什么需要一个统计模块呢,因为在实际的生成环境中,有很多种各式统计,数据来源于各个服务模块,我们得有一个统计模块来专门管理
简介
ECharts是百度的一个项目,后来百度把Echart捐给apache,用于图表展示,提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。
官方网站:https://echarts.apache.org/zh/index.html
echarts.min.js(链接:https://pan.baidu.com/s/1s-Az_JMZl7uj3ggDgAfzLg 提取码:pyrg)
1.引入ECharts
<script src="../js/echarts.min.js">script>
2.定义图表区域
<div id="main" style="width: 600px;height:400px;">div>
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>
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>
4.项目中集成ECharts
在yygh-page中安装
npm install --save [email protected]
操作模块:service-order
1.在OrderMapper类添加接口
//查询预约统计数据的方法
List<OrderCountVo> selectOrderCount(@Param("vo") OrderCountQueryVo orderCountQueryVo);
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>
3.添加application.yml配置
mybatis-plus:
mapper-locations: classpath:com/myproject/yygh/order/mapper/xml/*.xml
4.在yygh_parent的pom.xml中添加
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
在OrderService类添加接口
//预约统计
Map<String,Object> getCountMap(OrderCountQueryVo orderCountQueryVo);
在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;
}
在OrderApiController类添加方法
//获取订单统计数据
@PostMapping("inner/getCountMap")
public Map<String, Object> getCountMap(@RequestBody OrderCountQueryVo orderCountQueryVo) {
return orderService.getCountMap(orderCountQueryVo);
}
创建模块: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>
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);
}
创建service_statistics子模块
1.修改配置pom.xml
<dependencies>
<dependency>
<groupId>com.myprojectgroupId>
<artifactId>service_order_clientartifactId>
<version>1.0version>
dependency>
dependencies>
2.添加配置文件
application.yml
server:
port: 8208
spring:
application:
name: service-statistics
profiles:
active: dev
cloud:
nacos:
server-addr: 127.0.0.1:8848
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);
}
}
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);
}
}
5.添加网关
- id: service-statistics
uri: lb://service-statistics
predicates:
- name: Path
args:
- /*/statistics/**
操作: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: '预约统计' }
}
]
},
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
})
}
}
3.添加组件
创建/views/statistics/order/index.vue组件
查询