目录
一、订单列表
1、后端接口
2、前端对接
二、订单详情
三、订单支付 -- 微信支付
1、微信扫码支付申请
2、获取二维码
(1). 准备工作
(2) 添加交易记录接口
(3) 生成二维码接口
(4) 前端实现
3、微信支付 (扫描后处理)
(1) 接口方案
(2) 实现查询交易状态接口
(3) 前端实现
一、订单列表
需要先登录再进行查询(先获取userId),带分页带条件的列表查询
1、后端接口
1. 需求分析
*参数:page,limit,OrderQueryVo,请求对象(userId)
*返回值:Page
2. order模块 添加分页插件
@Configuration
@EnableTransactionManagement
public class OrderConfig {
//分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
3. controller
@ApiOperation(value = "带条件带分页查询订单列表")
@GetMapping("auth/{page}/{limit}")
public R list(@PathVariable Long page,
@PathVariable Long limit,
OrderQueryVo orderQueryVo, HttpServletRequest request) {
//1.获取userId,存入orderQueryVo
Long userId = AuthContextHolder.getUserId(request);
orderQueryVo.setUserId(userId);
//2.封装分页参数(苞米豆)
Page pageParams = new Page<>(page,limit);
//3.调用方法查询
Page pageModel = orderService.selectPage(pageParams,orderQueryVo);
return R.ok().data("pageModel",pageModel);
}
@ApiOperation(value = "获取订单状态")//下拉列选
@GetMapping("auth/getStatusList")
public R getStatusList() {
return R.ok().data("statusList", OrderStatusEnum.getStatusList());
}
4. service
//带条件带分页查询订单列表
@Override
public Page selectPage(Page pageParams, OrderQueryVo orderQueryVo) {
//1.取出参数
Long userId = orderQueryVo.getUserId();
String name = orderQueryVo.getKeyword(); //医院名称
Long patientId = orderQueryVo.getPatientId(); //就诊人名称
String orderStatus = orderQueryVo.getOrderStatus(); //订单状态
String reserveDate = orderQueryVo.getReserveDate();//安排时间
String createTimeBegin = orderQueryVo.getCreateTimeBegin();
String createTimeEnd = orderQueryVo.getCreateTimeEnd();
//2.验空,拼写筛选条件
QueryWrapper wrapper = new QueryWrapper<>();
if(!StringUtils.isEmpty(userId)){
wrapper.eq("user_id",userId);
}
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.ge("reserve_date",reserveDate);
}
if(!StringUtils.isEmpty(createTimeBegin)) {
wrapper.ge("create_time",createTimeBegin);
}
if(!StringUtils.isEmpty(createTimeEnd)) {
wrapper.le("create_time",createTimeEnd);
}
//3.分页查询
Page pageModel = baseMapper.selectPage(pageParams, wrapper);
//4.翻译状态(根据枚举,不涉及跨模块调用)
pageModel.getRecords().stream().forEach(item->{
this.packOrderInfo(item);
});
return pageModel;
}
//根据枚举类翻译状态
private OrderInfo packOrderInfo(OrderInfo orderInfo) {
orderInfo.getParam().put("orderStatusString", OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus()));
return orderInfo;
}
2、前端对接
1 确认入口,添加页面
2. 添加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'
})
},
3. 添加页面元素
实名认证
挂号订单
就诊人管理
修改账号信息
意见反馈
挂号订单
查询
{{ scope.row.reserveDate }} {{ scope.row.reserveTime === 0 ? '上午' : '下午' }}
详情
4. 实现JS
5. 测试
二、订单详情
1. controller
@ApiOperation(value = "根据订单id查询订单详情")
@GetMapping("auth/getOrders/{orderId}")
public R getOrders(@PathVariable Long orderId) {
OrderInfo orderInfo = orderService.getOrderById(orderId);
return R.ok().data("orderInfo",orderInfo);
}
2. service
//根据订单id查询订单详情
@Override
public OrderInfo getOrderById(Long orderId) {
//翻译字段并返回
OrderInfo orderInfo = this.packOrderInfo(baseMapper.selectById(orderId));
return this.packOrderInfo(orderInfo);
}
3. 确认入口,创建页面,添加页面元素
实名认证
挂号订单
就诊人管理
修改账号信息
意见反馈
挂号详情
{{ orderInfo.param.orderStatusString }}
微信关注“北京114预约挂号”
快速挂号,轻松就医
挂号信息
{{ orderInfo.patientName }}
{{ orderInfo.reserveDate }} {{ orderInfo.reserveTime == 0 ?
'上午' : '下午' }}
{{ orderInfo.hosname }}
{{ orderInfo.depname }}
{{ orderInfo.title }}
{{ orderInfo.amount }}元
{{ orderInfo.outTradeNo }}
{{ orderInfo.createTime }}
注意事项
1、请确认就诊人信息是否准确,若填写错误将无法取号就诊,损失由本人承担;
2、【取号】就诊当天需在{{ orderInfo.fetchTime }}在医院取号,未取号视为爽约,该号不退不换;
3、【退号】在{{ orderInfo.quitTime }}前可在线退号 ,逾期将不可办理退号退费;
4、北京114预约挂号支持自费患者使用身份证预约,同时支持北京市医保患者使用北京社保卡在平台预约挂号。请于就诊当日,携带预约挂号所使用的有效身份证件到院取号;
5、请注意北京市医保患者在住院期间不能使用社保卡在门诊取号。
取消预约
支付
请使用微信扫一扫
扫描二维码支付
4. API
//订单详情
getOrders(orderId) {
return request({
url: `${api_name}/auth/getOrders/${orderId}`,
method: `get`
})
},
5. JS实现,添加CSS
6. 测试
三、订单支付 -- 微信支付
1、微信扫码支付申请
在线微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
1、微信扫码支付申请
微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
申请步骤:(了解)
第一步:注册公众号(类型须为:服务号)
请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。
第二步:认证公众号
公众号认证后才可申请微信支付,认证费:300元/年。
第三步:提交资料申请微信支付
登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。
第四步:开户成功,登录商户平台进行验证
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。
第五步:在线签署协议
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。
2、微信支付提供工具
(1) 获取随机字符串:WXPayUtil.generateNonceStr()
(2) MAP转换为XML字符串(自动添加签名): WXPayUtil.generateSignedXml(param, partnerkey)
(3) XML字符串转换为MAP:WXPayUtil.xmlToMap(result)
2、获取二维码
(1). 准备工作
1. service_orders 引入依赖
com.github.wxpay
wxpay-sdk
0.0.3
2. 添加配置
#redis spring.redis.host=192.168.86.86 spring.redis.port=6379 spring.redis.database= 0 spring.redis.timeout=1800000 spring.redis.lettuce.pool.max-active=20 spring.redis.lettuce.pool.max-wait=-1 #最大阻塞等待时间(负数表示没限制) spring.redis.lettuce.pool.max-idle=5 spring.redis.lettuce.pool.min-idle=0 #关联的公众号appid weixin.pay.appid=wx74862e0dfcf69954 #商户号 weixin.pay.partner=1558950191 #商户key weixin.pay.partnerkey=T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
3. 添加工具类读取配置信息,注释属性 cert
(2) 添加交易记录接口
payment_info
1. 确认表
2. 搭建 payment接口、类
*参数:订单id,交易类型
*返回值:无
public interface PaymentService extends IService{ void savePaymentInfo(OrderInfo orderInfo, Integer paymentType); } @Service public class PaymentServiceImpl extends ServiceImpl implements PaymentService { //保存交易记录 @Override public void savePaymentInfo(OrderInfo orderInfo, Integer paymentType) { //1.根据order订单id+交易类型,查询交易记录 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("order_id", orderInfo.getId()); wrapper.eq("payment_type", paymentType); Integer count = baseMapper.selectCount(wrapper); if(count>0) return; //2.如果没有交易记录添加新的交易记录 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) 生成二维码接口
1. controller
*参数:orderId
*返回值:Map
@Api(tags = "微信支付接口")
@RestController
@RequestMapping("/api/order/weixin")
public class WeixinController {
@Autowired
private WeixinService weixinPayService;
@ApiOperation(value = "生成支付二维码")
@GetMapping("/createNative/{orderId}")
public R createNative(@PathVariable("orderId") Long orderId) {
Map map = weixinPayService.createNative(orderId);
return R.ok().data(map);
}
}
2. service
@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 {
//Map payMap = (Map) redisTemplate.opsForValue().get(orderId.toString());
//if(null != payMap) return payMap;
//1.查询订单信息
OrderInfo orderInfo = orderService.getById(orderId);
if(orderId==null){
throw new YyghException(20001,"订单信息有误");
}
//2.生成交易记录
paymentService.savePaymentInfo(orderInfo,PaymentTypeEnum.WEIXIN.getStatus());
//3.封装调用微信接口相关参数
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.创建客户端对象 (设置访问url 参考文档)
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
//5.设置参数 (map=>xml)
client.setXmlParam(WXPayUtil.generateSignedXml
(paramMap,ConstantPropertiesUtils.PARTNERKEY));
//6.客户端发送请求
client.setHttps(true); //开启加密方式 http与https区别:https是加密的
client.post();
//7.获取响应,转化响应类型(xml=>map)
String xml = client.getContent();
System.out.println("xml = " + xml);
Map resultMap = WXPayUtil.xmlToMap(xml);
//8.封装返回结果
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"));
//if(null != resultMap.get("result_code")) {
//微信支付二维码2小时过期,可采取2小时未支付取消订单
//redisTemplate.opsForValue().set(orderId.toString(), map, 1000, TimeUnit.MINUTES);
//}
return map;
} catch (Exception e) {
e.printStackTrace();
return new HashMap<>();
}
}
}
3. 测试:
(4) 前端实现
1. 安装vue-qriously
npm install vue-qriously
修改插件js文件
import Vue from 'vue' import ElementUI from 'element-ui' //element-uid的全部组件 import VueQriously from 'vue-qriously' import 'element-ui/lib/theme-chalk/index.css' //element-ui 的 css Vue.use(ElementUI) //使用elementUI Vue.use(VueQriously)
2. API
//微信二维码
createNative(orderId) {
return request({
url: `/api/order/weixin/createNative/${orderId}`,
method: 'get'
})
}
3. order/show.vue 页面改造
请使用微信扫一扫
扫描二维码支付
4. JS改造
import weixinApi from '@/api/wx'
...
//支付
pay() {
this.dialogPayVisible = true //打开对话框
weixinApi.createNative(this.orderId)
.then(response => {
this.payObj = response.data
if (this.payObj.codeUrl == '') {
this.dialogPayVisible = false
this.$message.error("支付错误")
}
})
}
5. 测试
3、微信支付 (扫描后处理)
(1) 接口方案
(1) 微信回调接口
(2) 主动查询交易结果 (定时器,setInterval clearInterval)
(2) 实现查询交易状态接口
*参数:orderId
*返回值:R.ok()
1. controller
@ApiOperation(value = "查询支付状态")
@GetMapping("/queryPayStatus/{orderId}")
public R queryPayStatus(@PathVariable("orderId") Long orderId) {
//1.根据参数查询交易状态
Map resultMap = weixinPayService
.queryPayStatus(orderId, PaymentTypeEnum.WEIXIN.name());
//2.判断交易是否失败
if(resultMap == null){
return R.error().message("支付出错");
}
//3.判断交易成功
if("SUCCESS".equals(resultMap.get("trade_state"))){
//交易成功,更新交易记录 (根据对外交易编号查询) 存储完整报文,更新订单状态
String out_trade_no = resultMap.get("out_trade_no");
paymentService.paySuccess(out_trade_no, PaymentTypeEnum.WEIXIN.getStatus(), resultMap);
return R.ok().message("支付成功");
}
//4.支付中
return R.ok().message("支付中");
}
2. service
(1) weixinService 微信查询交易状态
//根据参数查询交易状态
@Override
public Map queryPayStatus(Long orderId, String paymentType) {
try {
//1.查询订单信息
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.创建客户端对象 (设置访问url 参考文档)
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
//4.设置参数 (map=>xml)
client.setXmlParam(WXPayUtil.generateSignedXml
(paramMap, ConstantPropertiesUtils.PARTNERKEY));
//5.客户端发送请求
client.setHttps(true);
client.post();
//6.获取响应
String xml = client.getContent();
Map resultMap = WXPayUtil.xmlToMap(xml);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
return null; //在controller判断失败
}
}
(2) PaymentService 更新交易记录
//更新交易记录
@Override
public void paySuccess(String outTradeNo, Integer paymentType,
Map resultMap) {
//1.查询交易记录
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("out_trade_no",outTradeNo);
wrapper.eq("payment_type", paymentType);
PaymentInfo paymentInfo = baseMapper.selectOne(wrapper);
if(paymentInfo==null){
throw new YyghException(20001,"交易记录失效");
}
//2.判断交易记录状态 (PAID 2 已支付)
if(paymentInfo.getPaymentStatus()==
PaymentStatusEnum.PAID.getStatus()){
return;
}
//3.更新交易记录
paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getStatus());//状态
paymentInfo.setTradeNo(resultMap.get("transaction_id"));//交易编号
paymentInfo.setCallbackTime(new Date());//时间
paymentInfo.setCallbackContent(resultMap.toString());//完整报文
baseMapper.updateById(paymentInfo);
//4.更新订单状态
OrderInfo orderInfo = orderService.getById(paymentInfo.getOrderId());
orderInfo.setOrderStatus(OrderStatusEnum.PAID.getStatus()); //1 已支付
orderService.updateById(orderInfo);
//5.调用医院接口,更新订单状态 (省略)
}
(3) 前端实现
1. wx API方法
//支付后操作 更新状态
queryPayStatus(orderId) {
return request({
url: `/api/order/weixin/queryPayStatus/${orderId}`,
method: 'get'
})
}
2. 修改JS方法
/支付
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 {
//定时器,查询订单状态
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);
}
}
3. 测试
打开付款二维码后,循环查询



