Commit fbc7dc6a authored by Junling Bu's avatar Junling Bu
Browse files

chore[litemall-admin-api, litemall-wx-api]: 取消编程式事务管理,采用@Transactional注解事务管理

parent 714870bf
......@@ -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("更新数据已失效");
}
// 设置订单已取消状态
order.setOrderStatus(OrderUtil.STATUS_AUTO_CANCEL);
order.setEndTime(LocalDateTime.now());
if (orderService.updateWithOptimisticLocker(order) == 0) {
throw new RuntimeException("更新数据已失效");
}
// 商品货品数量增加
Integer orderId = order.getId();
List<LitemallOrderGoods> 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("商品货品库存增加失败");
}
// 商品货品数量增加
Integer orderId = order.getId();
List<LitemallOrderGoods> 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("商品货品库存增加失败");
}
} catch (Exception ex) {
txManager.rollback(status);
logger.info("订单 ID=" + order.getId() + " 数据更新失败,放弃自动确认收货");
return;
}
txManager.commit(status);
logger.info("订单 ID=" + order.getId() + " 已经超期自动取消订单");
}
}
......
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<LitemallGoods> goodsList = goodsService.querySelective(goodsSn, name, page, limit, sort, order);
int total = goodsService.countSelective(goodsSn, name, page, limit, sort, order);
Map<String, Object> 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;
}
/**
* 编辑商品
* <p>
* TODO
* 目前商品修改的逻辑是
* 1. 更新litemall_goods表
* 2. 逻辑删除litemall_goods_specification、litemall_goods_attribute、litemall_goods_product
* 3. 添加litemall_goods_specification、litemall_goods_attribute、litemall_goods_product
* <p>
* 这里商品三个表的数据采用删除再添加的策略是因为
* 商品编辑页面,支持管理员添加删除商品规格、添加删除商品属性,因此这里仅仅更新是不可能的,
* 只能删除三个表旧的数据,然后添加新的数据。
* 但是这里又会引入新的问题,就是存在订单商品货品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<LitemallCategory> l1CatList = categoryService.queryL1();
List<CatVo> categoryList = new ArrayList<>(l1CatList.size());
for (LitemallCategory l1 : l1CatList) {
CatVo l1CatVo = new CatVo();
l1CatVo.setValue(l1.getId());
l1CatVo.setLabel(l1.getName());
List<LitemallCategory> l2CatList = categoryService.queryByPid(l1.getId());
List<CatVo> 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<LitemallBrand> list = brandService.all();
List<Map<String, Object>> brandList = new ArrayList<>(l1CatList.size());
for (LitemallBrand brand : list) {
Map<String, Object> b = new HashMap<>(2);
b.put("value", brand.getId());
b.put("label", brand.getName());
brandList.add(b);
}
Map<String, Object> 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<LitemallGoodsProduct> products = productService.queryByGid(id);
List<LitemallGoodsSpecification> specifications = specificationService.queryByGid(id);
List<LitemallGoodsAttribute> 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<String, Object> 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);
}
}
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<Short> orderStatusArray,
Integer page, Integer limit, String sort, String order) {
List<LitemallOrder> orderList = orderService.querySelective(userId, orderSn, orderStatusArray, page, limit, sort, order);
int total = orderService.countSelective(userId, orderSn, orderStatusArray, page, limit, sort, order);
Map<String, Object> 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<LitemallOrderGoods> orderGoods = orderGoodsService.queryByOid(id);
UserVo user = userService.findUserVoById(order.getUserId());
Map<String, Object> data = new HashMap<>();
data.put("order", order);
data.put("orderGoods", orderGoods);
data.put("user", user);
return ResponseUtil.ok(data);
}
/**
* 订单退款
* <p>
* 1. 检测当前订单是否能够退款;
* 2. 微信退款操作;
* 3. 设置订单退款确认状态;
* 4. 订单商品库存回库。
* <p>
* 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<LitemallOrderGoods> 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();
}
}
......@@ -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<LitemallGoods> goodsList = goodsService.querySelective(goodsSn, name, page, limit, sort, order);
int total = goodsService.countSelective(goodsSn, name, page, limit, sort, order);
Map<String, Object> 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);
}
/**
* 编辑商品
* <p>
* 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<LitemallCategory> l1CatList = categoryService.queryL1();
List<CatVo> categoryList = new ArrayList<>(l1CatList.size());
for (LitemallCategory l1 : l1CatList) {
CatVo l1CatVo = new CatVo();
l1CatVo.setValue(l1.getId());
l1CatVo.setLabel(l1.getName());
List<LitemallCategory> l2CatList = categoryService.queryByPid(l1.getId());
List<CatVo> 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<LitemallBrand> list = brandService.all();
List<Map<String, Object>> brandList = new ArrayList<>(l1CatList.size());
for (LitemallBrand brand : list) {
Map<String, Object> b = new HashMap<>(2);
b.put("value", brand.getId());
b.put("label", brand.getName());
brandList.add(b);
}
Map<String, Object> 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<LitemallGoodsProduct> products = productService.queryByGid(id);
List<LitemallGoodsSpecification> specifications = specificationService.queryByGid(id);
List<LitemallGoodsAttribute> 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<String, Object> data = new HashMap<>();
data.put("goods", goods);
data.put("specifications", specifications);
data.put("products", products);
data.put("attributes", attributes);
data.put("categoryIds", categoryIds);
return adminGoodsService.detail(id);
return ResponseUtil.ok(data);
}
}
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<Short> 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<LitemallOrder> orderList = orderService.querySelective(userId, orderSn, orderStatusArray, page, limit, sort, order);
int total = orderService.countSelective(userId, orderSn, orderStatusArray, page, limit, sort, order);
Map<String, Object> 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<LitemallOrderGoods> orderGoods = orderGoodsService.queryByOid(id);
UserVo user = userService.findUserVoById(order.getUserId());
Map<String, Object> data = new HashMap<>();
data.put("order", order);
data.put("orderGoods", orderGoods);
data.put("user", user);
return ResponseUtil.ok(data);
return adminOrderService.detail(id);
}
/**
* 订单退款
* <p>
* 1. 检测当前订单是否能够退款;
* 2. 微信退款操作;
* 3. 设置订单退款确认状态;
* 4. 订单商品库存回库。
* <p>
* 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<LitemallOrderGoods> 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);
}
}
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.*;
/**
* 订单服务
*
* <p>
* 订单状态:
* 101 订单生成,未支付;102,下单后未支付用户取消;103,下单后未支付超时系统自动取消
* 201 支付完成,商家未发货;202,订单生产,已付款未发货,但是退款取消;
* 301 商家发货,用户未确认;
* 401 用户确认收货; 402 用户没有确认收货超过一定时间,系统自动确认收货;
*
* <p>
* 用户操作:
* 当101用户未付款时,此时用户可以进行的操作是取消订单,或者付款操作
* 当201支付完成而商家未发货时,此时用户可以取消订单并申请退款
* 当301商家已发货时,此时用户可以有确认收货的操作
* 当401用户确认收货以后,此时用户可以进行的操作是删除订单,评价商品,或者再次购买
* 当402系统自动确认收货以后,此时用户可以删除订单,评价商品,或者再次购买
*
* <p>
* 注意:目前不支持订单退货和售后服务
*/
@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<Short> orderStatus = OrderUtil.orderStatus(showType);
List<LitemallOrder> orderList = orderService.queryByOrderStatus(userId, orderStatus);
int count = orderService.countByOrderStatus(userId, orderStatus);
List<Map<String, Object>> orderVoList = new ArrayList<>(orderList.size());
for (LitemallOrder order : orderList) {
Map<String, Object> 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<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());
List<Map<String, Object>> orderGoodsVoList = new ArrayList<>(orderGoodsList.size());
for (LitemallOrderGoods orderGoods : orderGoodsList) {
Map<String, Object> 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<String, Object> 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<String, Object> orderVo = new HashMap<String, Object>();
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<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());
Map<String, Object> 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);
}
/**
* 提交订单
* <p>
* 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<LitemallCart> 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<String, Object> data = new HashMap<>();
data.put("orderId", orderId);
return ResponseUtil.ok(data);
}
/**
* 取消订单
* <p>
* 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<LitemallOrderGoods> 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();
}
/**
* 付款订单的预支付会话标识
* <p>
* 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);
}
/**
* 微信付款成功或失败回调接口
* <p>
* 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("处理成功!");
}
/**
* 订单申请退款
* <p>
* 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();
}
/**
* 确认收货
* <p>
* 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();
}
/**
* 删除订单
* <p>
* 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<LitemallOrderGoods> 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);
}
/**
* 评价订单商品
* <p>
* 确认商品收货或者系统自动确认商品收货后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<String> 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
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.*;
/**
* 订单服务
*
* <p>
* 订单状态:
* 101 订单生成,未支付;102,下单后未支付用户取消;103,下单后未支付超时系统自动取消
* 201 支付完成,商家未发货;202,订单生产,已付款未发货,但是退款取消;
* 301 商家发货,用户未确认;
* 401 用户确认收货; 402 用户没有确认收货超过一定时间,系统自动确认收货;
*
* <p>
* 用户操作:
* 当101用户未付款时,此时用户可以进行的操作是取消订单,或者付款操作
* 当201支付完成而商家未发货时,此时用户可以取消订单并申请退款
* 当301商家已发货时,此时用户可以有确认收货的操作
* 当401用户确认收货以后,此时用户可以进行的操作是删除订单,评价商品,或者再次购买
* 当402系统自动确认收货以后,此时用户可以删除订单,评价商品,或者再次购买
*
* <p>
* 注意:目前不支持订单退货和售后服务
*/
@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<Short> orderStatus = OrderUtil.orderStatus(showType);
List<LitemallOrder> orderList = orderService.queryByOrderStatus(userId, orderStatus);
int count = orderService.countByOrderStatus(userId, orderStatus);
List<Map<String, Object>> orderVoList = new ArrayList<>(orderList.size());
for (LitemallOrder order : orderList) {
Map<String, Object> 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<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());
List<Map<String, Object>> orderGoodsVoList = new ArrayList<>(orderGoodsList.size());
for (LitemallOrderGoods orderGoods : orderGoodsList) {
Map<String, Object> 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<String, Object> 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<String, Object> orderVo = new HashMap<String, Object>();
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<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());
Map<String, Object> 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);
}
/**
* 提交订单
* <p>
* 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<LitemallCart> 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<String, Object> data = new HashMap<>();
data.put("orderId", orderId);
return ResponseUtil.ok(data);
return wxOrderService.submit(userId, body);
}
/**
* 取消订单
* <p>
* 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<LitemallOrderGoods> 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);
}
/**
* 付款订单的预支付会话标识
* <p>
* 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);
}
/**
* 微信付款成功或失败回调接口
* <p>
* 1. 检测当前订单是否是付款状态;
* 2. 设置订单付款成功状态相关信息;
* 3. 响应微信商户平台.
* <p>
* 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);
}
/**
* 订单申请退款
* <p>
* 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);
}
/**
* 确认收货
* <p>
* 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);
}
/**
* 删除订单
* <p>
* 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<LitemallOrderGoods> 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);
}
/**
* 评价订单商品
* <p>
* 确认商品收货或者系统自动确认商品收货后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<String> 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
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment