一、结算页面显示订单信息
1、加入html页面
2、改造getOrderInfo.html
3、orderController.js
//定义控制器
app.controller("orderController", function($scope, cartService, addressService, orderService) {
/**
* 页面加载展示购物车中数据
*/
$scope.findCartList = function(){
cartService.findCartList().success(function(data){
$scope.cartList = data;
//计算页面需要展示的其他数据
$scope.totalNum = 0; //总数量
$scope.totalFee = 0.00; //总价格
for (var i = 0; i < data.length; i++) {
var orderItemList = data[i].orderItems;
for (var j = 0; j < orderItemList.length; j++) {
$scope.totalNum += orderItemList[j].num;
$scope.totalFee += orderItemList[j].totalFee;
}
}
});
}
//初始化选中的收货人地址
$scope.selectAddress = null;
/**
* 查询用户地址列表
*/
$scope.findAddressByUser = function(){
addressService.findAddressByUser().success(function(data){
$scope.addressList = data;
//设置默认选中的地址
for (var i = 0; i < data.length; i++) {
if(data[i].isDefault == '1'){
$scope.selectAddress = data[i];
break;
}
}
if($scope.selectAddress == null && data.length > 0){
$scope.selectAddress = data[0];
}
});
};
/**
* 判断当前地址是否是默认地址
*/
$scope.isSelectedAddress = function(pojo){
if($scope.selectAddress.id == pojo.id){
return true;
}else{
return false;
}
}
/**
* 更改收货地址
*/
$scope.changeAddress = function(pojo){
$scope.selectAddress = pojo;
}
});
4、addressService.js
//服务层
app.service('addressService',function($http){
//根据用户获取地址列表
this.findAddressByUser = function(){
return $http.get("../address/findAddressByUser.do");
}
});
5、AddressController.java
package com.pinyougou.cart.controller;
import java.util.List;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.cart.service.AddressService;
import com.pinyougou.pojo.TbAddress;
/**
* controller
*
* @author Administrator
*
*/
@RestController
@RequestMapping("/address")
public class AddressController {
@Reference
private AddressService addressService;
/**
* 返回当前登录人地址列表
*
* @return
*/
@RequestMapping("findAddressByUser")
public List<TbAddress> findAddressByUser() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
return addressService.findAddressByUser(username);
}
}
6、AddressServiceImpl.java
package com.pinyougou.cart.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.cart.service.AddressService;
import com.pinyougou.mapper.TbAddressMapper;
import com.pinyougou.pojo.TbAddress;
import com.pinyougou.pojo.TbAddressExample;
/**
* 服务实现层
*
* @author Administrator
*
*/
@Service
public class AddressServiceImpl implements AddressService {
@Autowired
private TbAddressMapper addressMapper;
public List<TbAddress> findAddressByUser(String username) {
TbAddressExample example = new TbAddressExample();
example.createCriteria().andUserIdEqualTo(username);
return addressMapper.selectByExample(example);
}
}
二、结算按钮提交订单操作的数据准备以及js方法
1、getOrderInfo.html
2、orderController.js
//初始化订单信息
$scope.entity = {paymentType:'1', sourceType:'2'};
/**
* 修改付款方式的方法
*/
$scope.changePaymentType = function(paymentType){
$scope.entity['paymentType'] = paymentType;
}
/**
* 提交订单
*/
$scope.saveOrder = function(){
$scope.entity['receiverAreaName'] = $scope.selectAddress.address;
$scope.entity['receiverMobile'] = $scope.selectAddress.mobile;
$scope.entity['receiver'] = $scope.selectAddress.contact;
orderService.add($scope.entity).success(function(data){
if(data.success){
//跳转到支付页面
}else{
alert(data.message);
}
});
}
3、orderService.js
/*用户的service层代码*/
app.service("orderService", function($http){
//保存订单
this.add = function(order){
return $http.post("./order/saveOrder.do", order);
}
});
三、搭建订单工程,实现提交订单(结算)后保存订单功能
1、搭建pinyougou-order-interface、order-service
项目搭建参考cart模块搭建过程以及配置
2、common项目中加入生成id的工具类
package utils;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
/**
* <p>名称:IdWorker.java</p>
* <p>描述:分布式自增长ID</p>
* <pre>
* Twitter的 Snowflake JAVA实现方案
* </pre>
* 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
* 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
* 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
* 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
* 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
* 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
* <p>
* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
*
* @author Polim
*/
public class IdWorker {
// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
private final static long twepoch = 1288834974657L;
// 机器标识位数
private final static long workerIdBits = 5L;
// 数据中心标识位数
private final static long datacenterIdBits = 5L;
// 机器ID最大值
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 数据中心ID最大值
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 毫秒内自增位
private final static long sequenceBits = 12L;
// 机器ID偏左移12位
private final static long workerIdShift = sequenceBits;
// 数据中心ID左移17位
private final static long datacenterIdShift = sequenceBits + workerIdBits;
// 时间毫秒左移22位
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
/* 上次生产id时间戳 */
private static long lastTimestamp = -1L;
// 0,并发控制
private long sequence = 0L;
private final long workerId;
// 数据标识id部分
private final long datacenterId;
public IdWorker(){
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/**
* @param workerId
* 工作机器ID
* @param datacenterId
* 序列号
*/
public IdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 获取下一个ID
*
* @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
// 当前毫秒内,则+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 当前毫秒内计数满了,则等待下一秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// ID偏移组合生成最终的ID,并返回ID
long nextId = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
return nextId;
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
/**
* <p>
* 获取 maxWorkerId
* </p>
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuffer mpid = new StringBuffer();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (!name.isEmpty()) {
/*
* GET jvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
* MAC + PID 的 hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/**
* <p>
* 数据标识id部分
* </p>
*/
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
id = ((0x000000FF & (long) mac[mac.length - 1])
| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
} catch (Exception e) {
System.out.println(" getDatacenterId: " + e.getMessage());
}
return id;
}
}
3、在common的spring配置文件中初始化工具类
<!-- 初始化雪花算法的工具类 -->
<bean id="idWorker" class="utils.IdWorker"></bean>
4、OrderController.java
package com.pinyougou.cart.controller;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.order.service.OrderService;
import com.pinyougou.pojo.TbOrder;
import com.resultentity.ResultMessage;
@RestController
@RequestMapping("/order")
public class OrderController {
@Reference
private OrderService orderService;
@RequestMapping("saveOrder")
public ResultMessage saveOrder(@RequestBody TbOrder order) {
//获取登录人id
String userId = SecurityContextHolder.getContext().getAuthentication().getName();
try {
order.setUserId(userId);
orderService.saveOrder(order);
return new ResultMessage(true, "");
} catch (Exception e) {
e.printStackTrace();
return new ResultMessage(false, "订单提交失败!!!");
}
}
}
5、OrderServiceImpl.java
package com.pinyougou.order.service.impl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.mapper.TbOrderItemMapper;
import com.pinyougou.mapper.TbOrderMapper;
import com.pinyougou.order.service.OrderService;
import com.pinyougou.pojo.TbOrder;
import com.pinyougou.pojo.TbOrderItem;
import com.resultentity.Cart;
import utils.IdWorker;
@Service
@Transactional
public class OrderServiceImpl implements OrderService {
@Autowired
private IdWorker idWork;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private TbOrderItemMapper orderItemMapper;
@Autowired
private TbOrderMapper orderMapper;
public void saveOrder(TbOrder order) {
// 从Redis中查询购物车信息
List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());
for (Cart cart : cartList) {
// 封装order数据
TbOrder tbOrder = new TbOrder();
// 使用雪花算法生成id
long orderId = idWork.nextId();
// 将原order中的数据移植到新的TBOrder对象中
tbOrder.setPaymentType(order.getPaymentType());
tbOrder.setReceiverAreaName(order.getReceiverAreaName());
tbOrder.setReceiverMobile(order.getReceiverMobile());
tbOrder.setSourceType(order.getSourceType());
// 封装其他数据
tbOrder.setOrderId(orderId);
tbOrder.setStatus("1");
tbOrder.setCreateTime(new Date());
tbOrder.setUpdateTime(tbOrder.getCreateTime());
tbOrder.setSellerId(cart.getSellerId());
List<TbOrderItem> orderItems = cart.getOrderItems();
// 定义订单总价格
double payment = 0.00;
for (TbOrderItem orderItem : orderItems) {
orderItem.setId(idWork.nextId());
orderItem.setOrderId(orderId);
payment += orderItem.getTotalFee().doubleValue();
orderItemMapper.insert(orderItem);
}
tbOrder.setPayment(new BigDecimal(payment));
orderMapper.insert(tbOrder);
}
// 订单生成之后删除redis中的购物车数据
redisTemplate.boundHashOps("cartList").delete(order.getUserId());
}
}
四、二维码插件使用
1、二维码优势
信息容量大, 可以容纳多达1850个大写字母或2710个数字或500多个汉字
应用范围广, 支持文字,声音,图片,指纹等等...
容错能力强, 即使图片出现部分破损也能使用
成本低, 容易制作
2、二维码容错级别
L级(低) 7%的码字可以被恢复。
M级(中) 的码字的15%可以被恢复。
Q级(四分)的码字的25%可以被恢复。
H级(高) 的码字的30%可以被恢复。
3、二维码生成插件--qrious
qrious.js二维码插件的可用配置参数如下:
4、二维码生成demo
<html>
<head>
<title>二维码入门小demo</title>
</head>
<body>
<img id="qrious">
<script src="qrious.min.js"></script>
<script>
//js代码必须在<img>标签之下
var qr = new QRious({
element:document.getElementById('qrious'),
size:250,
level:'H',
value:'http://www.itcast.cn'
});
</script>
</body>
</html>
五、微信支付流程简介
1、微信支付
开发文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
六、支付系统准备工作
1、搭建支付系统maven module -- interface、service、web
interface -- jar
service -- war
web -- war
2、配置文件以及静态资源
(1)、配置文件及静态资源参考cart系统的配置
(2)、service的pom.xml中加入微信支付的jar包坐标
<!-- 导入微信支付需要的jar包坐标 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
(3)、web项目中的applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 设置不拦截页面 -->
<http pattern="/register.html" security="none"></http>
<http pattern="/assets/**" security="none"></http>
<http pattern="/css/**" security="none"></http>
<http pattern="/data/**" security="none"></http>
<http pattern="/fonts/**" security="none"></http>
<http pattern="/img/**" security="none"></http>
<http pattern="/js/**" security="none"></http>
<http pattern="/plugins/**" security="none"></http>
<http pattern="/favicon.ico" security="none"></http>
<!-- entry-point-ref 入口点引用 -->
<http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
<intercept-url pattern="/**" access="ROLE_USER" />
<csrf disabled="true" />
<!--
custom-filter为过滤器, position 表示将过滤器放在指定的位置上,
before表示放在指定位置之前 ,
after表示放在指定的位置之后
-->
<custom-filter ref="casAuthenticationFilter" position="CAS_FILTER" />
<custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER" />
<custom-filter ref="singleLogoutFilter" before="CAS_FILTER" />
</http>
<!-- CAS入口点 开始 -->
<beans:bean id="casProcessingFilterEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<!-- 单点登录服务器登录URL -->
<beans:property name="loginUrl" value="http://cas.pinyougou.com/cas/login" />
<beans:property name="serviceProperties" ref="serviceProperties" />
</beans:bean>
<beans:bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<!--service 配置自身工程的根地址+/login/cas -->
<beans:property name="service" value="http://pay.pinyougou.com/login/cas" />
</beans:bean>
<!-- CAS入口点 结束 -->
<!-- 认证过滤器 开始 -->
<beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<!-- 认证管理器 -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="casAuthenticationProvider">
</authentication-provider>
</authentication-manager>
<!-- 认证提供者 -->
<beans:bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<beans:property name="authenticationUserDetailsService">
<beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:constructor-arg ref="userDetailsService" />
</beans:bean>
</beans:property>
<beans:property name="serviceProperties" ref="serviceProperties" />
<!-- ticketValidator 为票据验证器 -->
<beans:property name="ticketValidator">
<beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<beans:constructor-arg index="0" value="http://cas.pinyougou.com/cas" />
</beans:bean>
</beans:property>
<beans:property name="key" value="an_id_for_this_auth_provider_only" />
</beans:bean>
<!-- 认证类 -->
<beans:bean id="userDetailsService" class="com.pinyougou.user.service.UserDetailServiceImpl" />
<!-- 认证过滤器 结束 -->
<!-- 单点登出 开始 -->
<beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<beans:bean id="requestSingleLogoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<beans:constructor-arg value="http://cas.pinyougou.com/cas/logout?service=http://www.baidu.com" />
<beans:constructor-arg>
<beans:bean
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</beans:constructor-arg>
<beans:property name="filterProcessesUrl" value="/logout/cas" />
</beans:bean>
<!-- 单点登出 结束 -->
</beans:beans>
3、pinyougou-common中加入httpclient工具类并导入httpclient的jar包
package utils;
import java.io.IOException;
import java.security.GeneralSecurityException;
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;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
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.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
/**
* http请求客户端
*
* @author Administrator
*
*/
public class HttpClient {
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
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 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) {
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;
}
}
<!-- 导入httpclient的jar包坐标 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
七、完成订单支付的功能
1、修改order系统中遗留问题
(1)、生成订单时的日志保存操作
说明:保存订单的时候,将支付日志信息中订单相关的数据也保存在数据库中
package com.pinyougou.order.service.impl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.mapper.TbOrderItemMapper;
import com.pinyougou.mapper.TbOrderMapper;
import com.pinyougou.mapper.TbPayLogMapper;
import com.pinyougou.order.service.OrderService;
import com.pinyougou.pojo.TbOrder;
import com.pinyougou.pojo.TbOrderItem;
import com.pinyougou.pojo.TbPayLog;
import com.resultentity.Cart;
import utils.IdWorker;
@Service
@Transactional
public class OrderServiceImpl implements OrderService {
@Autowired
private IdWorker idWork;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private TbOrderItemMapper orderItemMapper;
@Autowired
private TbOrderMapper orderMapper;
@Autowired
private TbPayLogMapper payLogMapper;
public void saveOrder(TbOrder order) {
// 从Redis中查询购物车信息
List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());
// 用来拼接orderId,并放入log日志中
String orderList = "";
// 用于汇总所有订单的价格,为了放入log日志中
double totalFee = 0.00;
for (Cart cart : cartList) {
// 封装order数据
TbOrder tbOrder = new TbOrder();
// 使用雪花算法生成id
long orderId = idWork.nextId();
// 将id放入orderList中,保存在支付日志中
orderList += "," + orderId;
// 将原order中的数据移植到新的TBOrder对象中
tbOrder.setPaymentType(order.getPaymentType());
tbOrder.setReceiverAreaName(order.getReceiverAreaName());
tbOrder.setReceiverMobile(order.getReceiverMobile());
tbOrder.setSourceType(order.getSourceType());
// 封装其他数据
tbOrder.setOrderId(orderId);
tbOrder.setStatus("1");
tbOrder.setCreateTime(new Date());
tbOrder.setUpdateTime(tbOrder.getCreateTime());
tbOrder.setSellerId(cart.getSellerId());
List<TbOrderItem> orderItems = cart.getOrderItems();
// 定义订单总价格
double payment = 0.00;
for (TbOrderItem orderItem : orderItems) {
orderItem.setId(idWork.nextId());
orderItem.setOrderId(orderId);
payment += orderItem.getTotalFee().doubleValue();
orderItemMapper.insert(orderItem);
//将金额加入到支付日志信息的总价中
totalFee += payment;
}
tbOrder.setPayment(new BigDecimal(payment));
orderMapper.insert(tbOrder);
}
// 生成订单,在数据库中插入一条支付日志数据
TbPayLog payLog = new TbPayLog();
payLog.setCreateTime(new Date());
payLog.setOrderList(orderList.substring(1));
payLog.setOutTradeNo(idWork.nextId() + "");
// payLog.setPayTime(payTime);
payLog.setPayType(order.getPaymentType());
payLog.setTotalFee((long) (totalFee * 100));
payLog.setTradeState("0"); // 0未付款 1已付款
// payLog.setTransactionId(transactionId);
payLog.setUserId(order.getUserId());
payLogMapper.insert(payLog);
// 把支付日志信息放入redis缓存
redisTemplate.boundHashOps("payLog").put(order.getUserId(), payLog);
// 订单生成之后删除redis中的购物车数据
redisTemplate.boundHashOps("cartList").delete(order.getUserId());
}
}
(2)、订单生成之后跳转到支付页面操作
2、生成支付的二维码并进行支付操作
(1)、pay.html
(3)、payfaile.html
(3)、payController.js
//定义控制器
app.controller("payController", function($scope, $location, payService) {
// 进入页面生成二维码的操作
$scope.createNativeUrl = function() {
payService.createNativeUrl().success(function(data) {
$scope.resultMap = data;
// 根据返回的url生成二维码
var q = new QRious({
value : data.code_url,
element : document.getElementById("payImg"),
size : 300,
level : 'L'
});
// 二维码生成之后,轮询查看是否支付完成
$scope.queryOrder(data.out_trade_no);
});
}
// 轮询查询订单支付是否完成
$scope.queryOrder = function(out_trade_no) {
payService.queryOrder(out_trade_no).success(
function(data) {
if (data.success) {
// 支付成功,跳转到成功页面,并携带支付金额
location.href = "paysuccess.html#?totalFee="
+ $scope.resultMap.total_fee;
} else {
if (data.message == "支付失败") {
location.href = "payfail.html";
} else if (data.message == "二维码失效") {
// 重新生成支付的二维码
$scope.createNativeUrl();
}
}
});
}
// 获取支付总金额的方法
$scope.getMoney = function() {
// 获取其他页面请求参数
return $location.search()['totalFee'];
}
});
(4)、payService.js
/*用户的service层代码*/
app.service("payService", function($http) {
// 商城二维码操作
this.createNativeUrl = function() {
return $http.get("./pay/createNativeUrl.do");
}
// 轮询支付状态操作
this.queryOrder = function(out_trade_no) {
return $http.get("./pay/queryOrder.do?out_trade_no=" + out_trade_no);
}
});
(5)、PayController.java
package com.pinyougou.pay.controller;
import java.util.Map;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.pay.service.PayService;
import com.resultentity.ResultMessage;
@RestController
@RequestMapping("/pay")
public class PayController {
@Reference
private PayService payService;
/**
* 生成二维吗
* @return
*/
@RequestMapping("/createNativeUrl")
public Map<String, String> createNativeUrl() {
//获取当前登录人信息
String userId = SecurityContextHolder.getContext().getAuthentication().getName();
Map<String, String> map = payService.createNativeUrl(userId);
return map;
}
@RequestMapping("/queryOrder")
public ResultMessage queryOrder(String out_trade_no) {
//规定查询30分钟,30分钟之后,二维码失效
int times = 1;
while(true) {
Map<String, String> resultMap = payService.queryOrder(out_trade_no);
//判断支付状态
if(resultMap == null) {
return new ResultMessage(false, "支付失败");
}else if(resultMap.get("trade_state").equals("SUCCESS")) {
//支付成功,修改日志表
String transaction_id = resultMap.get("transaction_id");
payService.updatePayLog(out_trade_no, transaction_id);
return new ResultMessage(true, "支付成功");
}else if(resultMap.get("trade_state").equals("NOTPAY")) {
//支付未完成,whileTrue继续重复查询操作
}
try {
//3秒查询一次
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
times ++;
if(times > 600) {
return new ResultMessage(false, "二维码失效");
}
}
}
}
(6)、PayServiceImpl.java
package com.pinyougou.pay.service.impl;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import com.alibaba.dubbo.config.annotation.Service;
import com.github.wxpay.sdk.WXPayUtil;
import com.pinyougou.mapper.TbOrderMapper;
import com.pinyougou.mapper.TbPayLogMapper;
import com.pinyougou.pay.service.PayService;
import com.pinyougou.pojo.TbOrder;
import com.pinyougou.pojo.TbPayLog;
import utils.HttpClient;
import utils.IdWorker;
@Service
public class PayServiceImpl implements PayService {
@Value("${httpClientUrl}")
private String httpClientUrl;
@Value("${appid}")
private String appid;
@Value("${partner}")
private String partner;
@Value("${partnerkey}")
private String partnerkey;
@Value("${notifyurl}")
private String notifyurl;
@Autowired
private IdWorker idWorker;
@Autowired
private RedisTemplate redisTemplate;
/**
* 生成二维码
*/
public Map<String, String> createNativeUrl(String userId) {
try {
// 从redis中获取用户支付日志信息
TbPayLog payLog = (TbPayLog) redisTemplate.boundHashOps("payLog").get(userId);
// 获取商户订单号
String out_trade_no = payLog.getOutTradeNo();
HttpClient httpClient = new HttpClient(httpClientUrl);
Map<String, String> paramMap = new HashMap<String, String>();
// 公众账号ID appid
paramMap.put("appid", appid);
// 商户号 mch_id
paramMap.put("mch_id", partner);
// 随机字符串 nonce_str
paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
// 商品描述 body
paramMap.put("body", "品优购支付");
// 商户订单号 out_trade_no
System.out.println("商户订单号 :" + out_trade_no);
paramMap.put("out_trade_no", out_trade_no);
// 标价金额 total_fee,单位是分,从payLog中获取
// paramMap.put("total_fee", payLog.getTotalFee() + "");
paramMap.put("total_fee", "1");
// 终端IP spbill_create_ip
paramMap.put("spbill_create_ip", "127.0.0.1");
// 通知地址 notify_url
paramMap.put("notify_url", notifyurl);
// 交易类型 trade_type
paramMap.put("trade_type", "NATIVE");
// 生成xml字符串 顺便生成了签名 sign
String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
// 设置参数
httpClient.setXmlParam(xmlParam);
// 发送请求
httpClient.post();
// 获取返回值
String content = httpClient.getContent();
// xml转换为map
Map<String, String> resultMap = WXPayUtil.xmlToMap(content);
// 返回结果集中放入商户订单号
resultMap.put("out_trade_no", out_trade_no + "");
// 返回结果集中放入支付金额
resultMap.put("total_fee", payLog.getTotalFee() + "");
System.out.println("支付调用生成url的结果:" + resultMap);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 查询订单支付状态
*/
public Map<String, String> queryOrder(String outTradeNo) {
try {
HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
Map<String, String> paramMap = new HashMap<String, String>();
// 公众账号ID appid
paramMap.put("appid", appid);
// 商户号 mch_id
paramMap.put("mch_id", partner);
// 随机字符串 nonce_str
paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
// 商户订单号 out_trade_no
paramMap.put("out_trade_no", outTradeNo + "");
// 生成xml字符串 顺便生成了签名 sign
String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
httpClient.setXmlParam(xmlParam);
httpClient.post();
String content = httpClient.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(content);
System.out.println("调用支付状态生成的结果:" + resultMap);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Autowired
private TbPayLogMapper payLogMapper;
@Autowired
private TbOrderMapper orderMapper;
/**
* 修改支付日志
*/
public void updatePayLog(String outTradeNo, String transaction_id) {
// 更新payLog
TbPayLog payLog = payLogMapper.selectByPrimaryKey(outTradeNo);
payLog.setPayTime(new Date());
payLog.setTradeState("1");
payLog.setTransactionId(transaction_id);
payLogMapper.updateByPrimaryKey(payLog);
// 更新order
String[] orderIds = payLog.getOrderList().split(",");
for (String orderId : orderIds) {
TbOrder tbOrder = orderMapper.selectByPrimaryKey(Long.parseLong(orderId));
tbOrder.setPaymentTime(new Date());
tbOrder.setStatus("2"); // 代表已付款
tbOrder.setUpdateTime(new Date());
orderMapper.updateByPrimaryKey(tbOrder);
}
// 把redis中此用户的支付日志清空
redisTemplate.boundHashOps("payLog").delete(payLog.getUserId());
}
}