挂号流程
大体流程是这样的
1、用户系统选中某一个科室下的某个排班,点击剩余按钮,跳转到一个页面然后选择就诊人,点击确认挂号。
2、现在状态就变为预约成功,未支付状态。我们点击支付,之后就是扫描二维码付款了。
1、确认挂号(创建订单)
rabbitmq的使用
mq在这里充当了异步调用的作用,可以提高下单的响应速度。
1.1 调用医院端 的程序在医院端保存一条订单和在医院端扣减号源。
//使用httpclient发送请求,请求医院接口
//医院端 1.扣减号源 2.保存医院端自己的订单
JSONObject result =
HttpRequestHelper.sendRequest(paramMap, "http://localhost:9998/order/submitOrder");
1.2 在我们的平台端也保存一条我们自己的订单,之后发送消息到mq,异步处理。
1.3 mq在本项目的异步处理的体现:
平台端的医院服务更新mongodb中的号源(数据来自医院端的返回)
平台端医院服务再发送消息到mq,短信服务消费这些消息发送短信通知用户挂号成功。
OrderMqVo orderMqVo = new OrderMqVo();
//这2个数量都是医院端返给我们平台端的
orderMqVo.setReservedNumber(reservedNumber);//总的号源数量
orderMqVo.setAvailableNumber(availableNumber);//剩余可预约数量
orderMqVo.setScheduleId(scheduleId);//mongondb中排班id
MsmVo msmVo = new MsmVo();
msmVo.setPhone(orderInfo.getPatientPhone());
Map<String,Object> map = new HashMap<String,Object>(){{
put("message","请于"+orderInfo.getReserveDate()+"到"
+orderInfo.getHosname()+orderInfo.getDepname()+"就诊");
}};
msmVo.setParam(map);
orderMqVo.setMsmVo(msmVo);
//发送消息到mq,异步处理
rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,orderMqVo);
发送短信的话,我们模拟一下,在控制台输出。上一篇我们已经讲过了如何调用阿里云发送短信,在短信服务控制台中输出如下:
2、点击支付(生成二维码)
在线微信支付开发文档:
https://pay.weixin.qq.com/wiki/doc/api/index.html
--------------------------------------
生成二维码
使用Redis判断5分钟内是否生成过二维码
Map m = (Map)redisTemplate.opsForValue().get(orderId.toString());
//防止5分钟之内重复向微信端发请求获取支付链接
if(m!=null){
return m;
}
1、创建支付记录,处于未支付状态
2、向微信端“ 统一下单 ” 发请求,拿到二维码连接,并把map存储到Redis
paramMap参数的设置就多了去了,,,
//2、HTTPClient来根据URL访问第三方接口并且传递参数
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
//client设置参数
client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
client.setHttps(true);
client.post();
//3、微信端返回数据xml格式数据
String xml = client.getContent();
Map<String, String> result = WXPayUtil.xmlToMap(xml);
String return_code = result.get("return_code");
String result_code = result.get("result_code");
if (return_code.equals("SUCCESS") && result_code.equals("SUCCESS")) {
Map map = new HashMap();
map.put("orderId", orderId);
map.put("totalFee", order.getAmount());
map.put("resultCode", result.get("result_code"));//返回状态码 SUCCESS/FAIL
map.put("codeUrl", result.get("code_url"));//二维码链接
redisTemplate.opsForValue().set(orderId.toString(),map,5, TimeUnit.MINUTES);
return map;
}
----------------------------------------------
查询支付状态
打开二维码后,前端每隔3秒去调用查询支付状态接口
1、查询微信端“查询订单”接口
代码如下,没啥可说的。写法很固定!
代码里面有很多工具类,比如发送请求的、map和xml格式互转的。
//根据订单号去微信第三方查询支付状态
@Override
public Map queryPayStatus(Long orderId) {
OrderInfo orderInfo = orderService.getById(orderId);
//1、封装参数
Map paramMap = new HashMap<>();
paramMap.put("appid", ConstantPropertiesUtils.APPID);
paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);//商户号
paramMap.put("out_trade_no", orderInfo.getOutTradeNo());//订单的out_trade_no
paramMap.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
try {
//2、设置请求,调用微信端“查询订单”接口
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
//微信端都是xml作为参数和返回值
//generateSignedXml会自动添加签名
client.setXmlParam(WXPayUtil
.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));//商户号签名
client.setHttps(true); //是否支持指定协议
client.post();
//3、获取返回值,转成Map
String xml = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
//4、返回
return resultMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
2、支付成功,需要做的事情有修改订单的状态为已支付,修改或填充支付记录中的4个字段
比较重要的就是 trade_no,这个是微信端返回给我们的支付流水号。另外一个out_trade_no是我们订单表中自己的交易记录号,out_trade_no会伴随着整个支付流程
paymentInfo.setTradeNo(paramMap.get("transaction_id"))
3、取消预约 (取消订单)
取消预约 有2种情况
(1)未支付取消订单,直接通知医院更新取消预约状态
(2)已支付取消订单,先退款给用户,然后通知医院更新取消预约状态
取消预约需要配置证书:
拷贝证书到c盘下:
在配置文件中:
weixin.cert=C:\\apiclient_cert.p12
1、根据支付记录创建退款记录
2、调用微信端接口“申请退款”
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);//商户id
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;
}
return false;
3、查询退款成功则修改第一步创建的退款记录,并把平台端的订单状态也要改为-1
4、mq异步实现
- 平台端号源数量加1,(相比创建订单少发俩个参数)
- 通知就诊人退号成功
//4.向mq发送消息,完成mongondb中号源数量+1,短信通知就诊人
OrderMqVo orderMqVo = new OrderMqVo();
orderMqVo.setScheduleId(orderInfo.getScheduleId());
MsmVo msmVo =new MsmVo();
msmVo.setPhone(orderInfo.getPatientPhone());
Map<String,Object> param=new HashMap<>();
param.put("message","退号成功");
msmVo.setParam(param);
orderMqVo.setMsmVo(msmVo);
rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,msmVo);
就医提醒【定时任务、mq实现】
请夸我灵魂画手,低调低调哈哈
1、在定时任务模块发送消息到task队列
这个消息可以是空的,本来就是起一个提示的作用。
要注意的点就是 @Scheduled(cron = "0/50 * * * * ?") //等步增长
/**
* 每天8点执行 提醒就诊
*/
//@Scheduled(cron = "0 0 8 * * ?")
@Scheduled(cron = "0/5 * * * * ?") //等步增长
public void task() {
System.out.println(new Date().toLocaleString());
rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_TASK, MqConst.ROUTING_TASK_8, "");
}
RabbitService是我们自己封装好的一个操作rabbitmq的服务
@Component
public class RabbitService {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 发送消息
* @param exchange 交换机
* @param routingKey 路由键
* @param message 消息
*/
public boolean sendMessage(String exchange, String routingKey, Object message) {
rabbitTemplate.convertAndSend(exchange, routingKey, message);
return true;
}
}
2、订单服务去消费“task队列”的消息
2.1、去查询当天的就诊人,实际上是查询 就诊日期是当天的订单,
2.2、然后在订单服务遍历当天的这些订单,给“msm队列”里发送消息。
public void patientTips() {
//1、查询当天已支付所有的订单
QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("reserve_date", new DateTime().toString("yyyy-MM-dd"));
queryWrapper.eq("order_status",1);
List<OrderInfo> orderInfos = baseMapper.selectList(queryWrapper);
//2、循环遍历发送消息到mq
for (OrderInfo orderInfo : orderInfos) {
//短信提示
MsmVo msmVo = new MsmVo();
//就诊人手机号
msmVo.setPhone(orderInfo.getPatientPhone());
//短信内容
Map<String,Object> param = new HashMap<String,Object>(){{
put("message", orderInfo.getPatientName()+"请于今天就诊");
}};
msmVo.setParam(param);
//发送消息到msm队列
rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM, MqConst.ROUTING_MSM_ITEM, msmVo);
}
}
3、短信服务 去消费“msm队列”的消息
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = MqConst.QUEUE_MSM_ITEM, durable = "true"),
exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_MSM),
key = {MqConst.ROUTING_MSM_ITEM}
))
public void send(MsmVo msmVo) {
Map<String, Object> param = msmVo.getParam();
System.out.println("[尚医通]: "+param.get("message"));
}
控制台打印如下:当然你还可以让提示更加丰富,我这里就只取了“就诊人的名字”
预约统计 Echart
在实际的生产环境中,有很多种各式统计,数据来源于各个服务模块,我们得有一个统计模块来专门管理。
ECharts是百度的一个项目,后来百度把Echart捐给apache,用于图表展示,提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。
官方网站:https://echarts.apache.org/zh/index.html
需求:统计每天平台预约数据(即订单数量)
实际上返回给前端的就是横坐标集合和纵坐标集合。横坐标表示日期,纵坐标表示预约数量
项目中安装 echarts组件
npm install --save echarts@4.1.0
大体流程
我们要做的事,对应到mysql,差不多就是这样
SELECT `reserve_date`,COUNT(0) AS 'count' FROM `order_info` GROUP BY `reserve_date` ORDER BY `reserve_date`
核心代码就是
1、调用mapper接口查询
<select id="selectOrderCount" resultType="com.atguigu.yygh.vo.order.OrderCountVo">
select reserve_date as reserveDate, count(reserve_date) as count
from order_info
<where>
<if test="hosname != null and hosname != ''">
and hosname like CONCAT('%',#{hosname},'%')
</if>
<if test="reserveDateBegin != null and reserveDateBegin != ''">
and reserve_date >= #{reserveDateBegin}
</if>
<if test="reserveDateEnd != null and reserveDateEnd != ''">
and reserve_date <= #{reserveDateEnd}
</if>
and is_deleted = 0
</where>
group by reserve_date
order by reserve_date
</select>
2、把查询的结果封装横坐标集合和纵坐标集合
//获取订单统计数据
@Override
public Map<String, Object> getCountMap(OrderCountQueryVo orderCountQueryVo) {
List<OrderCountVo> orderCountVos = orderInfoMapper.selectOrderCount(orderCountQueryVo);
List<String> dateList= orderCountVos.stream()
.map(OrderCountVo::getReserveDate).collect(Collectors.toList());
List<Integer> countList= orderCountVos.stream()
.map(OrderCountVo::getCount).collect(Collectors.toList());
HashMap<String, Object> map = new HashMap<>();
//给前端返回横坐标集合(日期)和纵坐标集合(预约数量)
map.put("dateList",dateList);
map.put("countList",countList);
return map;
}
最后,就可以在页面看到好看的效果了,当然省略了前端的代码
项目总结
1、项目流程图
2、项目功能
医院端
医院设置管理
医院管理
数据字典
用户管理
统计管理