diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/OrderJob.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/OrderJob.java index 16a354ae..30f7b01a 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/OrderJob.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/OrderJob.java @@ -5,15 +5,14 @@ import org.apache.commons.logging.LogFactory; import org.linlinjava.litemall.db.domain.LitemallGoodsProduct; import org.linlinjava.litemall.db.domain.LitemallOrder; import org.linlinjava.litemall.db.domain.LitemallOrderGoods; -import org.linlinjava.litemall.db.service.*; +import org.linlinjava.litemall.db.service.LitemallGoodsProductService; +import org.linlinjava.litemall.db.service.LitemallOrderGoodsService; +import org.linlinjava.litemall.db.service.LitemallOrderService; import org.linlinjava.litemall.db.util.OrderUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; @@ -25,9 +24,6 @@ import java.util.List; public class OrderJob { private final Log logger = LogFactory.getLog(OrderJob.class); - @Autowired - private PlatformTransactionManager txManager; - @Autowired private LitemallOrderGoodsService orderGoodsService; @Autowired @@ -46,6 +42,7 @@ public class OrderJob { * 这里可以进一步地配合用户订单查询时订单未付款检查,如果订单超时半小时则取消。 */ @Scheduled(fixedDelay = 30 * 60 * 1000) + @Transactional public void checkOrderUnpaid() { logger.info("系统开启任务检查订单是否已经超期自动取消订单"); @@ -58,35 +55,24 @@ public class OrderJob { continue; } - // 开启事务管理 - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - TransactionStatus status = txManager.getTransaction(def); - try { - // 设置订单已取消状态 - order.setOrderStatus(OrderUtil.STATUS_AUTO_CANCEL); - order.setEndTime(LocalDateTime.now()); - if (orderService.updateWithOptimisticLocker(order) == 0) { - throw new Exception("更新数据已失效"); - } - - // 商品货品数量增加 - Integer orderId = order.getId(); - List orderGoodsList = orderGoodsService.queryByOid(orderId); - for (LitemallOrderGoods orderGoods : orderGoodsList) { - Integer productId = orderGoods.getProductId(); - LitemallGoodsProduct product = productService.findById(productId); - Short number = orderGoods.getNumber(); - if (productService.addStock(productId, number) == 0) { - throw new Exception("商品货品库存增加失败"); - } - } - } catch (Exception ex) { - txManager.rollback(status); - logger.info("订单 ID=" + order.getId() + " 数据更新失败,放弃自动确认收货"); - return; + // 设置订单已取消状态 + order.setOrderStatus(OrderUtil.STATUS_AUTO_CANCEL); + order.setEndTime(LocalDateTime.now()); + if (orderService.updateWithOptimisticLocker(order) == 0) { + throw new RuntimeException("更新数据已失效"); + } + + // 商品货品数量增加 + Integer orderId = order.getId(); + List orderGoodsList = orderGoodsService.queryByOid(orderId); + for (LitemallOrderGoods orderGoods : orderGoodsList) { + Integer productId = orderGoods.getProductId(); + LitemallGoodsProduct product = productService.findById(productId); + Short number = orderGoods.getNumber(); + if (productService.addStock(productId, number) == 0) { + throw new RuntimeException("商品货品库存增加失败"); + } } - txManager.commit(status); logger.info("订单 ID=" + order.getId() + " 已经超期自动取消订单"); } } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminGoodsService.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminGoodsService.java new file mode 100644 index 00000000..e0ceaf8d --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminGoodsService.java @@ -0,0 +1,333 @@ +package org.linlinjava.litemall.admin.service; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.dao.GoodsAllinone; +import org.linlinjava.litemall.admin.util.CatVo; +import org.linlinjava.litemall.core.qcode.QCodeService; +import org.linlinjava.litemall.core.util.ResponseUtil; +import org.linlinjava.litemall.db.domain.*; +import org.linlinjava.litemall.db.service.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.linlinjava.litemall.admin.util.AdminResponseCode.GOODS_NAME_EXIST; +import static org.linlinjava.litemall.admin.util.AdminResponseCode.GOODS_UPDATE_NOT_ALLOWED; + +@Service +public class AdminGoodsService { + private final Log logger = LogFactory.getLog(AdminGoodsService.class); + + @Autowired + private LitemallGoodsService goodsService; + @Autowired + private LitemallGoodsSpecificationService specificationService; + @Autowired + private LitemallGoodsAttributeService attributeService; + @Autowired + private LitemallGoodsProductService productService; + @Autowired + private LitemallCategoryService categoryService; + @Autowired + private LitemallBrandService brandService; + @Autowired + private LitemallCartService cartService; + @Autowired + private LitemallOrderGoodsService orderGoodsService; + + @Autowired + private QCodeService qCodeService; + + public Object list(String goodsSn, String name, + Integer page, Integer limit, String sort, String order) { + List goodsList = goodsService.querySelective(goodsSn, name, page, limit, sort, order); + int total = goodsService.countSelective(goodsSn, name, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", goodsList); + + return ResponseUtil.ok(data); + } + + private Object validate(GoodsAllinone goodsAllinone) { + LitemallGoods goods = goodsAllinone.getGoods(); + String name = goods.getName(); + if (StringUtils.isEmpty(name)) { + return ResponseUtil.badArgument(); + } + String goodsSn = goods.getGoodsSn(); + if (StringUtils.isEmpty(goodsSn)) { + return ResponseUtil.badArgument(); + } + // 品牌商可以不设置,如果设置则需要验证品牌商存在 + Integer brandId = goods.getBrandId(); + if (brandId != null && brandId != 0) { + if (brandService.findById(brandId) == null) { + return ResponseUtil.badArgumentValue(); + } + } + // 分类可以不设置,如果设置则需要验证分类存在 + Integer categoryId = goods.getCategoryId(); + if (categoryId != null && categoryId != 0) { + if (categoryService.findById(categoryId) == null) { + return ResponseUtil.badArgumentValue(); + } + } + + LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); + for (LitemallGoodsAttribute attribute : attributes) { + String attr = attribute.getAttribute(); + if (StringUtils.isEmpty(attr)) { + return ResponseUtil.badArgument(); + } + String value = attribute.getValue(); + if (StringUtils.isEmpty(value)) { + return ResponseUtil.badArgument(); + } + } + + LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); + for (LitemallGoodsSpecification specification : specifications) { + String spec = specification.getSpecification(); + if (StringUtils.isEmpty(spec)) { + return ResponseUtil.badArgument(); + } + String value = specification.getValue(); + if (StringUtils.isEmpty(value)) { + return ResponseUtil.badArgument(); + } + } + + LitemallGoodsProduct[] products = goodsAllinone.getProducts(); + for (LitemallGoodsProduct product : products) { + Integer number = product.getNumber(); + if (number == null || number < 0) { + return ResponseUtil.badArgument(); + } + + BigDecimal price = product.getPrice(); + if (price == null) { + return ResponseUtil.badArgument(); + } + + String[] productSpecifications = product.getSpecifications(); + if (productSpecifications.length == 0) { + return ResponseUtil.badArgument(); + } + } + + return null; + } + + /** + * 编辑商品 + *

+ * TODO + * 目前商品修改的逻辑是 + * 1. 更新litemall_goods表 + * 2. 逻辑删除litemall_goods_specification、litemall_goods_attribute、litemall_goods_product + * 3. 添加litemall_goods_specification、litemall_goods_attribute、litemall_goods_product + *

+ * 这里商品三个表的数据采用删除再添加的策略是因为 + * 商品编辑页面,支持管理员添加删除商品规格、添加删除商品属性,因此这里仅仅更新是不可能的, + * 只能删除三个表旧的数据,然后添加新的数据。 + * 但是这里又会引入新的问题,就是存在订单商品货品ID指向了失效的商品货品表。 + * 因此这里会拒绝管理员编辑商品,如果订单或购物车中存在商品。 + * 所以这里可能需要重新设计。 + */ + @Transactional + public Object update(GoodsAllinone goodsAllinone) { + Object error = validate(goodsAllinone); + if (error != null) { + return error; + } + + LitemallGoods goods = goodsAllinone.getGoods(); + LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); + LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); + LitemallGoodsProduct[] products = goodsAllinone.getProducts(); + + Integer id = goods.getId(); + // 检查是否存在购物车商品或者订单商品 + // 如果存在则拒绝修改商品。 + if (orderGoodsService.checkExist(id)) { + return ResponseUtil.fail(GOODS_UPDATE_NOT_ALLOWED, "商品已经在订单中,不能修改"); + } + if (cartService.checkExist(id)) { + return ResponseUtil.fail(GOODS_UPDATE_NOT_ALLOWED, "商品已经在购物车中,不能修改"); + } + + //将生成的分享图片地址写入数据库 + String url = qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName()); + goods.setShareUrl(url); + + // 商品基本信息表litemall_goods + if (goodsService.updateById(goods) == 0) { + throw new RuntimeException("更新数据失败"); + } + + Integer gid = goods.getId(); + specificationService.deleteByGid(gid); + attributeService.deleteByGid(gid); + productService.deleteByGid(gid); + + // 商品规格表litemall_goods_specification + for (LitemallGoodsSpecification specification : specifications) { + specification.setGoodsId(goods.getId()); + specificationService.add(specification); + } + + // 商品参数表litemall_goods_attribute + for (LitemallGoodsAttribute attribute : attributes) { + attribute.setGoodsId(goods.getId()); + attributeService.add(attribute); + } + + // 商品货品表litemall_product + for (LitemallGoodsProduct product : products) { + product.setGoodsId(goods.getId()); + productService.add(product); + } + qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName()); + + return ResponseUtil.ok(); + } + + @Transactional + public Object delete(LitemallGoods goods) { + Integer id = goods.getId(); + if (id == null) { + return ResponseUtil.badArgument(); + } + + Integer gid = goods.getId(); + goodsService.deleteById(gid); + specificationService.deleteByGid(gid); + attributeService.deleteByGid(gid); + productService.deleteByGid(gid); + return ResponseUtil.ok(); + } + + @Transactional + public Object create(GoodsAllinone goodsAllinone) { + Object error = validate(goodsAllinone); + if (error != null) { + return error; + } + + LitemallGoods goods = goodsAllinone.getGoods(); + LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); + LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); + LitemallGoodsProduct[] products = goodsAllinone.getProducts(); + + String name = goods.getName(); + if (goodsService.checkExistByName(name)) { + return ResponseUtil.fail(GOODS_NAME_EXIST, "商品名已经存在"); + } + + // 商品基本信息表litemall_goods + goodsService.add(goods); + + //将生成的分享图片地址写入数据库 + String url = qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName()); + if (!StringUtils.isEmpty(url)) { + goods.setShareUrl(url); + if (goodsService.updateById(goods) == 0) { + throw new RuntimeException("更新数据失败"); + } + } + + // 商品规格表litemall_goods_specification + for (LitemallGoodsSpecification specification : specifications) { + specification.setGoodsId(goods.getId()); + specificationService.add(specification); + } + + // 商品参数表litemall_goods_attribute + for (LitemallGoodsAttribute attribute : attributes) { + attribute.setGoodsId(goods.getId()); + attributeService.add(attribute); + } + + // 商品货品表litemall_product + for (LitemallGoodsProduct product : products) { + product.setGoodsId(goods.getId()); + productService.add(product); + } + return ResponseUtil.ok(); + } + + public Object list2() { + // http://element-cn.eleme.io/#/zh-CN/component/cascader + // 管理员设置“所属分类” + List l1CatList = categoryService.queryL1(); + List categoryList = new ArrayList<>(l1CatList.size()); + + for (LitemallCategory l1 : l1CatList) { + CatVo l1CatVo = new CatVo(); + l1CatVo.setValue(l1.getId()); + l1CatVo.setLabel(l1.getName()); + + List l2CatList = categoryService.queryByPid(l1.getId()); + List children = new ArrayList<>(l2CatList.size()); + for (LitemallCategory l2 : l2CatList) { + CatVo l2CatVo = new CatVo(); + l2CatVo.setValue(l2.getId()); + l2CatVo.setLabel(l2.getName()); + children.add(l2CatVo); + } + l1CatVo.setChildren(children); + + categoryList.add(l1CatVo); + } + + // http://element-cn.eleme.io/#/zh-CN/component/select + // 管理员设置“所属品牌商” + List list = brandService.all(); + List> brandList = new ArrayList<>(l1CatList.size()); + for (LitemallBrand brand : list) { + Map b = new HashMap<>(2); + b.put("value", brand.getId()); + b.put("label", brand.getName()); + brandList.add(b); + } + + Map data = new HashMap<>(); + data.put("categoryList", categoryList); + data.put("brandList", brandList); + return ResponseUtil.ok(data); + } + + public Object detail(Integer id) { + LitemallGoods goods = goodsService.findById(id); + List products = productService.queryByGid(id); + List specifications = specificationService.queryByGid(id); + List attributes = attributeService.queryByGid(id); + + Integer categoryId = goods.getCategoryId(); + LitemallCategory category = categoryService.findById(categoryId); + Integer[] categoryIds = new Integer[]{}; + if (category != null) { + Integer parentCategoryId = category.getPid(); + categoryIds = new Integer[]{parentCategoryId, categoryId}; + } + + Map data = new HashMap<>(); + data.put("goods", goods); + data.put("specifications", specifications); + data.put("products", products); + data.put("attributes", attributes); + data.put("categoryIds", categoryIds); + + return ResponseUtil.ok(data); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java new file mode 100644 index 00000000..28b941e2 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java @@ -0,0 +1,246 @@ +package org.linlinjava.litemall.admin.service; + +import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; +import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.core.notify.NotifyService; +import org.linlinjava.litemall.core.notify.NotifyType; +import org.linlinjava.litemall.core.util.JacksonUtil; +import org.linlinjava.litemall.core.util.ResponseUtil; +import org.linlinjava.litemall.db.domain.LitemallComment; +import org.linlinjava.litemall.db.domain.LitemallOrder; +import org.linlinjava.litemall.db.domain.LitemallOrderGoods; +import org.linlinjava.litemall.db.domain.UserVo; +import org.linlinjava.litemall.db.service.*; +import org.linlinjava.litemall.db.util.OrderUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.linlinjava.litemall.admin.util.AdminResponseCode.*; + +@Service + +public class AdminOrderService { + private final Log logger = LogFactory.getLog(AdminOrderService.class); + + @Autowired + private LitemallOrderGoodsService orderGoodsService; + @Autowired + private LitemallOrderService orderService; + @Autowired + private LitemallGoodsProductService productService; + @Autowired + private LitemallUserService userService; + @Autowired + private LitemallCommentService commentService; + @Autowired + private WxPayService wxPayService; + @Autowired + private NotifyService notifyService; + + public Object list(Integer userId, String orderSn, List orderStatusArray, + Integer page, Integer limit, String sort, String order) { + List orderList = orderService.querySelective(userId, orderSn, orderStatusArray, page, limit, sort, order); + int total = orderService.countSelective(userId, orderSn, orderStatusArray, page, limit, sort, order); + + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", orderList); + + return ResponseUtil.ok(data); + } + + public Object detail(Integer id) { + LitemallOrder order = orderService.findById(id); + List orderGoods = orderGoodsService.queryByOid(id); + UserVo user = userService.findUserVoById(order.getUserId()); + Map data = new HashMap<>(); + data.put("order", order); + data.put("orderGoods", orderGoods); + data.put("user", user); + + return ResponseUtil.ok(data); + } + + /** + * 订单退款 + *

+ * 1. 检测当前订单是否能够退款; + * 2. 微信退款操作; + * 3. 设置订单退款确认状态; + * 4. 订单商品库存回库。 + *

+ * TODO + * 虽然接入了微信退款API,但是从安全角度考虑,建议开发者删除这里微信退款代码,采用以下两步走步骤: + * 1. 管理员登录微信官方支付平台点击退款操作进行退款 + * 2. 管理员登录litemall管理后台点击退款操作进行订单状态修改和商品库存回库 + * + * @param body 订单信息,{ orderId:xxx } + * @return 订单退款操作结果 + */ + @Transactional + public Object refund(String body) { + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + String refundMoney = JacksonUtil.parseString(body, "refundMoney"); + if (orderId == null) { + return ResponseUtil.badArgument(); + } + if (StringUtils.isEmpty(refundMoney)) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgument(); + } + + if (order.getActualPrice().compareTo(new BigDecimal(refundMoney)) != 0) { + return ResponseUtil.badArgumentValue(); + } + + // 如果订单不是退款状态,则不能退款 + if (!order.getOrderStatus().equals(OrderUtil.STATUS_REFUND)) { + return ResponseUtil.fail(ORDER_CONFIRM_NOT_ALLOWED, "订单不能确认收货"); + } + + // 微信退款 + WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest(); + wxPayRefundRequest.setOutTradeNo(order.getOrderSn()); + wxPayRefundRequest.setOutRefundNo("refund_" + order.getOrderSn()); + // 元转成分 + Integer totalFee = order.getActualPrice().multiply(new BigDecimal(100)).intValue(); + wxPayRefundRequest.setTotalFee(totalFee); + wxPayRefundRequest.setRefundFee(totalFee); + + WxPayRefundResult wxPayRefundResult = null; + try { + wxPayRefundResult = wxPayService.refund(wxPayRefundRequest); + } catch (WxPayException e) { + e.printStackTrace(); + return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败"); + } + if (!wxPayRefundResult.getReturnCode().equals("SUCCESS")) { + logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg()); + return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败"); + } + if (!wxPayRefundResult.getResultCode().equals("SUCCESS")) { + logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg()); + return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败"); + } + + // 设置订单取消状态 + order.setOrderStatus(OrderUtil.STATUS_REFUND_CONFIRM); + if (orderService.updateWithOptimisticLocker(order) == 0) { + throw new RuntimeException("更新数据已失效"); + } + + // 商品货品数量增加 + List orderGoodsList = orderGoodsService.queryByOid(orderId); + for (LitemallOrderGoods orderGoods : orderGoodsList) { + Integer productId = orderGoods.getProductId(); + Short number = orderGoods.getNumber(); + if (productService.addStock(productId, number) == 0) { + throw new RuntimeException("商品货品库存增加失败"); + } + } + + //TODO 发送邮件和短信通知,这里采用异步发送 + // 退款成功通知用户, 例如“您申请的订单退款 [ 单号:{1} ] 已成功,请耐心等待到账。” + // 注意订单号只发后6位 + notifyService.notifySmsTemplate(order.getMobile(), NotifyType.REFUND, new String[]{order.getOrderSn().substring(8, 14)}); + + return ResponseUtil.ok(); + } + + /** + * 发货 + * 1. 检测当前订单是否能够发货 + * 2. 设置订单发货状态 + * + * @param body 订单信息,{ orderId:xxx, shipSn: xxx, shipChannel: xxx } + * @return 订单操作结果 + * 成功则 { errno: 0, errmsg: '成功' } + * 失败则 { errno: XXX, errmsg: XXX } + */ + public Object ship(String body) { + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + String shipSn = JacksonUtil.parseString(body, "shipSn"); + String shipChannel = JacksonUtil.parseString(body, "shipChannel"); + if (orderId == null || shipSn == null || shipChannel == null) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgument(); + } + + // 如果订单不是已付款状态,则不能发货 + if (!order.getOrderStatus().equals(OrderUtil.STATUS_PAY)) { + return ResponseUtil.fail(ORDER_CONFIRM_NOT_ALLOWED, "订单不能确认收货"); + } + + order.setOrderStatus(OrderUtil.STATUS_SHIP); + order.setShipSn(shipSn); + order.setShipChannel(shipChannel); + order.setShipTime(LocalDateTime.now()); + if (orderService.updateWithOptimisticLocker(order) == 0) { + return ResponseUtil.updatedDateExpired(); + } + + //TODO 发送邮件和短信通知,这里采用异步发送 + // 发货会发送通知短信给用户: * + // "您的订单已经发货,快递公司 {1},快递单 {2} ,请注意查收" + notifyService.notifySmsTemplate(order.getMobile(), NotifyType.SHIP, new String[]{shipChannel, shipSn}); + + return ResponseUtil.ok(); + } + + + /** + * 回复订单商品 + * + * @param body 订单信息,{ orderId:xxx } + * @return 订单操作结果 + * 成功则 { errno: 0, errmsg: '成功' } + * 失败则 { errno: XXX, errmsg: XXX } + */ + public Object reply(String body) { + Integer commentId = JacksonUtil.parseInteger(body, "commentId"); + if (commentId == null || commentId == 0) { + return ResponseUtil.badArgument(); + } + // 目前只支持回复一次 + if (commentService.findById(commentId) != null) { + return ResponseUtil.fail(ORDER_REPLY_EXIST, "订单商品已回复!"); + } + String content = JacksonUtil.parseString(body, "content"); + if (StringUtils.isEmpty(content)) { + return ResponseUtil.badArgument(); + } + // 创建评价回复 + LitemallComment comment = new LitemallComment(); + comment.setType((byte) 2); + comment.setValueId(commentId); + comment.setContent(content); + comment.setUserId(0); // 评价回复没有用 + comment.setStar((short) 0); // 评价回复没有用 + comment.setHasPicture(false); // 评价回复没有用 + comment.setPicUrls(new String[]{}); // 评价回复没有用 + commentService.save(comment); + + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java index a4416716..9f8240b7 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java @@ -5,31 +5,15 @@ import org.apache.commons.logging.LogFactory; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc; import org.linlinjava.litemall.admin.dao.GoodsAllinone; -import org.linlinjava.litemall.admin.util.CatVo; -import org.linlinjava.litemall.core.qcode.QCodeService; -import org.linlinjava.litemall.core.util.ResponseUtil; +import org.linlinjava.litemall.admin.service.AdminGoodsService; import org.linlinjava.litemall.core.validator.Order; import org.linlinjava.litemall.core.validator.Sort; -import org.linlinjava.litemall.db.domain.*; -import org.linlinjava.litemall.db.service.*; +import org.linlinjava.litemall.db.domain.LitemallGoods; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.NotNull; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.linlinjava.litemall.admin.util.AdminResponseCode.GOODS_NAME_EXIST; -import static org.linlinjava.litemall.admin.util.AdminResponseCode.GOODS_UPDATE_NOT_ALLOWED; @RestController @RequestMapping("/admin/goods") @@ -38,366 +22,88 @@ public class AdminGoodsController { private final Log logger = LogFactory.getLog(AdminGoodsController.class); @Autowired - private PlatformTransactionManager txManager; - - @Autowired - private LitemallGoodsService goodsService; - @Autowired - private LitemallGoodsSpecificationService specificationService; - @Autowired - private LitemallGoodsAttributeService attributeService; - @Autowired - private LitemallGoodsProductService productService; - @Autowired - private LitemallCategoryService categoryService; - @Autowired - private LitemallBrandService brandService; - @Autowired - private LitemallCartService cartService; - @Autowired - private LitemallOrderGoodsService orderGoodsService; - - @Autowired - private QCodeService qCodeService; + private AdminGoodsService adminGoodsService; + /** + * 查询商品 + * + * @param goodsSn + * @param name + * @param page + * @param limit + * @param sort + * @param order + * @return + */ @RequiresPermissions("admin:goods:list") - @RequiresPermissionsDesc(menu={"商品管理" , "商品列表"}, button="查询") + @RequiresPermissionsDesc(menu = {"商品管理", "商品列表"}, button = "查询") @GetMapping("/list") public Object list(String goodsSn, String name, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer limit, @Sort @RequestParam(defaultValue = "add_time") String sort, @Order @RequestParam(defaultValue = "desc") String order) { - List goodsList = goodsService.querySelective(goodsSn, name, page, limit, sort, order); - int total = goodsService.countSelective(goodsSn, name, page, limit, sort, order); - Map data = new HashMap<>(); - data.put("total", total); - data.put("items", goodsList); - - return ResponseUtil.ok(data); - } - - private Object validate(GoodsAllinone goodsAllinone) { - LitemallGoods goods = goodsAllinone.getGoods(); - String name = goods.getName(); - if (StringUtils.isEmpty(name)) { - return ResponseUtil.badArgument(); - } - String goodsSn = goods.getGoodsSn(); - if (StringUtils.isEmpty(goodsSn)) { - return ResponseUtil.badArgument(); - } - // 品牌商可以不设置,如果设置则需要验证品牌商存在 - Integer brandId = goods.getBrandId(); - if (brandId != null && brandId != 0) { - if (brandService.findById(brandId) == null) { - return ResponseUtil.badArgumentValue(); - } - } - // 分类可以不设置,如果设置则需要验证分类存在 - Integer categoryId = goods.getCategoryId(); - if (categoryId != null && categoryId != 0) { - if (categoryService.findById(categoryId) == null) { - return ResponseUtil.badArgumentValue(); - } - } - - LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); - for (LitemallGoodsAttribute attribute : attributes) { - String attr = attribute.getAttribute(); - if (StringUtils.isEmpty(attr)) { - return ResponseUtil.badArgument(); - } - String value = attribute.getValue(); - if (StringUtils.isEmpty(value)) { - return ResponseUtil.badArgument(); - } - } - - LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); - for (LitemallGoodsSpecification specification : specifications) { - String spec = specification.getSpecification(); - if (StringUtils.isEmpty(spec)) { - return ResponseUtil.badArgument(); - } - String value = specification.getValue(); - if (StringUtils.isEmpty(value)) { - return ResponseUtil.badArgument(); - } - } - - LitemallGoodsProduct[] products = goodsAllinone.getProducts(); - for (LitemallGoodsProduct product : products) { - Integer number = product.getNumber(); - if (number == null || number < 0) { - return ResponseUtil.badArgument(); - } - - BigDecimal price = product.getPrice(); - if (price == null) { - return ResponseUtil.badArgument(); - } - - String[] productSpecifications = product.getSpecifications(); - if (productSpecifications.length == 0) { - return ResponseUtil.badArgument(); - } - } - - return null; + return adminGoodsService.list(goodsSn, name, page, limit, sort, order); } /** * 编辑商品 - *

- * TODO - * 目前商品修改的逻辑是 - * 1. 更新litemall_goods表 - * 2. 逻辑删除litemall_goods_specification、litemall_goods_attribute、litemall_goods_product - * 3. 添加litemall_goods_specification、litemall_goods_attribute、litemall_goods_product * - * 这里商品三个表的数据采用删除再添加的策略是因为 - * 商品编辑页面,支持管理员添加删除商品规格、添加删除商品属性,因此这里仅仅更新是不可能的, - * 只能删除三个表旧的数据,然后添加新的数据。 - * 但是这里又会引入新的问题,就是存在订单商品货品ID指向了失效的商品货品表。 - * 因此这里会拒绝管理员编辑商品,如果订单或购物车中存在商品。 - * 所以这里可能需要重新设计。 + * @param goodsAllinone + * @return */ @RequiresPermissions("admin:goods:update") - @RequiresPermissionsDesc(menu={"商品管理" , "商品列表"}, button="编辑") + @RequiresPermissionsDesc(menu = {"商品管理", "商品列表"}, button = "编辑") @PostMapping("/update") public Object update(@RequestBody GoodsAllinone goodsAllinone) { - Object error = validate(goodsAllinone); - if (error != null) { - return error; - } - - LitemallGoods goods = goodsAllinone.getGoods(); - LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); - LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); - LitemallGoodsProduct[] products = goodsAllinone.getProducts(); - - Integer id = goods.getId(); - // 检查是否存在购物车商品或者订单商品 - // 如果存在则拒绝修改商品。 - if(orderGoodsService.checkExist(id)){ - return ResponseUtil.fail(GOODS_UPDATE_NOT_ALLOWED, "商品已经在订单中,不能修改"); - } - if(cartService.checkExist(id)){ - return ResponseUtil.fail(GOODS_UPDATE_NOT_ALLOWED, "商品已经在购物车中,不能修改"); - } - - // 开启事务管理 - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - TransactionStatus status = txManager.getTransaction(def); - try { - - //将生成的分享图片地址写入数据库 - String url = qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName()); - goods.setShareUrl(url); - - // 商品基本信息表litemall_goods - if (goodsService.updateById(goods) == 0) { - throw new Exception("更新数据失败"); - } - - Integer gid = goods.getId(); - specificationService.deleteByGid(gid); - attributeService.deleteByGid(gid); - productService.deleteByGid(gid); - - // 商品规格表litemall_goods_specification - for (LitemallGoodsSpecification specification : specifications) { - specification.setGoodsId(goods.getId()); - specificationService.add(specification); - } - - // 商品参数表litemall_goods_attribute - for (LitemallGoodsAttribute attribute : attributes) { - attribute.setGoodsId(goods.getId()); - attributeService.add(attribute); - } - - // 商品货品表litemall_product - for (LitemallGoodsProduct product : products) { - product.setGoodsId(goods.getId()); - productService.add(product); - } - } catch (Exception ex) { - txManager.rollback(status); - logger.error("系统内部错误", ex); - return ResponseUtil.fail(); - } - txManager.commit(status); - - qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName()); - - return ResponseUtil.ok(); + return adminGoodsService.update(goodsAllinone); } + /** + * 删除商品 + * + * @param goods + * @return + */ @RequiresPermissions("admin:goods:delete") - @RequiresPermissionsDesc(menu={"商品管理" , "商品列表"}, button="删除") + @RequiresPermissionsDesc(menu = {"商品管理", "商品列表"}, button = "删除") @PostMapping("/delete") public Object delete(@RequestBody LitemallGoods goods) { - Integer id = goods.getId(); - if (id == null) { - return ResponseUtil.badArgument(); - } - - // 开启事务管理 - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - TransactionStatus status = txManager.getTransaction(def); - try { - - Integer gid = goods.getId(); - goodsService.deleteById(gid); - specificationService.deleteByGid(gid); - attributeService.deleteByGid(gid); - productService.deleteByGid(gid); - } catch (Exception ex) { - txManager.rollback(status); - logger.error("系统内部错误", ex); - return ResponseUtil.fail(); - } - txManager.commit(status); - return ResponseUtil.ok(); + return adminGoodsService.delete(goods); } + /** + * 添加商品 + * + * @param goodsAllinone + * @return + */ @RequiresPermissions("admin:goods:create") - @RequiresPermissionsDesc(menu={"商品管理" , "商品上架"}, button="上架") + @RequiresPermissionsDesc(menu = {"商品管理", "商品上架"}, button = "上架") @PostMapping("/create") public Object create(@RequestBody GoodsAllinone goodsAllinone) { - Object error = validate(goodsAllinone); - if (error != null) { - return error; - } - - LitemallGoods goods = goodsAllinone.getGoods(); - LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); - LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); - LitemallGoodsProduct[] products = goodsAllinone.getProducts(); - - String name = goods.getName(); - if (goodsService.checkExistByName(name)) { - return ResponseUtil.fail(GOODS_NAME_EXIST, "商品名已经存在"); - } - - // 开启事务管理 - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - TransactionStatus status = txManager.getTransaction(def); - try { - - // 商品基本信息表litemall_goods - goodsService.add(goods); - - //将生成的分享图片地址写入数据库 - String url = qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName()); - if (!StringUtils.isEmpty(url)) { - goods.setShareUrl(url); - if (goodsService.updateById(goods) == 0) { - throw new Exception("更新数据失败"); - } - } - - // 商品规格表litemall_goods_specification - for (LitemallGoodsSpecification specification : specifications) { - specification.setGoodsId(goods.getId()); - specificationService.add(specification); - } - - // 商品参数表litemall_goods_attribute - for (LitemallGoodsAttribute attribute : attributes) { - attribute.setGoodsId(goods.getId()); - attributeService.add(attribute); - } - - // 商品货品表litemall_product - for (LitemallGoodsProduct product : products) { - product.setGoodsId(goods.getId()); - productService.add(product); - } - } catch (Exception ex) { - txManager.rollback(status); - logger.error("系统内部错误", ex); - return ResponseUtil.fail(); - } - txManager.commit(status); - - return ResponseUtil.ok(); + return adminGoodsService.create(goodsAllinone); } @RequiresPermissions("admin:goods:list") - @RequiresPermissionsDesc(menu={"商品管理" , "商品列表"}, button="查询") + @RequiresPermissionsDesc(menu = {"商品管理", "商品列表"}, button = "查询") @GetMapping("/catAndBrand") public Object list2() { - // http://element-cn.eleme.io/#/zh-CN/component/cascader - // 管理员设置“所属分类” - List l1CatList = categoryService.queryL1(); - List categoryList = new ArrayList<>(l1CatList.size()); - - for (LitemallCategory l1 : l1CatList) { - CatVo l1CatVo = new CatVo(); - l1CatVo.setValue(l1.getId()); - l1CatVo.setLabel(l1.getName()); - - List l2CatList = categoryService.queryByPid(l1.getId()); - List children = new ArrayList<>(l2CatList.size()); - for (LitemallCategory l2 : l2CatList) { - CatVo l2CatVo = new CatVo(); - l2CatVo.setValue(l2.getId()); - l2CatVo.setLabel(l2.getName()); - children.add(l2CatVo); - } - l1CatVo.setChildren(children); - - categoryList.add(l1CatVo); - } - - // http://element-cn.eleme.io/#/zh-CN/component/select - // 管理员设置“所属品牌商” - List list = brandService.all(); - List> brandList = new ArrayList<>(l1CatList.size()); - for (LitemallBrand brand : list) { - Map b = new HashMap<>(2); - b.put("value", brand.getId()); - b.put("label", brand.getName()); - brandList.add(b); - } - - Map data = new HashMap<>(); - data.put("categoryList", categoryList); - data.put("brandList", brandList); - return ResponseUtil.ok(data); + return adminGoodsService.list2(); } + /** + * 商品详情 + * + * @param id + * @return + */ @RequiresPermissions("admin:goods:read") - @RequiresPermissionsDesc(menu={"商品管理" , "商品列表"}, button="编辑") + @RequiresPermissionsDesc(menu = {"商品管理", "商品列表"}, button = "编辑") @GetMapping("/detail") public Object detail(@NotNull Integer id) { - LitemallGoods goods = goodsService.findById(id); - List products = productService.queryByGid(id); - List specifications = specificationService.queryByGid(id); - List attributes = attributeService.queryByGid(id); + return adminGoodsService.detail(id); - Integer categoryId = goods.getCategoryId(); - LitemallCategory category = categoryService.findById(categoryId); - Integer[] categoryIds = new Integer[]{}; - if (category != null) { - Integer parentCategoryId = category.getPid(); - categoryIds = new Integer[]{parentCategoryId, categoryId}; - } - - Map data = new HashMap<>(); - data.put("goods", goods); - data.put("specifications", specifications); - data.put("products", products); - data.put("attributes", attributes); - data.put("categoryIds", categoryIds); - - return ResponseUtil.ok(data); } } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java index 488cdf89..567d8c34 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java @@ -1,42 +1,18 @@ package org.linlinjava.litemall.admin.web; -import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; -import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; -import com.github.binarywang.wxpay.exception.WxPayException; -import com.github.binarywang.wxpay.service.WxPayService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc; -import org.linlinjava.litemall.core.notify.NotifyService; -import org.linlinjava.litemall.core.notify.NotifyType; -import org.linlinjava.litemall.core.util.JacksonUtil; -import org.linlinjava.litemall.core.util.ResponseUtil; +import org.linlinjava.litemall.admin.service.AdminOrderService; import org.linlinjava.litemall.core.validator.Order; import org.linlinjava.litemall.core.validator.Sort; -import org.linlinjava.litemall.db.domain.LitemallComment; -import org.linlinjava.litemall.db.domain.LitemallOrder; -import org.linlinjava.litemall.db.domain.LitemallOrderGoods; -import org.linlinjava.litemall.db.domain.UserVo; -import org.linlinjava.litemall.db.service.*; -import org.linlinjava.litemall.db.util.OrderUtil; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.NotNull; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import static org.linlinjava.litemall.admin.util.AdminResponseCode.*; @RestController @RequestMapping("/admin/order") @@ -45,25 +21,22 @@ public class AdminOrderController { private final Log logger = LogFactory.getLog(AdminOrderController.class); @Autowired - private PlatformTransactionManager txManager; - - @Autowired - private LitemallOrderGoodsService orderGoodsService; - @Autowired - private LitemallOrderService orderService; - @Autowired - private LitemallGoodsProductService productService; - @Autowired - private LitemallUserService userService; - @Autowired - private LitemallCommentService commentService; - @Autowired - private WxPayService wxPayService; - @Autowired - private NotifyService notifyService; + private AdminOrderService adminOrderService; + /** + * 查询订单 + * + * @param userId + * @param orderSn + * @param orderStatusArray + * @param page + * @param limit + * @param sort + * @param order + * @return + */ @RequiresPermissions("admin:order:list") - @RequiresPermissionsDesc(menu={"商城管理" , "订单管理"}, button="查询") + @RequiresPermissionsDesc(menu = {"商城管理", "订单管理"}, button = "查询") @GetMapping("/list") public Object list(Integer userId, String orderSn, @RequestParam(required = false) List orderStatusArray, @@ -71,218 +44,60 @@ public class AdminOrderController { @RequestParam(defaultValue = "10") Integer limit, @Sort @RequestParam(defaultValue = "add_time") String sort, @Order @RequestParam(defaultValue = "desc") String order) { - List orderList = orderService.querySelective(userId, orderSn, orderStatusArray, page, limit, sort, order); - int total = orderService.countSelective(userId, orderSn, orderStatusArray, page, limit, sort, order); - - Map data = new HashMap<>(); - data.put("total", total); - data.put("items", orderList); - - return ResponseUtil.ok(data); + return adminOrderService.list(userId, orderSn, orderStatusArray, page, limit, sort, order); } + /** + * 订单详情 + * + * @param id + * @return + */ @RequiresPermissions("admin:order:read") - @RequiresPermissionsDesc(menu={"商城管理" , "订单管理"}, button="详情") + @RequiresPermissionsDesc(menu = {"商城管理", "订单管理"}, button = "详情") @GetMapping("/detail") public Object detail(@NotNull Integer id) { - LitemallOrder order = orderService.findById(id); - List orderGoods = orderGoodsService.queryByOid(id); - UserVo user = userService.findUserVoById(order.getUserId()); - Map data = new HashMap<>(); - data.put("order", order); - data.put("orderGoods", orderGoods); - data.put("user", user); - - return ResponseUtil.ok(data); + return adminOrderService.detail(id); } /** * 订单退款 - *

- * 1. 检测当前订单是否能够退款; - * 2. 微信退款操作; - * 3. 设置订单退款确认状态; - * 4. 订单商品库存回库。 - *

- * TODO - * 虽然接入了微信退款API,但是从安全角度考虑,建议开发者删除这里微信退款代码,采用以下两步走步骤: - * 1. 管理员登录微信官方支付平台点击退款操作进行退款 - * 2. 管理员登录litemall管理后台点击退款操作进行订单状态修改和商品库存回库 * - * @param body 订单信息,{ orderId:xxx } + * @param body 订单信息,{ orderId:xxx } * @return 订单退款操作结果 */ @RequiresPermissions("admin:order:refund") - @RequiresPermissionsDesc(menu={"商城管理" , "订单管理"}, button="订单退款") + @RequiresPermissionsDesc(menu = {"商城管理", "订单管理"}, button = "订单退款") @PostMapping("refund") public Object refund(@RequestBody String body) { - Integer orderId = JacksonUtil.parseInteger(body, "orderId"); - String refundMoney = JacksonUtil.parseString(body, "refundMoney"); - if (orderId == null) { - return ResponseUtil.badArgument(); - } - if(StringUtils.isEmpty(refundMoney)){ - return ResponseUtil.badArgument(); - } - - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgument(); - } - - if (order.getActualPrice().compareTo(new BigDecimal(refundMoney)) != 0) { - return ResponseUtil.badArgumentValue(); - } - - // 如果订单不是退款状态,则不能退款 - if (!order.getOrderStatus().equals(OrderUtil.STATUS_REFUND)) { - return ResponseUtil.fail(ORDER_CONFIRM_NOT_ALLOWED, "订单不能确认收货"); - } - - // 微信退款 - WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest(); - wxPayRefundRequest.setOutTradeNo(order.getOrderSn()); - wxPayRefundRequest.setOutRefundNo("refund_" + order.getOrderSn()); - // 元转成分 - Integer totalFee = order.getActualPrice().multiply(new BigDecimal(100)).intValue(); - wxPayRefundRequest.setTotalFee(totalFee); - wxPayRefundRequest.setRefundFee(totalFee); - - WxPayRefundResult wxPayRefundResult = null; - try { - wxPayRefundResult = wxPayService.refund(wxPayRefundRequest); - } catch (WxPayException e) { - e.printStackTrace(); - return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败"); - } - if(!wxPayRefundResult.getReturnCode().equals("SUCCESS")){ - logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg()); - return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败"); - } - if(!wxPayRefundResult.getResultCode().equals("SUCCESS")){ - logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg()); - return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败"); - } - - // 开启事务管理 - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - TransactionStatus status = txManager.getTransaction(def); - try { - // 设置订单取消状态 - order.setOrderStatus(OrderUtil.STATUS_REFUND_CONFIRM); - if (orderService.updateWithOptimisticLocker(order) == 0) { - throw new Exception("更新数据已失效"); - } - - // 商品货品数量增加 - List orderGoodsList = orderGoodsService.queryByOid(orderId); - for (LitemallOrderGoods orderGoods : orderGoodsList) { - Integer productId = orderGoods.getProductId(); - Short number = orderGoods.getNumber(); - if (productService.addStock(productId, number) == 0) { - throw new Exception("商品货品库存增加失败"); - } - } - } catch (Exception ex) { - txManager.rollback(status); - logger.error("系统内部错误", ex); - return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败"); - } - txManager.commit(status); - - //TODO 发送邮件和短信通知,这里采用异步发送 - // 退款成功通知用户, 例如“您申请的订单退款 [ 单号:{1} ] 已成功,请耐心等待到账。” - // 注意订单号只发后6位 - notifyService.notifySmsTemplate(order.getMobile(), NotifyType.REFUND, new String[]{order.getOrderSn().substring(8, 14)}); - - return ResponseUtil.ok(); + return adminOrderService.refund(body); } /** * 发货 - * 1. 检测当前订单是否能够发货 - * 2. 设置订单发货状态 * - * @param body 订单信息,{ orderId:xxx, shipSn: xxx, shipChannel: xxx } + * @param body 订单信息,{ orderId:xxx, shipSn: xxx, shipChannel: xxx } * @return 订单操作结果 - * 成功则 { errno: 0, errmsg: '成功' } - * 失败则 { errno: XXX, errmsg: XXX } */ @RequiresPermissions("admin:order:ship") - @RequiresPermissionsDesc(menu={"商城管理" , "订单管理"}, button="订单发货") + @RequiresPermissionsDesc(menu = {"商城管理", "订单管理"}, button = "订单发货") @PostMapping("ship") public Object ship(@RequestBody String body) { - Integer orderId = JacksonUtil.parseInteger(body, "orderId"); - String shipSn = JacksonUtil.parseString(body, "shipSn"); - String shipChannel = JacksonUtil.parseString(body, "shipChannel"); - if (orderId == null || shipSn == null || shipChannel == null) { - return ResponseUtil.badArgument(); - } - - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgument(); - } - - // 如果订单不是已付款状态,则不能发货 - if (!order.getOrderStatus().equals(OrderUtil.STATUS_PAY)) { - return ResponseUtil.fail(ORDER_CONFIRM_NOT_ALLOWED, "订单不能确认收货"); - } - - order.setOrderStatus(OrderUtil.STATUS_SHIP); - order.setShipSn(shipSn); - order.setShipChannel(shipChannel); - order.setShipTime(LocalDateTime.now()); - if (orderService.updateWithOptimisticLocker(order) == 0) { - return ResponseUtil.updatedDateExpired(); - } - - //TODO 发送邮件和短信通知,这里采用异步发送 - // 发货会发送通知短信给用户: * - // "您的订单已经发货,快递公司 {1},快递单 {2} ,请注意查收" - notifyService.notifySmsTemplate(order.getMobile(), NotifyType.SHIP, new String[]{shipChannel, shipSn}); - - return ResponseUtil.ok(); + return adminOrderService.ship(body); } /** * 回复订单商品 * - * @param body 订单信息,{ orderId:xxx } + * @param body 订单信息,{ orderId:xxx } * @return 订单操作结果 - * 成功则 { errno: 0, errmsg: '成功' } - * 失败则 { errno: XXX, errmsg: XXX } */ @RequiresPermissions("admin:order:reply") - @RequiresPermissionsDesc(menu={"商城管理" , "订单管理"}, button="订单商品回复") + @RequiresPermissionsDesc(menu = {"商城管理", "订单管理"}, button = "订单商品回复") @PostMapping("reply") public Object reply(@RequestBody String body) { - Integer commentId = JacksonUtil.parseInteger(body, "commentId"); - if (commentId == null || commentId == 0) { - return ResponseUtil.badArgument(); - } - // 目前只支持回复一次 - if (commentService.findById(commentId) != null) { - return ResponseUtil.fail(ORDER_REPLY_EXIST, "订单商品已回复!"); - } - String content = JacksonUtil.parseString(body, "content"); - if (StringUtils.isEmpty(content)) { - return ResponseUtil.badArgument(); - } - // 创建评价回复 - LitemallComment comment = new LitemallComment(); - comment.setType((byte) 2); - comment.setValueId(commentId); - comment.setContent(content); - comment.setUserId(0); // 评价回复没有用 - comment.setStar((short) 0); // 评价回复没有用 - comment.setHasPicture(false); // 评价回复没有用 - comment.setPicUrls(new String[]{}); // 评价回复没有用 - commentService.save(comment); - - return ResponseUtil.ok(); + return adminOrderService.reply(body); } } diff --git a/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java new file mode 100644 index 00000000..447c0c12 --- /dev/null +++ b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java @@ -0,0 +1,928 @@ +package org.linlinjava.litemall.wx.service; + +import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.core.express.ExpressService; +import org.linlinjava.litemall.core.express.dao.ExpressInfo; +import org.linlinjava.litemall.core.notify.NotifyService; +import org.linlinjava.litemall.core.notify.NotifyType; +import org.linlinjava.litemall.core.qcode.QCodeService; +import org.linlinjava.litemall.core.system.SystemConfig; +import org.linlinjava.litemall.core.util.DateTimeUtil; +import org.linlinjava.litemall.core.util.JacksonUtil; +import org.linlinjava.litemall.core.util.ResponseUtil; +import org.linlinjava.litemall.db.domain.*; +import org.linlinjava.litemall.db.service.*; +import org.linlinjava.litemall.db.util.CouponUserConstant; +import org.linlinjava.litemall.db.util.OrderHandleOption; +import org.linlinjava.litemall.db.util.OrderUtil; +import org.linlinjava.litemall.wx.annotation.LoginUser; +import org.linlinjava.litemall.wx.util.IpUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.util.Assert; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.NotNull; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.linlinjava.litemall.wx.util.WxResponseCode.*; + +/** + * 订单服务 + * + *

+ * 订单状态: + * 101 订单生成,未支付;102,下单后未支付用户取消;103,下单后未支付超时系统自动取消 + * 201 支付完成,商家未发货;202,订单生产,已付款未发货,但是退款取消; + * 301 商家发货,用户未确认; + * 401 用户确认收货; 402 用户没有确认收货超过一定时间,系统自动确认收货; + * + *

+ * 用户操作: + * 当101用户未付款时,此时用户可以进行的操作是取消订单,或者付款操作 + * 当201支付完成而商家未发货时,此时用户可以取消订单并申请退款 + * 当301商家已发货时,此时用户可以有确认收货的操作 + * 当401用户确认收货以后,此时用户可以进行的操作是删除订单,评价商品,或者再次购买 + * 当402系统自动确认收货以后,此时用户可以删除订单,评价商品,或者再次购买 + * + *

+ * 注意:目前不支持订单退货和售后服务 + */ +@Service +public class WxOrderService { + private final Log logger = LogFactory.getLog(WxOrderService.class); + + @Autowired + private LitemallUserService userService; + @Autowired + private LitemallOrderService orderService; + @Autowired + private LitemallOrderGoodsService orderGoodsService; + @Autowired + private LitemallAddressService addressService; + @Autowired + private LitemallCartService cartService; + @Autowired + private LitemallRegionService regionService; + @Autowired + private LitemallGoodsProductService productService; + @Autowired + private WxPayService wxPayService; + @Autowired + private NotifyService notifyService; + @Autowired + private LitemallUserFormIdService formIdService; + @Autowired + private LitemallGrouponRulesService grouponRulesService; + @Autowired + private LitemallGrouponService grouponService; + @Autowired + private QCodeService qCodeService; + @Autowired + private ExpressService expressService; + @Autowired + private LitemallCommentService commentService; + @Autowired + private LitemallCouponService couponService; + @Autowired + private LitemallCouponUserService couponUserService; + @Autowired + private CouponVerifyService couponVerifyService; + + private String detailedAddress(LitemallAddress litemallAddress) { + Integer provinceId = litemallAddress.getProvinceId(); + Integer cityId = litemallAddress.getCityId(); + Integer areaId = litemallAddress.getAreaId(); + String provinceName = regionService.findById(provinceId).getName(); + String cityName = regionService.findById(cityId).getName(); + String areaName = regionService.findById(areaId).getName(); + String fullRegion = provinceName + " " + cityName + " " + areaName; + return fullRegion + " " + litemallAddress.getAddress(); + } + + /** + * 订单列表 + * + * @param userId 用户ID + * @param showType 订单信息: + * 0,全部订单; + * 1,待付款; + * 2,待发货; + * 3,待收货; + * 4,待评价。 + * @param page 分页页数 + * @param size 分页大小 + * @return 订单列表 + */ + public Object list(Integer userId, Integer showType, Integer page, Integer size) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + + List orderStatus = OrderUtil.orderStatus(showType); + List orderList = orderService.queryByOrderStatus(userId, orderStatus); + int count = orderService.countByOrderStatus(userId, orderStatus); + + List> orderVoList = new ArrayList<>(orderList.size()); + for (LitemallOrder order : orderList) { + Map orderVo = new HashMap<>(); + orderVo.put("id", order.getId()); + orderVo.put("orderSn", order.getOrderSn()); + orderVo.put("actualPrice", order.getActualPrice()); + orderVo.put("orderStatusText", OrderUtil.orderStatusText(order)); + orderVo.put("handleOption", OrderUtil.build(order)); + + LitemallGroupon groupon = grouponService.queryByOrderId(order.getId()); + if (groupon != null) { + orderVo.put("isGroupin", true); + } else { + orderVo.put("isGroupin", false); + } + + List orderGoodsList = orderGoodsService.queryByOid(order.getId()); + List> orderGoodsVoList = new ArrayList<>(orderGoodsList.size()); + for (LitemallOrderGoods orderGoods : orderGoodsList) { + Map orderGoodsVo = new HashMap<>(); + orderGoodsVo.put("id", orderGoods.getId()); + orderGoodsVo.put("goodsName", orderGoods.getGoodsName()); + orderGoodsVo.put("number", orderGoods.getNumber()); + orderGoodsVo.put("picUrl", orderGoods.getPicUrl()); + orderGoodsVoList.add(orderGoodsVo); + } + orderVo.put("goodsList", orderGoodsVoList); + + orderVoList.add(orderVo); + } + + Map result = new HashMap<>(); + result.put("count", count); + result.put("data", orderVoList); + + return ResponseUtil.ok(result); + } + + /** + * 订单详情 + * + * @param userId 用户ID + * @param orderId 订单ID + * @return 订单详情 + */ + public Object detail(Integer userId, Integer orderId) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + + // 订单信息 + LitemallOrder order = orderService.findById(orderId); + if (null == order) { + return ResponseUtil.fail(ORDER_UNKNOWN, "订单不存在"); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.fail(ORDER_INVALID, "不是当前用户的订单"); + } + Map orderVo = new HashMap(); + orderVo.put("id", order.getId()); + orderVo.put("orderSn", order.getOrderSn()); + orderVo.put("addTime", order.getAddTime()); + orderVo.put("consignee", order.getConsignee()); + orderVo.put("mobile", order.getMobile()); + orderVo.put("address", order.getAddress()); + orderVo.put("goodsPrice", order.getGoodsPrice()); + orderVo.put("freightPrice", order.getFreightPrice()); + orderVo.put("actualPrice", order.getActualPrice()); + orderVo.put("orderStatusText", OrderUtil.orderStatusText(order)); + orderVo.put("handleOption", OrderUtil.build(order)); + orderVo.put("expCode", order.getShipChannel()); + orderVo.put("expNo", order.getShipSn()); + + List orderGoodsList = orderGoodsService.queryByOid(order.getId()); + + Map result = new HashMap<>(); + result.put("orderInfo", orderVo); + result.put("orderGoods", orderGoodsList); + + // 订单状态为已发货且物流信息不为空 + //"YTO", "800669400640887922" + if (order.getOrderStatus().equals(OrderUtil.STATUS_SHIP)) { + ExpressInfo ei = expressService.getExpressInfo(order.getShipChannel(), order.getShipSn()); + result.put("expressInfo", ei); + } + + return ResponseUtil.ok(result); + + } + + /** + * 提交订单 + *

+ * 1. 创建订单表项和订单商品表项; + * 2. 购物车清空; + * 3. 优惠券设置已用; + * 4. 商品货品库存减少; + * 5. 如果是团购商品,则创建团购活动表项。 + * + * @param userId 用户ID + * @param body 订单信息,{ cartId:xxx, addressId: xxx, couponId: xxx, message: xxx, grouponRulesId: xxx, grouponLinkId: xxx} + * @return 提交订单操作结果 + */ + @Transactional + public Object submit(Integer userId, String body) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + if (body == null) { + return ResponseUtil.badArgument(); + } + Integer cartId = JacksonUtil.parseInteger(body, "cartId"); + Integer addressId = JacksonUtil.parseInteger(body, "addressId"); + Integer couponId = JacksonUtil.parseInteger(body, "couponId"); + String message = JacksonUtil.parseString(body, "message"); + Integer grouponRulesId = JacksonUtil.parseInteger(body, "grouponRulesId"); + Integer grouponLinkId = JacksonUtil.parseInteger(body, "grouponLinkId"); + + //如果是团购项目,验证活动是否有效 + if (grouponRulesId != null && grouponRulesId > 0) { + LitemallGrouponRules rules = grouponRulesService.queryById(grouponRulesId); + //找不到记录 + if (rules == null) { + return ResponseUtil.badArgument(); + } + //团购活动已经过期 + if (grouponRulesService.isExpired(rules)) { + return ResponseUtil.fail(GROUPON_EXPIRED, "团购活动已过期!"); + } + } + + if (cartId == null || addressId == null || couponId == null) { + return ResponseUtil.badArgument(); + } + + // 收货地址 + LitemallAddress checkedAddress = addressService.findById(addressId); + if (checkedAddress == null) { + return ResponseUtil.badArgument(); + } + + // 团购优惠 + BigDecimal grouponPrice = new BigDecimal(0.00); + LitemallGrouponRules grouponRules = grouponRulesService.queryById(grouponRulesId); + if (grouponRules != null) { + grouponPrice = grouponRules.getDiscount(); + } + + // 货品价格 + List checkedGoodsList = null; + if (cartId.equals(0)) { + checkedGoodsList = cartService.queryByUidAndChecked(userId); + } else { + LitemallCart cart = cartService.findById(cartId); + checkedGoodsList = new ArrayList<>(1); + checkedGoodsList.add(cart); + } + if (checkedGoodsList.size() == 0) { + return ResponseUtil.badArgumentValue(); + } + BigDecimal checkedGoodsPrice = new BigDecimal(0.00); + for (LitemallCart checkGoods : checkedGoodsList) { + // 只有当团购规格商品ID符合才进行团购优惠 + if (grouponRules != null && grouponRules.getGoodsId().equals(checkGoods.getGoodsId())) { + checkedGoodsPrice = checkedGoodsPrice.add(checkGoods.getPrice().subtract(grouponPrice).multiply(new BigDecimal(checkGoods.getNumber()))); + } else { + checkedGoodsPrice = checkedGoodsPrice.add(checkGoods.getPrice().multiply(new BigDecimal(checkGoods.getNumber()))); + } + } + + // 获取可用的优惠券信息 + // 使用优惠券减免的金额 + BigDecimal couponPrice = new BigDecimal(0.00); + // 如果couponId=0则没有优惠券,couponId=-1则不使用优惠券 + if(couponId != 0 && couponId != -1){ + LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, checkedGoodsPrice); + if(coupon == null){ + return ResponseUtil.badArgumentValue(); + } + couponPrice = coupon.getDiscount(); + } + + + // 根据订单商品总价计算运费,满足条件(例如88元)则免运费,否则需要支付运费(例如8元); + BigDecimal freightPrice = new BigDecimal(0.00); + if (checkedGoodsPrice.compareTo(SystemConfig.getFreightLimit()) < 0) { + freightPrice = SystemConfig.getFreight(); + } + + // 可以使用的其他钱,例如用户积分 + BigDecimal integralPrice = new BigDecimal(0.00); + + // 订单费用 + BigDecimal orderTotalPrice = checkedGoodsPrice.add(freightPrice).subtract(couponPrice); + // 最终支付费用 + BigDecimal actualPrice = orderTotalPrice.subtract(integralPrice); + + Integer orderId = null; + LitemallOrder order = null; + // 订单 + order = new LitemallOrder(); + order.setUserId(userId); + order.setOrderSn(orderService.generateOrderSn(userId)); + order.setOrderStatus(OrderUtil.STATUS_CREATE); + order.setConsignee(checkedAddress.getName()); + order.setMobile(checkedAddress.getMobile()); + order.setMessage(message); + String detailedAddress = detailedAddress(checkedAddress); + order.setAddress(detailedAddress); + order.setGoodsPrice(checkedGoodsPrice); + order.setFreightPrice(freightPrice); + order.setCouponPrice(couponPrice); + order.setIntegralPrice(integralPrice); + order.setOrderPrice(orderTotalPrice); + order.setActualPrice(actualPrice); + + // 有团购活动 + if (grouponRules != null) { + order.setGrouponPrice(grouponPrice); // 团购价格 + } else { + order.setGrouponPrice(new BigDecimal(0.00)); // 团购价格 + } + + // 添加订单表项 + orderService.add(order); + orderId = order.getId(); + + // 添加订单商品表项 + for (LitemallCart cartGoods : checkedGoodsList) { + // 订单商品 + LitemallOrderGoods orderGoods = new LitemallOrderGoods(); + orderGoods.setOrderId(order.getId()); + orderGoods.setGoodsId(cartGoods.getGoodsId()); + orderGoods.setGoodsSn(cartGoods.getGoodsSn()); + orderGoods.setProductId(cartGoods.getProductId()); + orderGoods.setGoodsName(cartGoods.getGoodsName()); + orderGoods.setPicUrl(cartGoods.getPicUrl()); + orderGoods.setPrice(cartGoods.getPrice()); + orderGoods.setNumber(cartGoods.getNumber()); + orderGoods.setSpecifications(cartGoods.getSpecifications()); + orderGoods.setAddTime(LocalDateTime.now()); + + orderGoodsService.add(orderGoods); + } + + // 删除购物车里面的商品信息 + cartService.clearGoods(userId); + + // 商品货品数量减少 + for (LitemallCart checkGoods : checkedGoodsList) { + Integer productId = checkGoods.getProductId(); + LitemallGoodsProduct product = productService.findById(productId); + + Integer remainNumber = product.getNumber() - checkGoods.getNumber(); + if (remainNumber < 0) { + throw new RuntimeException("下单的商品货品数量大于库存量"); + } + if (productService.reduceStock(productId, checkGoods.getNumber()) == 0) { + throw new RuntimeException("商品货品库存减少失败"); + } + } + + // 如果使用了优惠券,设置优惠券使用状态 + if(couponId != 0 && couponId != -1){ + LitemallCouponUser couponUser = couponUserService.queryOne(userId, couponId); + couponUser.setStatus(CouponUserConstant.STATUS_USED); + couponUser.setUsedTime(LocalDateTime.now()); + couponUser.setOrderId(orderId); + couponUserService.update(couponUser); + } + + //如果是团购项目,添加团购信息 + if (grouponRulesId != null && grouponRulesId > 0) { + LitemallGroupon groupon = new LitemallGroupon(); + groupon.setOrderId(orderId); + groupon.setPayed(false); + groupon.setUserId(userId); + groupon.setRulesId(grouponRulesId); + + //参与者 + if (grouponLinkId != null && grouponLinkId > 0) { + //参与的团购记录 + LitemallGroupon baseGroupon = grouponService.queryById(grouponLinkId); + groupon.setCreatorUserId(baseGroupon.getCreatorUserId()); + groupon.setGrouponId(grouponLinkId); + groupon.setShareUrl(baseGroupon.getShareUrl()); + } else { + groupon.setCreatorUserId(userId); + groupon.setGrouponId(0); + } + + grouponService.createGroupon(groupon); + } + + Map data = new HashMap<>(); + data.put("orderId", orderId); + return ResponseUtil.ok(data); + } + + /** + * 取消订单 + *

+ * 1. 检测当前订单是否能够取消; + * 2. 设置订单取消状态; + * 3. 商品货品库存恢复; + * 4. TODO 优惠券; + * 5. TODO 团购活动。 + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 取消订单操作结果 + */ + @Transactional + public Object cancel(Integer userId, String body) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + if (orderId == null) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgumentValue(); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.badArgumentValue(); + } + + LocalDateTime preUpdateTime = order.getUpdateTime(); + + // 检测是否能够取消 + OrderHandleOption handleOption = OrderUtil.build(order); + if (!handleOption.isCancel()) { + return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能取消"); + } + + // 设置订单已取消状态 + order.setOrderStatus(OrderUtil.STATUS_CANCEL); + order.setEndTime(LocalDateTime.now()); + if (orderService.updateWithOptimisticLocker(order) == 0) { + throw new RuntimeException("更新数据已失效"); + } + + // 商品货品数量增加 + List orderGoodsList = orderGoodsService.queryByOid(orderId); + for (LitemallOrderGoods orderGoods : orderGoodsList) { + Integer productId = orderGoods.getProductId(); + Short number = orderGoods.getNumber(); + if (productService.addStock(productId, number) == 0) { + throw new RuntimeException("商品货品库存增加失败"); + } + } + + return ResponseUtil.ok(); + } + + /** + * 付款订单的预支付会话标识 + *

+ * 1. 检测当前订单是否能够付款 + * 2. 微信商户平台返回支付订单ID + * 3. 设置订单付款状态 + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 支付订单ID + */ + @Transactional + public Object prepay(Integer userId, String body, HttpServletRequest request) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + if (orderId == null) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgumentValue(); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.badArgumentValue(); + } + + // 检测是否能够取消 + OrderHandleOption handleOption = OrderUtil.build(order); + if (!handleOption.isPay()) { + return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能支付"); + } + + LitemallUser user = userService.findById(userId); + String openid = user.getWeixinOpenid(); + if (openid == null) { + return ResponseUtil.fail(AUTH_OPENID_UNACCESS, "订单不能支付"); + } + WxPayMpOrderResult result = null; + try { + WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest(); + orderRequest.setOutTradeNo(order.getOrderSn()); + orderRequest.setOpenid(openid); + orderRequest.setBody("订单:" + order.getOrderSn()); + // 元转成分 + int fee = 0; + BigDecimal actualPrice = order.getActualPrice(); + fee = actualPrice.multiply(new BigDecimal(100)).intValue(); + orderRequest.setTotalFee(fee); + orderRequest.setSpbillCreateIp(IpUtil.getIpAddr(request)); + + result = wxPayService.createOrder(orderRequest); + + //缓存prepayID用于后续模版通知 + String prepayId = result.getPackageValue(); + prepayId = prepayId.replace("prepay_id=", ""); + LitemallUserFormid userFormid = new LitemallUserFormid(); + userFormid.setOpenid(user.getWeixinOpenid()); + userFormid.setFormid(prepayId); + userFormid.setIsprepay(true); + userFormid.setUseamount(3); + userFormid.setExpireTime(LocalDateTime.now().plusDays(7)); + formIdService.addUserFormid(userFormid); + + } catch (Exception e) { + e.printStackTrace(); + return ResponseUtil.fail(ORDER_PAY_FAIL, "订单不能支付"); + } + + if (orderService.updateWithOptimisticLocker(order) == 0) { + return ResponseUtil.updatedDateExpired(); + } + return ResponseUtil.ok(result); + } + + /** + * 微信付款成功或失败回调接口 + *

