半藏商城中应用消息队列的场景
1、用于解决用户下单以后,订单超时如何取消订单的问题。 - 用户进行提交订单操作(会有锁定商品库存等操作); - 生成订单,获取订单的id; - 获取到设置的订单超时时间(假设设置的为60分钟不支付取消订单); - 按订单超时时间发送一个延迟消息给RabbitMQ,让它在订单超时后触发取消订单的操作; - 如果用户没有支付,进行取消订单操作(释放锁定商品库存一系列操作)。
实现方法 - 需要一个订单延迟消息队列 以及一个取消订单消息队列, - 一旦有消息以延迟订单设置的路由键发送过来,会转发到订单延迟消息队列,并在此队列保存一定时间,等到超时后会自动将消息发送到取消订单消息消费队列。
2、短信验证码以及邮箱验证码都采用消息队列进行消费。 - 采用队列,交换机,路由键进行消费。一条队列,一个交换机,一个路由键就可以实现。
在pom.xml中添加相关依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>
修改SpringBoot配置文件
#SpringBoot配置RabbitMqrabbitmq:host: localhost # rabbitmq的连接地址port: 5672 # rabbitmq的连接端口号virtual-host: /hanzoMall # rabbitmq的虚拟hostusername: hanzoMall # rabbitmq的用户名password: hanzoMall # rabbitmq的密码publisher-confirms: true #如果对异步消息需要回调必须设置为true
消息队列的枚举配置类QueueEnum
用于延迟消息队列及处理取消订单消息队列的常量定义,包括交换机名称、队列名称、路由键名称。 以及短信发送消息队列和邮箱验证码发送消息队列的常量定义,包括交换机名称、队列名称、路由键名称。
import com.rabbitmq.client.AMQP;import lombok.Getter;/*** @Author 皓宇QAQ* @email 2469653218@qq.com* @Date 2020/5/23 21:00* @link https://github.com/Tianhaoy/hanzomall* @Description: 消息队列枚举配置*/@Getterpublic enum QueueEnum {/*** 发送短信消息通知队列*/QUEUE_SMS_SEND("mall.sms.direct", "mall.sms.send", "mall.sms.send"),/*** 发送邮件消息通知队列*/QUEUE_EMAIL_SEND("mall.email.direct", "mall.email.send", "mall.email.send"),/*** 消息通知队列* mall.order.direct(取消订单消息队列所绑定的交换机):绑定的队列为mall.order.cancel,一旦有消息以mall.order.cancel为路由键发过来,会发送到此队列。*/QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"),/*** 消息通知ttl队列* mall.order.direct.ttl(订单延迟消息队列所绑定的交换机):绑定的队列为mall.order.cancel.ttl,一旦有消息以mall.order.cancel.ttl为路由键发送过来,会转发到此队列,并在此队列保存一定时间,等到超时后会自动将消息发送到mall.order.cancel(取消订单消息消费队列)。*/QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl");/*** 交换机名称*/private String exchange;/*** 队列名称*/private String name;/*** 路由键*/private String routeKey;QueueEnum(String exchange, String name, String routeKey) {this.exchange = exchange;this.name = name;this.routeKey = routeKey;}}
添加RabbitMq的配置
用于配置交换机、队列及队列与交换机的绑定关系。
import ltd.hanzo.mall.common.QueueEnum;import org.springframework.amqp.core.*;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/*** @Author 皓宇QAQ* @email 2469653218@qq.com* @Date 2020/5/23 21:04* @link https://github.com/Tianhaoy/hanzomall* @Description: 消息队列配置*/@Configurationpublic class RabbitMqConfig {/*** 1.0发送短信消息通知队列所绑定的->交换机*/@BeanDirectExchange sendSmsDirect() {return (DirectExchange) ExchangeBuilder.directExchange(QueueEnum.QUEUE_SMS_SEND.getExchange()).durable(true).build();}/*** 1.0发送短信的->消费队列*/@Beanpublic Queue sendSmsQueue() {return new Queue(QueueEnum.QUEUE_SMS_SEND.getName());}/*** 1.0将发送短信 队列绑定到->交换机*/@BeanBinding sendSmsBinding(DirectExchange sendSmsDirect, Queue sendSmsQueue){return BindingBuilder.bind(sendSmsQueue).to(sendSmsDirect).with(QueueEnum.QUEUE_SMS_SEND.getRouteKey());}/*** 2.0发送邮件消息通知队列所绑定的->交换机*/@BeanDirectExchange sendEmailDirect() {return (DirectExchange) ExchangeBuilder.directExchange(QueueEnum.QUEUE_EMAIL_SEND.getExchange()).durable(true).build();}/*** 2.0发送邮件的->消费队列*/@Beanpublic Queue sendEmailQueue() {return new Queue(QueueEnum.QUEUE_EMAIL_SEND.getName());}/*** 2.0将发送邮件 队列绑定到->交换机*/@BeanBinding sendEmailBinding(DirectExchange sendEmailDirect, Queue sendEmailQueue){return BindingBuilder.bind(sendEmailQueue).to(sendEmailDirect).with(QueueEnum.QUEUE_EMAIL_SEND.getRouteKey());}/*** 3.0订单消息实际消费队列所绑定的->交换机*/@BeanDirectExchange orderDirect() {return (DirectExchange) ExchangeBuilder.directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange()).durable(true).build();}/*** 3.0订单实际消费队列*/@Beanpublic Queue orderQueue() {return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName());}/*** 3.0将订单队列绑定到交换机*/@BeanBinding orderBinding(DirectExchange orderDirect,Queue orderQueue){return BindingBuilder.bind(orderQueue).to(orderDirect).with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey());}/*** 4.0订单延迟队列队列所绑定的->交换机*/@BeanDirectExchange orderTtlDirect() {return (DirectExchange) ExchangeBuilder.directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange()).durable(true).build();}/*** 4.0订单延迟队列(死信队列)*/@Beanpublic Queue orderTtlQueue() {return QueueBuilder.durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName()).withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//到期后转发的交换机.withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//到期后转发的路由键.build();}/*** 4.0将订单延迟队列绑定到交换机*/@BeanBinding orderTtlBinding(DirectExchange orderTtlDirect,Queue orderTtlQueue){return BindingBuilder.bind(orderTtlQueue).to(orderTtlDirect).with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey());}}
在RabbitMQ管理页面可以看到以下交换机和队列


交换机及队列说明
- mall.order.direct(取消订单消息队列所绑定的交换机):绑定的队列为mall.order.cancel,一旦有消息以mall.order.cancel为路由键发过来,会发送到此队列。
- mall.order.direct.ttl(订单延迟消息队列所绑定的交换机):绑定的队列为mall.order.cancel.ttl,一旦有消息以mall.order.cancel.ttl为路由键发送过来,会转发到此队列,并在此队列保存一定时间,等到超时后会自动将消息发送到mall.order.cancel(取消订单消息消费队列)。
添加延迟消息的发送者CancelOrderSender
用于向订单延迟消息队列(mall.order.cancel.ttl)里发送消息。
import lombok.extern.slf4j.Slf4j;import ltd.hanzo.mall.common.QueueEnum;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.amqp.AmqpException;import org.springframework.amqp.core.AmqpTemplate;import org.springframework.amqp.core.Message;import org.springframework.amqp.core.MessagePostProcessor;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;/*** @Author 皓宇QAQ* @Date 2020/6/6 17:24* @Description:取消订单消息的发出者*/@Component@Slf4jpublic class CancelOrderSender {@Autowiredprivate AmqpTemplate amqpTemplate;public void sendMessage(String orderNo,final long delayTimes){//给延迟队列发送消息amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderNo, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {//给消息设置延迟毫秒值message.getMessageProperties().setExpiration(String.valueOf(delayTimes));return message;}});log.info("send delay message orderNo:{}",orderNo);}}
添加取消订单消息的接收者CancelOrderReceiver
用于从取消订单的消息队列(mall.order.cancel)里接收消息。
import lombok.extern.slf4j.Slf4j;import ltd.hanzo.mall.service.HanZoMallOrderService;import ltd.hanzo.mall.service.TaskService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.amqp.rabbit.annotation.RabbitHandler;import org.springframework.amqp.rabbit.annotation.RabbitListener;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;/*** @Author 皓宇QAQ* @Date 2020/6/6 17:25* @Description:取消订单消息的处理者*/@Component@RabbitListener(queues = "mall.order.cancel")@Slf4jpublic class CancelOrderReceiver {@Autowiredprivate HanZoMallOrderService hanZoMallOrderService;@Autowiredprivate TaskService taskService;@RabbitHandlerpublic void handle(String orderNo){log.info("receive delay message orderNo:{}",orderNo);hanZoMallOrderService.cancelOrder(orderNo);taskService.cancelOrderSendSimpleMail(orderNo);}}
添加HanZoMallOrderService接口
public interface HanZoMallOrderService {/*** 保存订单** @param user* @param myShoppingCartItems* @return*/String saveOrder(HanZoMallUserVO user, List<HanZoMallShoppingCartItemVO> myShoppingCartItems);/*** 取消单个超时订单*/@Transactionalvoid cancelOrder(String orderNo);}
添加HanZoMallOrderService实现类HanZoMallOrderServiceImpl
@Slf4j@Servicepublic class HanZoMallOrderServiceImpl implements HanZoMallOrderService {@Resourceprivate HanZoMallOrderMapper hanZoMallOrderMapper;@Resourceprivate HanZoMallOrderItemMapper hanZoMallOrderItemMapper;@Resourceprivate HanZoMallShoppingCartItemMapper hanZoMallShoppingCartItemMapper;@Resourceprivate HanZoMallGoodsMapper hanZoMallGoodsMapper;@Autowiredprivate CancelOrderSender cancelOrderSender;@Override@Transactionalpublic String saveOrder(HanZoMallUserVO user, List<HanZoMallShoppingCartItemVO> myShoppingCartItems) {//todo 执行一系类下单操作,代码在github中//下单完成后开启一个延迟消息,用于当用户没有付款时取消订单sendDelayMessageCancelOrder(orderNo);//所有操作成功后,将订单号返回,以供Controller方法跳转到订单详情return orderNo;}@Overridepublic void cancelOrder(String orderNo) {HanZoMallOrder hanZoMallOrder = hanZoMallOrderMapper.selectByOrderNo(orderNo);if (hanZoMallOrder != null && hanZoMallOrder.getOrderStatus() == 0) {//超时取消订单hanZoMallOrderMapper.closeOrder(Collections.singletonList(hanZoMallOrder.getOrderId()), HanZoMallOrderStatusEnum.ORDER_CLOSED_BY_EXPIRED.getOrderStatus());}}private void sendDelayMessageCancelOrder(String orderNo) {//获取订单超时时间,假设为60分钟long delayTimes = 36 * 100000;//发送延迟消息cancelOrderSender.sendMessage(orderNo, delayTimes);}
添加OrderController定义接口
import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import ltd.hanzo.mall.common.*;import ltd.hanzo.mall.controller.vo.HanZoMallOrderDetailVO;import ltd.hanzo.mall.controller.vo.HanZoMallOrderItemVO;import ltd.hanzo.mall.controller.vo.HanZoMallShoppingCartItemVO;import ltd.hanzo.mall.controller.vo.HanZoMallUserVO;import ltd.hanzo.mall.entity.HanZoMallOrder;import ltd.hanzo.mall.service.HanZoMallOrderService;import ltd.hanzo.mall.service.HanZoMallShoppingCartService;import ltd.hanzo.mall.service.MailSendService;import ltd.hanzo.mall.util.PageQueryUtil;import ltd.hanzo.mall.util.Result;import ltd.hanzo.mall.util.ResultGenerator;import org.apache.poi.xssf.streaming.SXSSFRow;import org.apache.poi.xssf.streaming.SXSSFSheet;import org.apache.poi.xssf.streaming.SXSSFWorkbook;import org.springframework.stereotype.Controller;import org.springframework.util.CollectionUtils;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.*;import org.springframework.web.servlet.mvc.support.RedirectAttributes;import javax.annotation.Resource;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.text.SimpleDateFormat;import java.util.List;import java.util.Map;@Api(tags = "OrderController", description = "用户订单")@Slf4j@Controllerpublic class OrderController {@Resourceprivate HanZoMallShoppingCartService hanZoMallShoppingCartService;@Resourceprivate HanZoMallOrderService hanZoMallOrderService;@Resourceprivate MailSendService mailSendService;@ApiOperation("某个订单信息")@GetMapping("/orders/{orderNo}")public String orderDetailPage(HttpServletRequest request, @PathVariable("orderNo") String orderNo, HttpSession httpSession) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);HanZoMallOrderDetailVO orderDetailVO = hanZoMallOrderService.getOrderDetailByOrderNo(orderNo, user.getUserId());if (orderDetailVO == null) {return "error/error_5xx";}request.setAttribute("orderDetailVO", orderDetailVO);return "mall/order-detail";}@ApiOperation("我的订单信息")@GetMapping("/orders")public String orderListPage(@RequestParam Map<String, Object> params, HttpServletRequest request, HttpSession httpSession) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);params.put("userId", user.getUserId());if (StringUtils.isEmpty(params.get("page"))) {params.put("page", 1);}params.put("limit", Constants.ORDER_SEARCH_PAGE_LIMIT);//封装我的订单数据PageQueryUtil pageUtil = new PageQueryUtil(params);request.setAttribute("orderPageResult", hanZoMallOrderService.getMyOrders(pageUtil));request.setAttribute("path", "orders");return "mall/my-orders";}@ApiOperation("提交订单")@GetMapping("/saveOrder")public String saveOrder(HttpSession httpSession) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);List<HanZoMallShoppingCartItemVO> myShoppingCartItems = hanZoMallShoppingCartService.getMyShoppingCartItems(user.getUserId());if (StringUtils.isEmpty(user.getAddress().trim())) {//无收货地址HanZoMallException.fail(ServiceResultEnum.NULL_ADDRESS_ERROR.getResult());}if (CollectionUtils.isEmpty(myShoppingCartItems)) {//购物车中无数据则跳转至错误页 可能会出现快速双击 导致两个请求 第二个请求会抛出错误HanZoMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult());}//保存订单并返回订单号String saveOrderResult = hanZoMallOrderService.saveOrder(user, myShoppingCartItems);//跳转到订单详情页return "redirect:/orders/" + saveOrderResult;}@ApiOperation("取消订单")@PutMapping("/orders/{orderNo}/cancel")@ResponseBodypublic Result cancelOrder(@PathVariable("orderNo") String orderNo, HttpSession httpSession) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);String cancelOrderResult = hanZoMallOrderService.cancelOrder(orderNo, user.getUserId());if (ServiceResultEnum.SUCCESS.getResult().equals(cancelOrderResult)) {return ResultGenerator.genSuccessResult();} else {return ResultGenerator.genFailResult(cancelOrderResult);}}@ApiOperation("确认收货")@PutMapping("/orders/{orderNo}/finish")@ResponseBodypublic Result finishOrder(@PathVariable("orderNo") String orderNo, HttpSession httpSession) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);String finishOrderResult = hanZoMallOrderService.finishOrder(orderNo, user.getUserId());if (ServiceResultEnum.SUCCESS.getResult().equals(finishOrderResult)) {return ResultGenerator.genSuccessResult();} else {return ResultGenerator.genFailResult(finishOrderResult);}}@ApiOperation("支付前验证状态")@GetMapping("/selectPayType")public String selectPayType(HttpServletRequest request, @RequestParam("orderNo") String orderNo, HttpSession httpSession) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);HanZoMallOrder hanZoMallOrder = hanZoMallOrderService.getHanZoMallOrderByOrderNo(orderNo);// 判断订单userId 验证是否是当前userId下的订单,否则报错 by thy 20200301 已添加验证是否同一用户// 判断订单状态 by thy 20200301 已添加验证状态为待支付 '0'if (user.getUserId()==hanZoMallOrder.getUserId()&&hanZoMallOrder.getOrderStatus()== HanZoMallOrderStatusEnum.ORDER_PRE_PAY.getOrderStatus()){log.debug("通过用户状态验证!");request.setAttribute("orderNo", orderNo);request.setAttribute("totalPrice", hanZoMallOrder.getTotalPrice());return "mall/pay-select";}return "error/error_5xx";}//各种支付@ApiOperation("跳转支付接口")@GetMapping("/payPage")public String payOrder(HttpServletRequest request, @RequestParam("orderNo") String orderNo, HttpSession httpSession, @RequestParam("payType") int payType, RedirectAttributes attributes) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);HanZoMallOrder hanZoMallOrder = hanZoMallOrderService.getHanZoMallOrderByOrderNo(orderNo);List<HanZoMallOrderItemVO> orderItems = hanZoMallOrderService.getOrderItems(hanZoMallOrder.getOrderId());String itemString = "";if (!CollectionUtils.isEmpty(orderItems)) {for (int i = 0; i < orderItems.size(); i++) {itemString += orderItems.get(i).getGoodsName() + " x " + orderItems.get(i).getGoodsCount() +" ";}}log.debug("订单商品详情 "+itemString);// 判断订单userId 验证是否是当前userId下的订单,否则报错 by thy 20200301 已添加验证是否同一用户// 判断订单状态 by thy 20200301 已添加验证状态为待支付 '0'if (user.getUserId()==hanZoMallOrder.getUserId()&&hanZoMallOrder.getOrderStatus()== HanZoMallOrderStatusEnum.ORDER_PRE_PAY.getOrderStatus()&&hanZoMallOrder.getPayStatus()==HanZoMallOrderStatusEnum.ORDER_PRE_PAY.getOrderStatus()){log.debug("通过用户状态验证!");request.setAttribute("orderNo", orderNo);request.setAttribute("totalPrice", hanZoMallOrder.getTotalPrice());if (payType == 1) {//payType == 1为支付宝支付 重定向到支付宝沙箱接口controller 进行支付//使用RedirectAttributes 拼key和value 自动帮拼到url上 不会出现中文乱码attributes.addAttribute("orderNo", orderNo);attributes.addAttribute("totalPrice", hanZoMallOrder.getTotalPrice());attributes.addAttribute("itemString", itemString);return "redirect:/alipay/goAlipay";} else {return "mall/wxpay";}}return "error/error_5xx";}@ApiOperation("微信支付成功确认死接口")@GetMapping("/paySuccess")@ResponseBodypublic Result paySuccess(@RequestParam("orderNo") String orderNo, @RequestParam("payType") int payType, HttpSession httpSession) {HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);String payResult = hanZoMallOrderService.paySuccess(orderNo, payType,user.getUserId());if (ServiceResultEnum.SUCCESS.getResult().equals(payResult)) {String emailAddress = user.getEmailAddress();if (!"".equals(emailAddress)){mailSendService.sendSimpleMail(emailAddress, "【半藏商城付款成功】", "您好,订单号为"+orderNo+"的订单通过微信付款成功!");}return ResultGenerator.genSuccessResult();} else {return ResultGenerator.genFailResult(payResult);}}//导出订单为Excel表格@ApiOperation("导出我的订单表格")@GetMapping("/orders/putExcel")public void ordersExcel(HttpServletResponse response,HttpSession httpSession){HanZoMallUserVO user = (HanZoMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);List<HanZoMallOrder> orderListVOS = hanZoMallOrderService.getHanZoMallOrderByUserId(user.getUserId().toString());//创建poi导出数据对象SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook();//创建sheet页SXSSFSheet sheet = sxssfWorkbook.createSheet("我的订单");//创建表头SXSSFRow headRow = sheet.createRow(0);//设置表头信息headRow.createCell(0).setCellValue("序号");headRow.createCell(1).setCellValue("订单号");headRow.createCell(2).setCellValue("商品名称");//可能有多个商品headRow.createCell(3).setCellValue("消费金额(元)");headRow.createCell(4).setCellValue("支付状态");headRow.createCell(5).setCellValue("支付类型");headRow.createCell(6).setCellValue("支付时间");headRow.createCell(7).setCellValue("订单状态");headRow.createCell(8).setCellValue("收货地址");headRow.createCell(9).setCellValue("创建时间");//序号int x= 1; //序号int j = 0;//从订单详情表中读取商品名称的标识SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//不转换Excel时间是数字// 遍历上面数据库查到的数据for (HanZoMallOrder order : orderListVOS) {//填充数据 从第二行开始String orderNo = order.getOrderNo();int TotalPrice = order.getTotalPrice();String payStatus = PayStatusEnum.getPayStatusEnumByStatus(order.getPayStatus()).getName();String PayType = PayTypeEnum.getPayTypeEnumByType(order.getPayType()).getName();String orderStatus = HanZoMallOrderStatusEnum.getHanZoMallOrderStatusEnumByStatus(order.getOrderStatus()).getName();String userAddress = order.getUserAddress();String patTime = "";String createTime = "";String goodsNames = "";if (order.getPayTime()!=null && !"".equals(order.getPayTime())){patTime = sdf.format(order.getPayTime());}else {patTime = "无";}if (order.getCreateTime()!=null && !"".equals(order.getCreateTime())){createTime = sdf.format(order.getCreateTime());}else {createTime = "无";}//还需要查tb_xxx_mall_order_item表读取订单所包含的商品名称 可能有多个List<HanZoMallOrderItemVO> orderItems = hanZoMallOrderService.getOrderItems(orderListVOS.get(j).getOrderId());if (orderItems!=null){StringBuffer sb = new StringBuffer();for (int k=0;k<orderItems.size();k++){sb.append(orderItems.get(k).getGoodsName() + " x " +orderItems.get(k).getGoodsCount()+" ");}goodsNames = sb.toString();}SXSSFRow dataRow = sheet.createRow(sheet.getLastRowNum() + 1);//开始填充dataRow.createCell(0).setCellValue(x);dataRow.createCell(1).setCellValue(orderNo);dataRow.createCell(2).setCellValue(goodsNames);dataRow.createCell(3).setCellValue(TotalPrice);dataRow.createCell(4).setCellValue(payStatus);dataRow.createCell(5).setCellValue(PayType);dataRow.createCell(6).setCellValue(patTime);dataRow.createCell(7).setCellValue(orderStatus);dataRow.createCell(8).setCellValue(userAddress);dataRow.createCell(9).setCellValue(createTime);x++;//序号自增j++;//查tb_xxx_mall_order_item 根据的orderId自增}try {// 下载导出String filename = "半藏商城订单明细";//filename = URLEncoder.encode(filename, "UTF-8");try {//避免文件名中文乱码,将UTF8打散重组成ISO-8859-1编码方式filename = new String (filename.getBytes("UTF8"),"ISO-8859-1");} catch (UnsupportedEncodingException e) {e.printStackTrace();}// 设置头信息response.setCharacterEncoding("UTF-8");response.setContentType("application/vnd.ms-excel");//让浏览器下载文件,name是上述默认文件下载名response.setHeader("Content-Disposition", "attachment;filename=" + filename+ ".xlsx");//创建一个输出流ServletOutputStream outputStream = response.getOutputStream();//写入数据sxssfWorkbook.write(outputStream);// 关闭outputStream.close();sxssfWorkbook.close();}catch (IOException e){log.error("导出我的订单报表出错");e.printStackTrace();}}}
https://github.com/Tianhaoy/hanzomall
