前台用户系统
预约挂号
1、接口分析
(1)根据预约周期,展示可预约日期数据,按分页展示
(2)选择日期展示当天可预约列表(该接口后台已经实现过)
2、页面展示分析
(1)分页展示可预约日期,根据有号、无号、约满等状态展示不同颜色,以示区分
(2)可预约最后一个日期为即将放号日期,根据放号时间页面展示倒计时
点进任意一个科室可以查看预约信息
点击剩余按钮,实现预约挂号
预约确认
1、根据排班id获取排班信息,在页面展示
2、选择就诊人
3、预约下单
下单参数:就诊人id与排班id
1、下单我们要获取就诊人信息
2、获取排班下单信息与规则信息
3、获取医院签名信息,然后通过接口去医院预约下单(实际挂号用的是医院系统,就是hospital-manage)
4、下单成功更新排班信息与发送短信
消息队列
预约成功后我们要更新预约数和短信提醒预约成功,为了提高下单的并发性,这部分逻辑我们就交给mq为我们完成,预约成功发送消息即可
以商品订单场景为例,
如果商品服务和订单服务是两个不同的微服务,在下单的过程中订单服务需要调用商品服务进行扣库存操作。按照传统的方式,下单过程要等到调用完毕之后才能返回下单成功,如果网络产生波动等原因使得商品服务扣库存延迟或者失败,会带来较差的用户体验,如果在高并发的场景下,这样的处理显然是不合适的,那怎么进行优化呢?这就需要消息队列登场了。
消息队列提供一个异步通信机制,消息的发送者不必一直等待到消息被成功处理才返回,而是立即返回。消息中间件负责处理网络通信,如果网络连接不可用,消息被暂存于队列当中,当网络畅通的时候在将消息转发给相应的应用程序或者服务,当然前提是这些服务订阅了该队列。如果在商品服务和订单服务之间使用消息中间件,既可以提高并发量,又降低服务之间的耦合度。
RabbitMQ就是这样一款消息队列。RabbitMQ是一个开源的消息代理的队列服务器,用来通过普通协议在完全不同的应用之间共享数据。
典型应用场景:
异步处理。把消息放入消息中间件中,等到需要的时候再去处理。
流量削峰。例如秒杀活动,在短时间内访问量急剧增加,使用消息队列,当消息队列满了就拒绝响应,跳转到错误页面,这样就可以使得系统不会因为超负载而崩溃。
安装RabbitMQ
docker pull rabbitmq:management
docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:management
注意阿里云的安全组要打开对应的端口,不然访问不了,参考教程
管理后台就是ip+15672
默认密码就是guest guest
主要流程:order下单-》预约下单mq监听器接收,更新hosp排班信息-》再msm通过mq发送短信-》短信mq监听器接收,做发短信操作
调用时报错
查看报错信息
serviceOrderApplication
Read timed out executing GET http://service-user/api/user/patient/inner/get/1
serviceUserApplication
Read timed out executing GET http://service-cmn/admin/cmn/dict/getName/CertificatesType/10
这里我发现需要把数据库的数据改好,老师上课讲的是一套,后面他偷偷改了,但我不知道
参考百度文库
讲解P160有坑的地方
得到页面结果
具体代码查看gitee第二十二次更新
完成挂号订单显示功能
对应数据库yygh-manage表中的orderinfo部分
这个时间显示是这样的,数据库慢八个小时
可以根据状态和就诊人分别查询
gitee第二十三次更新
微信支付功能
看看效果:
点击支付
扫码得到效果
打印输出resultMap可以得到一组数据
resultMap:{nonce_str=pG9ymNirFXtrxkSP, code_url=weixin://wxpay/bizpayurl?pr=n7PXIkuzz, appid=wx74862e0dfcf69954, sign=829F33F3C2038E67057C7557DB24E811, trade_type=NATIVE, return_msg=OK, result_code=SUCCESS, mch_id=1558950191, return_code=SUCCESS, prepay_id=wx0521391336312794fe3b6dd5ec50a40000}
支付信息确认
这里虽然生成了支付界面,我们也可以实际汇款,但是我的后台并不知道我有没有支付,还需要搭建这两者的桥梁
所以还要写这样一些逻辑的代码
@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, PaymentInfo> implements PaymentService {
@Autowired
private OrderService orderService;
@Autowired
private HospitalFeignClient hospitalFeignClient;
//向支付记录表添加信息
@Override
public void savePaymentInfo(OrderInfo order, Integer paymentType) {
//根据订单id和支付类型,查询支付记录表是否存在相同订单
QueryWrapper<PaymentInfo> wrapper = new QueryWrapper<>();
wrapper.eq("order_id", order.getId());
wrapper.eq("payment_type", paymentType);
Integer count = baseMapper.selectCount(wrapper);
//有数据,直接返回
if (count > 0) {
return;
}
//没有数据,则添加记录
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setCreateTime(new Date());
paymentInfo.setOrderId(order.getId());
paymentInfo.setPaymentType(paymentType);
paymentInfo.setOutTradeNo(order.getOutTradeNo());
paymentInfo.setPaymentStatus(PaymentStatusEnum.UNPAID.getStatus());
String subject = new DateTime(order.getReserveDate()).toString("yyyy-MM-dd") + "|" + order.getHosname() + "|" + order.getDepname() + "|" + order.getTitle();
paymentInfo.setSubject(subject);
paymentInfo.setTotalAmount(order.getAmount());
baseMapper.insert(paymentInfo);
}
//更新订单状态
@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.setCallbackTime(new Date());
paymentInfo.setTradeNo(resultMap.get("transaction_id"));
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");
}
}
具体查看gitee的更新
支付完成后
表里的状态也更新了
serviceorder也会输出
由于每隔3秒才刷新一次,所有会有多次打印信息
退款功能
取消订单分两种情况:
1、未支付取消订单,直接通知医院更新取消预约状态
2、已支付取消订单,先退款给用户,然后通知医院更新取消预约状态
支付成功后点取消预约
报错了,前端有个参数写错了,我一直以为是后端写错,重新搜了还是没有,结果是前端
接着报了一个签名错误
原来是我的数据库这两者一个要加密,一个是保持原型
这样才判断的出来
yygh_hosp下面的sign_key要加密后的
yygh_manage下面的sign_key要加密前的