+ * 1. 检测当前订单是否是付款状态; + * 2. 设置订单付款成功状态相关信息; + * 3. 响应微信商户平台. + * + * @param request 请求内容 + * @param response 响应内容 + * @return 操作结果 + */ + @Transactional + public Object payNotify(HttpServletRequest request, HttpServletResponse response) { + String xmlResult = null; + try { + xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding()); + } catch (IOException e) { + e.printStackTrace(); + return WxPayNotifyResponse.fail(e.getMessage()); + } + + WxPayOrderNotifyResult result = null; + try { + result = wxPayService.parseOrderNotifyResult(xmlResult); + } catch (WxPayException e) { + e.printStackTrace(); + return WxPayNotifyResponse.fail(e.getMessage()); + } + + logger.info("处理腾讯支付平台的订单支付"); + logger.info(result); + + String orderSn = result.getOutTradeNo(); + String payId = result.getTransactionId(); + + // 分转化成元 + String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee()); + LitemallOrder order = orderService.findBySn(orderSn); + if (order == null) { + return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn); + } + + // 检查这个订单是否已经处理过 + if (OrderUtil.isPayStatus(order) && order.getPayId() != null) { + return WxPayNotifyResponse.success("订单已经处理成功!"); + } + + // 检查支付订单金额 + if (!totalFee.equals(order.getActualPrice().toString())) { + return WxPayNotifyResponse.fail(order.getOrderSn() + " : 支付金额不符合 totalFee=" + totalFee); + } + + order.setPayId(payId); + order.setPayTime(LocalDateTime.now()); + order.setOrderStatus(OrderUtil.STATUS_PAY); + if (orderService.updateWithOptimisticLocker(order) == 0) { + // 这里可能存在这样一个问题,用户支付和系统自动取消订单发生在同时 + // 如果数据库首先因为系统自动取消订单而更新了订单状态; + // 此时用户支付完成回调这里也要更新数据库,而由于乐观锁机制这里的更新会失败 + // 因此,这里会重新读取数据库检查状态是否是订单自动取消,如果是则更新成支付状态。 + order = orderService.findBySn(orderSn); + int updated = 0; + if (OrderUtil.isAutoCancelStatus(order)) { + order.setPayId(payId); + order.setPayTime(LocalDateTime.now()); + order.setOrderStatus(OrderUtil.STATUS_PAY); + updated = orderService.updateWithOptimisticLocker(order); + } + + // 如果updated是0,那么数据库更新失败 + if (updated == 0) { + return WxPayNotifyResponse.fail("更新数据已失效"); + } + } + + // 支付成功,有团购信息,更新团购信息 + LitemallGroupon groupon = grouponService.queryByOrderId(order.getId()); + if (groupon != null) { + LitemallGrouponRules grouponRules = grouponRulesService.queryById(groupon.getRulesId()); + + //仅当发起者才创建分享图片 + if (groupon.getGrouponId() == 0) { + String url = qCodeService.createGrouponShareImage(grouponRules.getGoodsName(), grouponRules.getPicUrl(), groupon); + groupon.setShareUrl(url); + } + groupon.setPayed(true); + if (grouponService.updateById(groupon) == 0) { + return WxPayNotifyResponse.fail("更新数据已失效"); + } + } + + //TODO 发送邮件和短信通知,这里采用异步发送 + // 订单支付成功以后,会发送短信给用户,以及发送邮件给管理员 + notifyService.notifyMail("新订单通知", order.toString()); + // 这里微信的短信平台对参数长度有限制,所以将订单号只截取后6位 + notifyService.notifySmsTemplateSync(order.getMobile(), NotifyType.PAY_SUCCEED, new String[]{orderSn.substring(8, 14)}); + + // 请依据自己的模版消息配置更改参数 + String[] parms = new String[]{ + order.getOrderSn(), + order.getOrderPrice().toString(), + DateTimeUtil.getDateTimeDisplayString(order.getAddTime()), + order.getConsignee(), + order.getMobile(), + order.getAddress() + }; + + notifyService.notifyWxTemplate(result.getOpenid(), NotifyType.PAY_SUCCEED, parms, "pages/index/index?orderId=" + order.getId()); + + return WxPayNotifyResponse.success("处理成功!"); + } + + /** + * 订单申请退款 + *

+ * 1. 检测当前订单是否能够退款; + * 2. 设置订单申请退款状态。 + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 订单退款操作结果 + */ + public Object refund(Integer userId, String body) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + if (orderId == null) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgument(); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.badArgumentValue(); + } + + OrderHandleOption handleOption = OrderUtil.build(order); + if (!handleOption.isRefund()) { + return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能取消"); + } + + // 设置订单申请退款状态 + order.setOrderStatus(OrderUtil.STATUS_REFUND); + if (orderService.updateWithOptimisticLocker(order) == 0) { + return ResponseUtil.updatedDateExpired(); + } + + //TODO 发送邮件和短信通知,这里采用异步发送 + // 有用户申请退款,邮件通知运营人员 + notifyService.notifyMail("退款申请", order.toString()); + + return ResponseUtil.ok(); + } + + /** + * 确认收货 + *

+ * 1. 检测当前订单是否能够确认收货; + * 2. 设置订单确认收货状态。 + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 订单操作结果 + */ + public Object confirm(Integer userId, String body) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + if (orderId == null) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgument(); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.badArgumentValue(); + } + + OrderHandleOption handleOption = OrderUtil.build(order); + if (!handleOption.isConfirm()) { + return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能确认收货"); + } + + Short comments = orderGoodsService.getComments(orderId); + order.setComments(comments); + + order.setOrderStatus(OrderUtil.STATUS_CONFIRM); + order.setConfirmTime(LocalDateTime.now()); + if (orderService.updateWithOptimisticLocker(order) == 0) { + return ResponseUtil.updatedDateExpired(); + } + return ResponseUtil.ok(); + } + + /** + * 删除订单 + *

+ * 1. 检测当前订单是否可以删除; + * 2. 删除订单。 + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 订单操作结果 + */ + public Object delete(Integer userId, String body) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + if (orderId == null) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgument(); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.badArgumentValue(); + } + + OrderHandleOption handleOption = OrderUtil.build(order); + if (!handleOption.isDelete()) { + return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能删除"); + } + + // 订单order_status没有字段用于标识删除 + // 而是存在专门的delete字段表示是否删除 + orderService.deleteById(orderId); + + return ResponseUtil.ok(); + } + + /** + * 待评价订单商品信息 + * + * @param userId 用户ID + * @param orderId 订单ID + * @param goodsId 商品ID + * @return 待评价订单商品信息 + */ + public Object goods(Integer userId, Integer orderId, Integer goodsId) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + + List orderGoodsList = orderGoodsService.findByOidAndGid(orderId, goodsId); + int size = orderGoodsList.size(); + + Assert.state(size < 2, "存在多个符合条件的订单商品"); + + if (size == 0) { + return ResponseUtil.badArgumentValue(); + } + + LitemallOrderGoods orderGoods = orderGoodsList.get(0); + return ResponseUtil.ok(orderGoods); + } + + /** + * 评价订单商品 + *

+ * 确认商品收货或者系统自动确认商品收货后7天内可以评价,过期不能评价。 + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 订单操作结果 + */ + public Object comment(Integer userId, String body) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + + Integer orderGoodsId = JacksonUtil.parseInteger(body, "orderGoodsId"); + if (orderGoodsId == null) { + return ResponseUtil.badArgument(); + } + LitemallOrderGoods orderGoods = orderGoodsService.findById(orderGoodsId); + if (orderGoods == null) { + return ResponseUtil.badArgumentValue(); + } + Integer orderId = orderGoods.getOrderId(); + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgumentValue(); + } + Short orderStatus = order.getOrderStatus(); + if (!OrderUtil.isConfirmStatus(order) && !OrderUtil.isAutoConfirmStatus(order)) { + return ResponseUtil.fail(ORDER_INVALID_OPERATION, "当前商品不能评价"); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.fail(ORDER_INVALID, "当前商品不属于用户"); + } + Integer commentId = orderGoods.getComment(); + if (commentId == -1) { + return ResponseUtil.fail(ORDER_COMMENT_EXPIRED, "当前商品评价时间已经过期"); + } + if (commentId != 0) { + return ResponseUtil.fail(ORDER_COMMENTED, "订单商品已评价"); + } + + String content = JacksonUtil.parseString(body, "content"); + Integer star = JacksonUtil.parseInteger(body, "star"); + if (star == null || star < 0 || star > 5) { + return ResponseUtil.badArgumentValue(); + } + Boolean hasPicture = JacksonUtil.parseBoolean(body, "hasPicture"); + List picUrls = JacksonUtil.parseStringList(body, "picUrls"); + if (hasPicture == null || !hasPicture) { + picUrls = new ArrayList<>(0); + } + + // 1. 创建评价 + LitemallComment comment = new LitemallComment(); + comment.setUserId(userId); + comment.setType((byte) 0); + comment.setValueId(orderGoods.getGoodsId()); + comment.setStar(star.shortValue()); + comment.setContent(content); + comment.setHasPicture(hasPicture); + comment.setPicUrls(picUrls.toArray(new String[]{})); + commentService.save(comment); + + // 2. 更新订单商品的评价列表 + orderGoods.setComment(comment.getId()); + orderGoodsService.updateById(orderGoods); + + // 3. 更新订单中未评价的订单商品可评价数量 + Short commentCount = order.getComments(); + if (commentCount > 0) { + commentCount--; + } + order.setComments(commentCount); + orderService.updateWithOptimisticLocker(order); + + return ResponseUtil.ok(); + } + +} \ No newline at end of file diff --git a/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java index c0c21833..98c46101 100644 --- a/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java +++ b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java @@ -1,74 +1,17 @@ package org.linlinjava.litemall.wx.web; -import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; -import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; -import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; -import com.github.binarywang.wxpay.exception.WxPayException; -import com.github.binarywang.wxpay.service.WxPayService; -import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.linlinjava.litemall.core.express.ExpressService; -import org.linlinjava.litemall.core.express.dao.ExpressInfo; -import org.linlinjava.litemall.core.notify.NotifyService; -import org.linlinjava.litemall.core.notify.NotifyType; -import org.linlinjava.litemall.core.qcode.QCodeService; -import org.linlinjava.litemall.core.system.SystemConfig; -import org.linlinjava.litemall.core.util.DateTimeUtil; -import org.linlinjava.litemall.core.util.JacksonUtil; -import org.linlinjava.litemall.core.util.ResponseUtil; -import org.linlinjava.litemall.db.domain.*; -import org.linlinjava.litemall.db.service.*; -import org.linlinjava.litemall.db.util.CouponUserConstant; -import org.linlinjava.litemall.db.util.OrderHandleOption; -import org.linlinjava.litemall.db.util.OrderUtil; import org.linlinjava.litemall.wx.annotation.LoginUser; -import org.linlinjava.litemall.wx.util.IpUtil; +import org.linlinjava.litemall.wx.service.WxOrderService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotNull; -import java.io.IOException; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import static org.linlinjava.litemall.wx.util.WxResponseCode.*; - -/** - * 订单服务 - * - *

- * 订单状态: - * 101 订单生成,未支付;102,下单后未支付用户取消;103,下单后未支付超时系统自动取消 - * 201 支付完成,商家未发货;202,订单生产,已付款未发货,但是退款取消; - * 301 商家发货,用户未确认; - * 401 用户确认收货; 402 用户没有确认收货超过一定时间,系统自动确认收货; - * - *

- * 用户操作: - * 当101用户未付款时,此时用户可以进行的操作是取消订单,或者付款操作 - * 当201支付完成而商家未发货时,此时用户可以取消订单并申请退款 - * 当301商家已发货时,此时用户可以有确认收货的操作 - * 当401用户确认收货以后,此时用户可以进行的操作是删除订单,评价商品,或者再次购买 - * 当402系统自动确认收货以后,此时用户可以删除订单,评价商品,或者再次购买 - * - *

- * 注意:目前不支持订单退货和售后服务 - */ @RestController @RequestMapping("/wx/order") @Validated @@ -76,65 +19,13 @@ public class WxOrderController { private final Log logger = LogFactory.getLog(WxOrderController.class); @Autowired - private PlatformTransactionManager txManager; - @Autowired - private LitemallUserService userService; - @Autowired - private LitemallOrderService orderService; - @Autowired - private LitemallOrderGoodsService orderGoodsService; - @Autowired - private LitemallAddressService addressService; - @Autowired - private LitemallCartService cartService; - @Autowired - private LitemallRegionService regionService; - @Autowired - private LitemallGoodsProductService productService; - @Autowired - private WxPayService wxPayService; - @Autowired - private NotifyService notifyService; - @Autowired - private LitemallUserFormIdService formIdService; - @Autowired - private LitemallGrouponRulesService grouponRulesService; - @Autowired - private LitemallGrouponService grouponService; - @Autowired - private QCodeService qCodeService; - @Autowired - private ExpressService expressService; - @Autowired - private LitemallCommentService commentService; - @Autowired - private LitemallCouponService couponService; - @Autowired - private LitemallCouponUserService couponUserService; - @Autowired - private CouponVerifyService couponVerifyService; - - private String detailedAddress(LitemallAddress litemallAddress) { - Integer provinceId = litemallAddress.getProvinceId(); - Integer cityId = litemallAddress.getCityId(); - Integer areaId = litemallAddress.getAreaId(); - String provinceName = regionService.findById(provinceId).getName(); - String cityName = regionService.findById(cityId).getName(); - String areaName = regionService.findById(areaId).getName(); - String fullRegion = provinceName + " " + cityName + " " + areaName; - return fullRegion + " " + litemallAddress.getAddress(); - } + private WxOrderService wxOrderService; /** * 订单列表 * * @param userId 用户ID - * @param showType 订单信息: - * 0,全部订单; - * 1,待付款; - * 2,待发货; - * 3,待收货; - * 4,待评价。 + * @param showType 订单信息 * @param page 分页页数 * @param size 分页大小 * @return 订单列表 @@ -144,50 +35,7 @@ public class WxOrderController { @RequestParam(defaultValue = "0") Integer showType, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer size) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - - List orderStatus = OrderUtil.orderStatus(showType); - List orderList = orderService.queryByOrderStatus(userId, orderStatus); - int count = orderService.countByOrderStatus(userId, orderStatus); - - List> orderVoList = new ArrayList<>(orderList.size()); - for (LitemallOrder order : orderList) { - Map orderVo = new HashMap<>(); - orderVo.put("id", order.getId()); - orderVo.put("orderSn", order.getOrderSn()); - orderVo.put("actualPrice", order.getActualPrice()); - orderVo.put("orderStatusText", OrderUtil.orderStatusText(order)); - orderVo.put("handleOption", OrderUtil.build(order)); - - LitemallGroupon groupon = grouponService.queryByOrderId(order.getId()); - if (groupon != null) { - orderVo.put("isGroupin", true); - } else { - orderVo.put("isGroupin", false); - } - - List orderGoodsList = orderGoodsService.queryByOid(order.getId()); - List> orderGoodsVoList = new ArrayList<>(orderGoodsList.size()); - for (LitemallOrderGoods orderGoods : orderGoodsList) { - Map orderGoodsVo = new HashMap<>(); - orderGoodsVo.put("id", orderGoods.getId()); - orderGoodsVo.put("goodsName", orderGoods.getGoodsName()); - orderGoodsVo.put("number", orderGoods.getNumber()); - orderGoodsVo.put("picUrl", orderGoods.getPicUrl()); - orderGoodsVoList.add(orderGoodsVo); - } - orderVo.put("goodsList", orderGoodsVoList); - - orderVoList.add(orderVo); - } - - Map result = new HashMap<>(); - result.put("count", count); - result.put("data", orderVoList); - - return ResponseUtil.ok(result); + return wxOrderService.list(userId, showType, page, size); } /** @@ -199,58 +47,11 @@ public class WxOrderController { */ @GetMapping("detail") public Object detail(@LoginUser Integer userId, @NotNull Integer orderId) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - - // 订单信息 - LitemallOrder order = orderService.findById(orderId); - if (null == order) { - return ResponseUtil.fail(ORDER_UNKNOWN, "订单不存在"); - } - if (!order.getUserId().equals(userId)) { - return ResponseUtil.fail(ORDER_INVALID, "不是当前用户的订单"); - } - Map orderVo = new HashMap(); - orderVo.put("id", order.getId()); - orderVo.put("orderSn", order.getOrderSn()); - orderVo.put("addTime", order.getAddTime()); - orderVo.put("consignee", order.getConsignee()); - orderVo.put("mobile", order.getMobile()); - orderVo.put("address", order.getAddress()); - orderVo.put("goodsPrice", order.getGoodsPrice()); - orderVo.put("freightPrice", order.getFreightPrice()); - orderVo.put("actualPrice", order.getActualPrice()); - orderVo.put("orderStatusText", OrderUtil.orderStatusText(order)); - orderVo.put("handleOption", OrderUtil.build(order)); - orderVo.put("expCode", order.getShipChannel()); - orderVo.put("expNo", order.getShipSn()); - - List orderGoodsList = orderGoodsService.queryByOid(order.getId()); - - Map result = new HashMap<>(); - result.put("orderInfo", orderVo); - result.put("orderGoods", orderGoodsList); - - // 订单状态为已发货且物流信息不为空 - //"YTO", "800669400640887922" - if (order.getOrderStatus().equals(OrderUtil.STATUS_SHIP)) { - ExpressInfo ei = expressService.getExpressInfo(order.getShipChannel(), order.getShipSn()); - result.put("expressInfo", ei); - } - - return ResponseUtil.ok(result); - + return wxOrderService.detail(userId, orderId); } /** * 提交订单 - *

- * 1. 创建订单表项和订单商品表项; - * 2. 购物车清空; - * 3. 优惠券设置已用; - * 4. 商品货品库存减少; - * 5. 如果是团购商品,则创建团购活动表项。 * * @param userId 用户ID * @param body 订单信息,{ cartId:xxx, addressId: xxx, couponId: xxx, message: xxx, grouponRulesId: xxx, grouponLinkId: xxx} @@ -258,219 +59,11 @@ public class WxOrderController { */ @PostMapping("submit") public Object submit(@LoginUser Integer userId, @RequestBody String body) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - if (body == null) { - return ResponseUtil.badArgument(); - } - Integer cartId = JacksonUtil.parseInteger(body, "cartId"); - Integer addressId = JacksonUtil.parseInteger(body, "addressId"); - Integer couponId = JacksonUtil.parseInteger(body, "couponId"); - String message = JacksonUtil.parseString(body, "message"); - Integer grouponRulesId = JacksonUtil.parseInteger(body, "grouponRulesId"); - Integer grouponLinkId = JacksonUtil.parseInteger(body, "grouponLinkId"); - - //如果是团购项目,验证活动是否有效 - if (grouponRulesId != null && grouponRulesId > 0) { - LitemallGrouponRules rules = grouponRulesService.queryById(grouponRulesId); - //找不到记录 - if (rules == null) { - return ResponseUtil.badArgument(); - } - //团购活动已经过期 - if (grouponRulesService.isExpired(rules)) { - return ResponseUtil.fail(GROUPON_EXPIRED, "团购活动已过期!"); - } - } - - if (cartId == null || addressId == null || couponId == null) { - return ResponseUtil.badArgument(); - } - - // 收货地址 - LitemallAddress checkedAddress = addressService.findById(addressId); - if (checkedAddress == null) { - return ResponseUtil.badArgument(); - } - - // 团购优惠 - BigDecimal grouponPrice = new BigDecimal(0.00); - LitemallGrouponRules grouponRules = grouponRulesService.queryById(grouponRulesId); - if (grouponRules != null) { - grouponPrice = grouponRules.getDiscount(); - } - - // 货品价格 - List checkedGoodsList = null; - if (cartId.equals(0)) { - checkedGoodsList = cartService.queryByUidAndChecked(userId); - } else { - LitemallCart cart = cartService.findById(cartId); - checkedGoodsList = new ArrayList<>(1); - checkedGoodsList.add(cart); - } - if (checkedGoodsList.size() == 0) { - return ResponseUtil.badArgumentValue(); - } - BigDecimal checkedGoodsPrice = new BigDecimal(0.00); - for (LitemallCart checkGoods : checkedGoodsList) { - // 只有当团购规格商品ID符合才进行团购优惠 - if (grouponRules != null && grouponRules.getGoodsId().equals(checkGoods.getGoodsId())) { - checkedGoodsPrice = checkedGoodsPrice.add(checkGoods.getPrice().subtract(grouponPrice).multiply(new BigDecimal(checkGoods.getNumber()))); - } else { - checkedGoodsPrice = checkedGoodsPrice.add(checkGoods.getPrice().multiply(new BigDecimal(checkGoods.getNumber()))); - } - } - - // 获取可用的优惠券信息 - // 使用优惠券减免的金额 - BigDecimal couponPrice = new BigDecimal(0.00); - // 如果couponId=0则没有优惠券,couponId=-1则不使用优惠券 - if(couponId != 0 && couponId != -1){ - LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, checkedGoodsPrice); - if(coupon == null){ - return ResponseUtil.badArgumentValue(); - } - couponPrice = coupon.getDiscount(); - } - - - // 根据订单商品总价计算运费,满足条件(例如88元)则免运费,否则需要支付运费(例如8元); - BigDecimal freightPrice = new BigDecimal(0.00); - if (checkedGoodsPrice.compareTo(SystemConfig.getFreightLimit()) < 0) { - freightPrice = SystemConfig.getFreight(); - } - - // 可以使用的其他钱,例如用户积分 - BigDecimal integralPrice = new BigDecimal(0.00); - - // 订单费用 - BigDecimal orderTotalPrice = checkedGoodsPrice.add(freightPrice).subtract(couponPrice); - // 最终支付费用 - BigDecimal actualPrice = orderTotalPrice.subtract(integralPrice); - - // 开启事务管理 - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - TransactionStatus status = txManager.getTransaction(def); - Integer orderId = null; - LitemallOrder order = null; - try { - // 订单 - order = new LitemallOrder(); - order.setUserId(userId); - order.setOrderSn(orderService.generateOrderSn(userId)); - order.setOrderStatus(OrderUtil.STATUS_CREATE); - order.setConsignee(checkedAddress.getName()); - order.setMobile(checkedAddress.getMobile()); - order.setMessage(message); - String detailedAddress = detailedAddress(checkedAddress); - order.setAddress(detailedAddress); - order.setGoodsPrice(checkedGoodsPrice); - order.setFreightPrice(freightPrice); - order.setCouponPrice(couponPrice); - order.setIntegralPrice(integralPrice); - order.setOrderPrice(orderTotalPrice); - order.setActualPrice(actualPrice); - - // 有团购活动 - if (grouponRules != null) { - order.setGrouponPrice(grouponPrice); // 团购价格 - } else { - order.setGrouponPrice(new BigDecimal(0.00)); // 团购价格 - } - - // 添加订单表项 - orderService.add(order); - orderId = order.getId(); - - // 添加订单商品表项 - for (LitemallCart cartGoods : checkedGoodsList) { - // 订单商品 - LitemallOrderGoods orderGoods = new LitemallOrderGoods(); - orderGoods.setOrderId(order.getId()); - orderGoods.setGoodsId(cartGoods.getGoodsId()); - orderGoods.setGoodsSn(cartGoods.getGoodsSn()); - orderGoods.setProductId(cartGoods.getProductId()); - orderGoods.setGoodsName(cartGoods.getGoodsName()); - orderGoods.setPicUrl(cartGoods.getPicUrl()); - orderGoods.setPrice(cartGoods.getPrice()); - orderGoods.setNumber(cartGoods.getNumber()); - orderGoods.setSpecifications(cartGoods.getSpecifications()); - orderGoods.setAddTime(LocalDateTime.now()); - - orderGoodsService.add(orderGoods); - } - - // 删除购物车里面的商品信息 - cartService.clearGoods(userId); - - // 商品货品数量减少 - for (LitemallCart checkGoods : checkedGoodsList) { - Integer productId = checkGoods.getProductId(); - LitemallGoodsProduct product = productService.findById(productId); - - Integer remainNumber = product.getNumber() - checkGoods.getNumber(); - if (remainNumber < 0) { - throw new RuntimeException("下单的商品货品数量大于库存量"); - } - if (productService.reduceStock(productId, checkGoods.getNumber()) == 0) { - throw new Exception("商品货品库存减少失败"); - } - } - - // 如果使用了优惠券,设置优惠券使用状态 - if(couponId != 0 && couponId != -1){ - LitemallCouponUser couponUser = couponUserService.queryOne(userId, couponId); - couponUser.setStatus(CouponUserConstant.STATUS_USED); - couponUser.setUsedTime(LocalDateTime.now()); - couponUser.setOrderId(orderId); - couponUserService.update(couponUser); - } - - //如果是团购项目,添加团购信息 - if (grouponRulesId != null && grouponRulesId > 0) { - LitemallGroupon groupon = new LitemallGroupon(); - groupon.setOrderId(orderId); - groupon.setPayed(false); - groupon.setUserId(userId); - groupon.setRulesId(grouponRulesId); - - //参与者 - if (grouponLinkId != null && grouponLinkId > 0) { - //参与的团购记录 - LitemallGroupon baseGroupon = grouponService.queryById(grouponLinkId); - groupon.setCreatorUserId(baseGroupon.getCreatorUserId()); - groupon.setGrouponId(grouponLinkId); - groupon.setShareUrl(baseGroupon.getShareUrl()); - } else { - groupon.setCreatorUserId(userId); - groupon.setGrouponId(0); - } - - grouponService.createGroupon(groupon); - } - } catch (Exception ex) { - txManager.rollback(status); - logger.error("系统内部错误", ex); - return ResponseUtil.fail(ORDER_CHECKOUT_FAIL, "下单失败"); - } - txManager.commit(status); - - Map data = new HashMap<>(); - data.put("orderId", orderId); - return ResponseUtil.ok(data); + return wxOrderService.submit(userId, body); } /** * 取消订单 - *

- * 1. 检测当前订单是否能够取消; - * 2. 设置订单取消状态; - * 3. 商品货品库存恢复; - * 4. TODO 优惠券; - * 5. TODO 团购活动。 * * @param userId 用户ID * @param body 订单信息,{ orderId:xxx } @@ -478,67 +71,11 @@ public class WxOrderController { */ @PostMapping("cancel") public Object cancel(@LoginUser Integer userId, @RequestBody String body) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - Integer orderId = JacksonUtil.parseInteger(body, "orderId"); - if (orderId == null) { - return ResponseUtil.badArgument(); - } - - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgumentValue(); - } - if (!order.getUserId().equals(userId)) { - return ResponseUtil.badArgumentValue(); - } - - LocalDateTime preUpdateTime = order.getUpdateTime(); - - // 检测是否能够取消 - OrderHandleOption handleOption = OrderUtil.build(order); - if (!handleOption.isCancel()) { - return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能取消"); - } - - // 开启事务管理 - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - TransactionStatus status = txManager.getTransaction(def); - try { - // 设置订单已取消状态 - order.setOrderStatus(OrderUtil.STATUS_CANCEL); - order.setEndTime(LocalDateTime.now()); - if (orderService.updateWithOptimisticLocker(order) == 0) { - throw new Exception("更新数据已失效"); - } - - // 商品货品数量增加 - List orderGoodsList = orderGoodsService.queryByOid(orderId); - for (LitemallOrderGoods orderGoods : orderGoodsList) { - Integer productId = orderGoods.getProductId(); - Short number = orderGoods.getNumber(); - if (productService.addStock(productId, number) == 0) { - throw new Exception("商品货品库存增加失败"); - } - } - } catch (Exception ex) { - txManager.rollback(status); - logger.error("系统内部错误", ex); - return ResponseUtil.fail(ORDER_CANCEL_FAIL, "订单取消失败"); - } - txManager.commit(status); - - return ResponseUtil.ok(); + return wxOrderService.cancel(userId, body); } /** * 付款订单的预支付会话标识 - *

- * 1. 检测当前订单是否能够付款 - * 2. 微信商户平台返回支付订单ID - * 3. 设置订单付款状态 * * @param userId 用户ID * @param body 订单信息,{ orderId:xxx } @@ -546,77 +83,13 @@ public class WxOrderController { */ @PostMapping("prepay") public Object prepay(@LoginUser Integer userId, @RequestBody String body, HttpServletRequest request) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - Integer orderId = JacksonUtil.parseInteger(body, "orderId"); - if (orderId == null) { - return ResponseUtil.badArgument(); - } - - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgumentValue(); - } - if (!order.getUserId().equals(userId)) { - return ResponseUtil.badArgumentValue(); - } - - // 检测是否能够取消 - OrderHandleOption handleOption = OrderUtil.build(order); - if (!handleOption.isPay()) { - return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能支付"); - } - - LitemallUser user = userService.findById(userId); - String openid = user.getWeixinOpenid(); - if (openid == null) { - return ResponseUtil.fail(AUTH_OPENID_UNACCESS, "订单不能支付"); - } - WxPayMpOrderResult result = null; - try { - WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest(); - orderRequest.setOutTradeNo(order.getOrderSn()); - orderRequest.setOpenid(openid); - orderRequest.setBody("订单:" + order.getOrderSn()); - // 元转成分 - Integer fee = 0; - BigDecimal actualPrice = order.getActualPrice(); - fee = actualPrice.multiply(new BigDecimal(100)).intValue(); - orderRequest.setTotalFee(fee); - orderRequest.setSpbillCreateIp(IpUtil.getIpAddr(request)); - - result = wxPayService.createOrder(orderRequest); - - //缓存prepayID用于后续模版通知 - String prepayId = result.getPackageValue(); - prepayId = prepayId.replace("prepay_id=", ""); - LitemallUserFormid userFormid = new LitemallUserFormid(); - userFormid.setOpenid(user.getWeixinOpenid()); - userFormid.setFormid(prepayId); - userFormid.setIsprepay(true); - userFormid.setUseamount(3); - userFormid.setExpireTime(LocalDateTime.now().plusDays(7)); - formIdService.addUserFormid(userFormid); - - } catch (Exception e) { - e.printStackTrace(); - return ResponseUtil.fail(ORDER_PAY_FAIL, "订单不能支付"); - } - - if (orderService.updateWithOptimisticLocker(order) == 0) { - return ResponseUtil.updatedDateExpired(); - } - return ResponseUtil.ok(result); + return wxOrderService.prepay(userId, body, request); } /** * 微信付款成功或失败回调接口 *

- * 1. 检测当前订单是否是付款状态; - * 2. 设置订单付款成功状态相关信息; - * 3. 响应微信商户平台. - *

+ * TODO * 注意,这里pay-notify是示例地址,建议开发者应该设立一个隐蔽的回调地址 * * @param request 请求内容 @@ -625,110 +98,11 @@ public class WxOrderController { */ @PostMapping("pay-notify") public Object payNotify(HttpServletRequest request, HttpServletResponse response) { - String xmlResult = null; - try { - xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding()); - } catch (IOException e) { - e.printStackTrace(); - return WxPayNotifyResponse.fail(e.getMessage()); - } - - WxPayOrderNotifyResult result = null; - try { - result = wxPayService.parseOrderNotifyResult(xmlResult); - } catch (WxPayException e) { - e.printStackTrace(); - return WxPayNotifyResponse.fail(e.getMessage()); - } - - logger.info("处理腾讯支付平台的订单支付"); - logger.info(result); - - String orderSn = result.getOutTradeNo(); - String payId = result.getTransactionId(); - - // 分转化成元 - String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee()); - LitemallOrder order = orderService.findBySn(orderSn); - if (order == null) { - return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn); - } - - // 检查这个订单是否已经处理过 - if (OrderUtil.isPayStatus(order) && order.getPayId() != null) { - return WxPayNotifyResponse.success("订单已经处理成功!"); - } - - // 检查支付订单金额 - if (!totalFee.equals(order.getActualPrice().toString())) { - return WxPayNotifyResponse.fail(order.getOrderSn() + " : 支付金额不符合 totalFee=" + totalFee); - } - - order.setPayId(payId); - order.setPayTime(LocalDateTime.now()); - order.setOrderStatus(OrderUtil.STATUS_PAY); - if (orderService.updateWithOptimisticLocker(order) == 0) { - // 这里可能存在这样一个问题,用户支付和系统自动取消订单发生在同时 - // 如果数据库首先因为系统自动取消订单而更新了订单状态; - // 此时用户支付完成回调这里也要更新数据库,而由于乐观锁机制这里的更新会失败 - // 因此,这里会重新读取数据库检查状态是否是订单自动取消,如果是则更新成支付状态。 - order = orderService.findBySn(orderSn); - int updated = 0; - if (OrderUtil.isAutoCancelStatus(order)) { - order.setPayId(payId); - order.setPayTime(LocalDateTime.now()); - order.setOrderStatus(OrderUtil.STATUS_PAY); - updated = orderService.updateWithOptimisticLocker(order); - } - - // 如果updated是0,那么数据库更新失败 - if (updated == 0) { - return WxPayNotifyResponse.fail("更新数据已失效"); - } - } - - // 支付成功,有团购信息,更新团购信息 - LitemallGroupon groupon = grouponService.queryByOrderId(order.getId()); - if (groupon != null) { - LitemallGrouponRules grouponRules = grouponRulesService.queryById(groupon.getRulesId()); - - //仅当发起者才创建分享图片 - if (groupon.getGrouponId() == 0) { - String url = qCodeService.createGrouponShareImage(grouponRules.getGoodsName(), grouponRules.getPicUrl(), groupon); - groupon.setShareUrl(url); - } - groupon.setPayed(true); - if (grouponService.updateById(groupon) == 0) { - return WxPayNotifyResponse.fail("更新数据已失效"); - } - } - - //TODO 发送邮件和短信通知,这里采用异步发送 - // 订单支付成功以后,会发送短信给用户,以及发送邮件给管理员 - notifyService.notifyMail("新订单通知", order.toString()); - // 这里微信的短信平台对参数长度有限制,所以将订单号只截取后6位 - notifyService.notifySmsTemplateSync(order.getMobile(), NotifyType.PAY_SUCCEED, new String[]{orderSn.substring(8, 14)}); - - // 请依据自己的模版消息配置更改参数 - String[] parms = new String[]{ - order.getOrderSn(), - order.getOrderPrice().toString(), - DateTimeUtil.getDateTimeDisplayString(order.getAddTime()), - order.getConsignee(), - order.getMobile(), - order.getAddress() - }; - - notifyService.notifyWxTemplate(result.getOpenid(), NotifyType.PAY_SUCCEED, parms, "pages/index/index?orderId=" + order.getId()); - - return WxPayNotifyResponse.success("处理成功!"); + return wxOrderService.payNotify(request, response); } /** * 订单申请退款 - *

- * 1. 检测当前订单是否能够退款; - * 2. 设置订单申请退款状态。 * * @param userId 用户ID * @param body 订单信息,{ orderId:xxx } @@ -736,45 +110,11 @@ public class WxOrderController { */ @PostMapping("refund") public Object refund(@LoginUser Integer userId, @RequestBody String body) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - Integer orderId = JacksonUtil.parseInteger(body, "orderId"); - if (orderId == null) { - return ResponseUtil.badArgument(); - } - - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgument(); - } - if (!order.getUserId().equals(userId)) { - return ResponseUtil.badArgumentValue(); - } - - OrderHandleOption handleOption = OrderUtil.build(order); - if (!handleOption.isRefund()) { - return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能取消"); - } - - // 设置订单申请退款状态 - order.setOrderStatus(OrderUtil.STATUS_REFUND); - if (orderService.updateWithOptimisticLocker(order) == 0) { - return ResponseUtil.updatedDateExpired(); - } - - //TODO 发送邮件和短信通知,这里采用异步发送 - // 有用户申请退款,邮件通知运营人员 - notifyService.notifyMail("退款申请", order.toString()); - - return ResponseUtil.ok(); + return wxOrderService.refund(userId, body); } /** * 确认收货 - *

- * 1. 检测当前订单是否能够确认收货; - * 2. 设置订单确认收货状态。 * * @param userId 用户ID * @param body 订单信息,{ orderId:xxx } @@ -782,43 +122,11 @@ public class WxOrderController { */ @PostMapping("confirm") public Object confirm(@LoginUser Integer userId, @RequestBody String body) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - Integer orderId = JacksonUtil.parseInteger(body, "orderId"); - if (orderId == null) { - return ResponseUtil.badArgument(); - } - - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgument(); - } - if (!order.getUserId().equals(userId)) { - return ResponseUtil.badArgumentValue(); - } - - OrderHandleOption handleOption = OrderUtil.build(order); - if (!handleOption.isConfirm()) { - return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能确认收货"); - } - - Short comments = orderGoodsService.getComments(orderId); - order.setComments(comments); - - order.setOrderStatus(OrderUtil.STATUS_CONFIRM); - order.setConfirmTime(LocalDateTime.now()); - if (orderService.updateWithOptimisticLocker(order) == 0) { - return ResponseUtil.updatedDateExpired(); - } - return ResponseUtil.ok(); + return wxOrderService.confirm(userId, body); } /** * 删除订单 - *

- * 1. 检测当前订单是否可以删除; - * 2. 删除订单。 * * @param userId 用户ID * @param body 订单信息,{ orderId:xxx } @@ -826,32 +134,7 @@ public class WxOrderController { */ @PostMapping("delete") public Object delete(@LoginUser Integer userId, @RequestBody String body) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - Integer orderId = JacksonUtil.parseInteger(body, "orderId"); - if (orderId == null) { - return ResponseUtil.badArgument(); - } - - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgument(); - } - if (!order.getUserId().equals(userId)) { - return ResponseUtil.badArgumentValue(); - } - - OrderHandleOption handleOption = OrderUtil.build(order); - if (!handleOption.isDelete()) { - return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能删除"); - } - - // 订单order_status没有字段用于标识删除 - // 而是存在专门的delete字段表示是否删除 - orderService.deleteById(orderId); - - return ResponseUtil.ok(); + return wxOrderService.delete(userId, body); } /** @@ -866,27 +149,11 @@ public class WxOrderController { public Object goods(@LoginUser Integer userId, @NotNull Integer orderId, @NotNull Integer goodsId) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - - List orderGoodsList = orderGoodsService.findByOidAndGid(orderId, goodsId); - int size = orderGoodsList.size(); - - Assert.state(size < 2, "存在多个符合条件的订单商品"); - - if (size == 0) { - return ResponseUtil.badArgumentValue(); - } - - LitemallOrderGoods orderGoods = orderGoodsList.get(0); - return ResponseUtil.ok(orderGoods); + return wxOrderService.goods(userId, orderId, goodsId); } /** * 评价订单商品 - *

- * 确认商品收货或者系统自动确认商品收货后7天内可以评价,过期不能评价。 * * @param userId 用户ID * @param body 订单信息,{ orderId:xxx } @@ -894,73 +161,7 @@ public class WxOrderController { */ @PostMapping("comment") public Object comment(@LoginUser Integer userId, @RequestBody String body) { - if (userId == null) { - return ResponseUtil.unlogin(); - } - - Integer orderGoodsId = JacksonUtil.parseInteger(body, "orderGoodsId"); - if (orderGoodsId == null) { - return ResponseUtil.badArgument(); - } - LitemallOrderGoods orderGoods = orderGoodsService.findById(orderGoodsId); - if (orderGoods == null) { - return ResponseUtil.badArgumentValue(); - } - Integer orderId = orderGoods.getOrderId(); - LitemallOrder order = orderService.findById(orderId); - if (order == null) { - return ResponseUtil.badArgumentValue(); - } - Short orderStatus = order.getOrderStatus(); - if (!OrderUtil.isConfirmStatus(order) && !OrderUtil.isAutoConfirmStatus(order)) { - return ResponseUtil.fail(ORDER_INVALID_OPERATION, "当前商品不能评价"); - } - if (!order.getUserId().equals(userId)) { - return ResponseUtil.fail(ORDER_INVALID, "当前商品不属于用户"); - } - Integer commentId = orderGoods.getComment(); - if (commentId == -1) { - return ResponseUtil.fail(ORDER_COMMENT_EXPIRED, "当前商品评价时间已经过期"); - } - if (commentId != 0) { - return ResponseUtil.fail(ORDER_COMMENTED, "订单商品已评价"); - } - - String content = JacksonUtil.parseString(body, "content"); - Integer star = JacksonUtil.parseInteger(body, "star"); - if (star == null || star < 0 || star > 5) { - return ResponseUtil.badArgumentValue(); - } - Boolean hasPicture = JacksonUtil.parseBoolean(body, "hasPicture"); - List picUrls = JacksonUtil.parseStringList(body, "picUrls"); - if (hasPicture == null || !hasPicture) { - picUrls = new ArrayList<>(0); - } - - // 1. 创建评价 - LitemallComment comment = new LitemallComment(); - comment.setUserId(userId); - comment.setType((byte) 0); - comment.setValueId(orderGoods.getGoodsId()); - comment.setStar(star.shortValue()); - comment.setContent(content); - comment.setHasPicture(hasPicture); - comment.setPicUrls(picUrls.toArray(new String[]{})); - commentService.save(comment); - - // 2. 更新订单商品的评价列表 - orderGoods.setComment(comment.getId()); - orderGoodsService.updateById(orderGoods); - - // 3. 更新订单中未评价的订单商品可评价数量 - Short commentCount = order.getComments(); - if (commentCount > 0) { - commentCount--; - } - order.setComments(commentCount); - orderService.updateWithOptimisticLocker(order); - - return ResponseUtil.ok(); + return wxOrderService.comment(userId, body); } } \ No newline at end of file