应用程序主函数接口

@SpringBootApplication(scanBasePackages = {"org.linlinjava.litemall.db", "org.linlinjava.litemall.core", "org.linlinjava.litemall.admin"})

@MapperScan("org.linlinjava.litemall.db.dao")

@EnableTransactionManagement

@EnableScheduling

public class Application {

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

}

优惠卷管理

/**

* 检测优惠券过期情况

*/

@Component

public class CouponJob {

private final Log logger = LogFactory.getLog(CouponJob.class);

@Autowired

private LitemallCouponService couponService;

@Autowired

private LitemallCouponUserService couponUserService;

/**

* 每隔一个小时检查

* TODO

* 注意,因为是相隔一个小时检查,因此导致优惠券真正超时时间可能比设定时间延迟1个小时

*/

@Scheduled(fixedDelay = 60 * 60 * 1000)

public void checkCouponExpired() {

logger.info("系统开启任务检查优惠券是否已经过期");

List<LitemallCoupon> couponList = couponService.queryExpired();

for(LitemallCoupon coupon : couponList){

coupon.setStatus(CouponConstant.STATUS_EXPIRED);

couponService.updateById(coupon);

}

List<LitemallCouponUser> couponUserList = couponUserService.queryExpired();

for(LitemallCouponUser couponUser : couponUserList){

couponUser.setStatus(CouponUserConstant.STATUS_EXPIRED);

couponUserService.update(couponUser);

}

}

}

订单管理

/**

* 检测订单状态

*/

@Component

public class OrderJob {

private final Log logger = LogFactory.getLog(OrderJob.class);

@Autowired

private LitemallOrderGoodsService orderGoodsService;

@Autowired

private LitemallOrderService orderService;

@Autowired

private LitemallGoodsProductService productService;

/**

* 自动取消订单

* <p>

* 定时检查订单未付款情况,如果超时 LITEMALL_ORDER_UNPAID 分钟则自动取消订单

* 定时时间是每次相隔半个小时。

* <p>

* TODO

* 注意,因为是相隔半小时检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_UNPAID, 30 + LITEMALL_ORDER_UNPAID]

*/

@Scheduled(fixedDelay = 30 * 60 * 1000)

@Transactional

public void checkOrderUnpaid() {

logger.info("系统开启任务检查订单是否已经超期自动取消订单");

List<LitemallOrder> orderList = orderService.queryUnpaid(SystemConfig.getOrderUnpaid());

for (LitemallOrder order : orderList) {

// 设置订单已取消状态

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();

Short number = orderGoods.getNumber();

if (productService.addStock(productId, number) == 0) {

throw new RuntimeException("商品货品库存增加失败");

}

}

logger.info("订单 ID=" + order.getId() + " 已经超期自动取消订单");

}

}

/**

* 自动确认订单

* <p>

* 定时检查订单未确认情况,如果超时 LITEMALL_ORDER_UNCONFIRM 天则自动确认订单

* 定时时间是每天凌晨3点。

* <p>

* TODO

* 注意,因为是相隔一天检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_UNCONFIRM, 1 + LITEMALL_ORDER_UNCONFIRM]

*/

@Scheduled(cron = "0 0 3 * * ?")

public void checkOrderUnconfirm() {

logger.info("系统开启任务检查订单是否已经超期自动确认收货");

List<LitemallOrder> orderList = orderService.queryUnconfirm(SystemConfig.getOrderUnconfirm());

for (LitemallOrder order : orderList) {

// 设置订单已取消状态

order.setOrderStatus(OrderUtil.STATUS_AUTO_CONFIRM);

order.setConfirmTime(LocalDateTime.now());

if (orderService.updateWithOptimisticLocker(order) == 0) {

logger.info("订单 ID=" + order.getId() + " 数据已经更新,放弃自动确认收货");

} else {

logger.info("订单 ID=" + order.getId() + " 已经超期自动确认收货");

}

}

}

/**

* 可评价订单商品超期

* <p>

* 定时检查订单商品评价情况,如果确认商品超时 LITEMALL_ORDER_COMMENT 天则取消可评价状态

* 定时时间是每天凌晨4点。

* <p>

* TODO

* 注意,因为是相隔一天检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_COMMENT, 1 + LITEMALL_ORDER_COMMENT]

*/

@Scheduled(cron = "0 0 4 * * ?")

public void checkOrderComment() {

logger.info("系统开启任务检查订单是否已经超期未评价");

LocalDateTime now = LocalDateTime.now();

List<LitemallOrder> orderList = orderService.queryComment(SystemConfig.getOrderComment());

for (LitemallOrder order : orderList) {

order.setComments((short) 0);

orderService.updateWithOptimisticLocker(order);

List<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());

for (LitemallOrderGoods orderGoods : orderGoodsList) {

orderGoods.setComment(-1);

orderGoodsService.updateById(orderGoods);

}

}

}

}

上架商品管理服务

@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);

return ResponseUtil.okList(goodsList);

}

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();

//将生成的分享图片地址写入数据库

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);

}

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);

}

}

订单管理服务

@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;

@Autowired

private LogHelper logHelper;

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);

return ResponseUtil.okList(orderList);

}

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)});

logHelper.logOrderSucceed("退款", "订单编号 " + orderId);

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});

logHelper.logOrderSucceed("发货", "订单编号 " + orderId);

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()

}

}

操作日志管理服务

/**

* 这里的日志类型设计成四种(当然开发者需要可以自己扩展)

* 一般日志:用户觉得需要查看的一般操作日志,建议是默认的日志级别

* 安全日志:用户安全相关的操作日志,例如登录、删除管理员

* 订单日志:用户交易相关的操作日志,例如订单发货、退款

* 其他日志:如果以上三种不合适,可以选择其他日志,建议是优先级最低的日志级别

*

* 当然可能很多操作是不需要记录到数据库的,例如编辑商品、编辑广告品之类。

*/

@Component

public class LogHelper {

public final static Integer LOG_TYPE_GENERAL = 0;

public final static Integer LOG_TYPE_AUTH = 1;

public final static Integer LOG_TYPE_ORDER = 2;

public final static Integer LOG_TYPE_OTHER = 3;

@Autowired

private LitemallLogService logService;

public void logGeneralSucceed(String action){

logAdmin(LOG_TYPE_GENERAL, action, true, "", "");

}

public void logGeneralSucceed(String action, String result){

logAdmin(LOG_TYPE_GENERAL, action, true, result, "");

}

public void logGeneralFail(String action, String error){

logAdmin(LOG_TYPE_GENERAL, action, false, error, "");

}

public void logAuthSucceed(String action){

logAdmin(LOG_TYPE_AUTH, action, true, "", "");

}

public void logAuthSucceed(String action, String result){

logAdmin(LOG_TYPE_AUTH, action, true, result, "");

}

public void logAuthFail(String action, String error){

logAdmin(LOG_TYPE_AUTH, action, false, error, "");

}

public void logOrderSucceed(String action){

logAdmin(LOG_TYPE_ORDER, action, true, "", "");

}

public void logOrderSucceed(String action, String result){

logAdmin(LOG_TYPE_ORDER, action, true, result, "");

}

public void logOrderFail(String action, String error){

logAdmin(LOG_TYPE_ORDER, action, false, error, "");

}

public void logOtherSucceed(String action){

logAdmin(LOG_TYPE_OTHER, action, true, "", "");

}

public void logOtherSucceed(String action, String result){

logAdmin(LOG_TYPE_OTHER, action, true, result, "");

}

public void logOtherFail(String action, String error){

logAdmin(LOG_TYPE_OTHER, action, false, error, "");

}

public void logAdmin (Integer type, String action, Boolean succeed, String result, String comment){

LitemallLog log = new LitemallLog();

Subject currentUser = SecurityUtils.getSubject();

if(currentUser != null) {

LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();

if(admin != null) {

log.setAdmin(admin.getUsername());

}

else{

log.setAdmin("匿名用户");

}

}

else{

log.setAdmin("匿名用户");

}

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

if(request != null) {

log.setIp(IpUtil.getIpAddr(request));

}

log.setType(type);

log.setAction(action);

log.setStatus(succeed);

log.setResult(result);

log.setComment(comment);

logService.add(log);

}

}

顾客(会员)身份认证

public class AdminAuthorizingRealm extends AuthorizingRealm {

private static final Logger log = LoggerFactory.getLogger(AdminAuthorizingRealm.class);

@Autowired

private LitemallAdminService adminService;

@Autowired

private LitemallRoleService roleService;

@Autowired

private LitemallPermissionService permissionService;

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

if (principals == null) {

throw new AuthorizationException("PrincipalCollection method argument cannot be null.");

}

LitemallAdmin admin = (LitemallAdmin) getAvailablePrincipal(principals);

Integer[] roleIds = admin.getRoleIds();

Set<String> roles = roleService.queryByIds(roleIds);

Set<String> permissions = permissionService.queryByRoleIds(roleIds);

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

info.setRoles(roles);

info.setStringPermissions(permissions);

return info;

}

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

UsernamePasswordToken upToken = (UsernamePasswordToken) token;

String username = upToken.getUsername();

String password=new String(upToken.getPassword());

if (StringUtils.isEmpty(username)) {

throw new AccountException("用户名不能为空");

}

if (StringUtils.isEmpty(password)) {

throw new AccountException("密码不能为空");

}

List<LitemallAdmin> adminList = adminService.findAdmin(username);

Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");

if (adminList.size() == 0) {

throw new UnknownAccountException("找不到用户("+username+")的帐号信息");

}

LitemallAdmin admin = adminList.get(0);

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

if (!encoder.matches(password, admin.getPassword())) {

throw new UnknownAccountException("找不到用户("+username+")的帐号信息");

}

return new SimpleAuthenticationInfo(admin,password,getName());

}

}

广告管理控制器

Ps:其他控制器与此类似,不在一一冗赘

@RestController

@RequestMapping("/admin/ad")

@Validated

public class AdminAdController {

private final Log logger = LogFactory.getLog(AdminAdController.class);

@Autowired

private LitemallAdService adService;

@RequiresPermissions("admin:ad:list")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="查询")

@GetMapping("/list")

public Object list(String name, String content,

@RequestParam(defaultValue = "1") Integer page,

@RequestParam(defaultValue = "10") Integer limit,

@Sort @RequestParam(defaultValue = "add_time") String sort,

@Order @RequestParam(defaultValue = "desc") String order) {

List<LitemallAd> adList = adService.querySelective(name, content, page, limit, sort, order);

return ResponseUtil.okList(adList);

}

private Object validate(LitemallAd ad) {

String name = ad.getName();

if (StringUtils.isEmpty(name)) {

return ResponseUtil.badArgument();

}

String content = ad.getContent();

if (StringUtils.isEmpty(content)) {

return ResponseUtil.badArgument();

}

return null;

}

@RequiresPermissions("admin:ad:create")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="添加")

@PostMapping("/create")

public Object create(@RequestBody LitemallAd ad) {

Object error = validate(ad);

if (error != null) {

return error;

}

adService.add(ad);

return ResponseUtil.ok(ad);

}

@RequiresPermissions("admin:ad:read")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="详情")

@GetMapping("/read")

public Object read(@NotNull Integer id) {

LitemallAd ad = adService.findById(id);

return ResponseUtil.ok(ad);

}

@RequiresPermissions("admin:ad:update")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="编辑")

@PostMapping("/update")

public Object update(@RequestBody LitemallAd ad) {

Object error = validate(ad);

if (error != null) {

return error;

}

if (adService.updateById(ad) == 0) {

return ResponseUtil.updatedDataFailed();

}

return ResponseUtil.ok(ad);

}

@RequiresPermissions("admin:ad:delete")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="删除")

@PostMapping("/delete")

public Object delete(@RequestBody LitemallAd ad) {

Integer id = ad.getId();

if (id == null) {

return ResponseUtil.badArgument();

}

adService.deleteById(id);

return ResponseUtil.ok();

}

}

数据库操作部分

Ps:其他操作和对商品品牌数据库表操作类似,再次不在冗赘。

商品品牌数据库表操作

@Service

publicclassLitemallBrandService {

@Resource

privateLitemallBrandMapperbrandMapper;

privateColumn[] columns = newColumn[]{Column.id, Column.name, Column.desc, Column.picUrl, Column.floorPrice};

publicList<LitemallBrand> query(Integerpage, Integerlimit, Stringsort, Stringorder) {

LitemallBrandExampleexample = newLitemallBrandExample();

example.or().andDeletedEqualTo(false);

if (!StringUtils.isEmpty(sort) && !StringUtils.isEmpty(order)) {

example.setOrderByClause(sort + " " + order);

}

PageHelper.startPage(page, limit);

returnbrandMapper.selectByExampleSelective(example, columns);

}

publicList<LitemallBrand> query(Integerpage, Integerlimit) {

returnquery(page, limit, null, null);

}

publicLitemallBrandfindById(Integerid) {

returnbrandMapper.selectByPrimaryKey(id);

}

publicList<LitemallBrand> querySelective(Stringid, Stringname, Integerpage, Integersize, Stringsort, Stringorder) {

LitemallBrandExampleexample = newLitemallBrandExample();

LitemallBrandExample.Criteriacriteria = example.createCriteria();

if (!StringUtils.isEmpty(id)) {

criteria.andIdEqualTo(Integer.valueOf(id));

}

if (!StringUtils.isEmpty(name)) {

criteria.andNameLike("%" + name + "%");

}

criteria.andDeletedEqualTo(false);

if (!StringUtils.isEmpty(sort) && !StringUtils.isEmpty(order)) {

example.setOrderByClause(sort + " " + order);

}

PageHelper.startPage(page, size);

returnbrandMapper.selectByExample(example);

}

publicintupdateById(LitemallBrandbrand) {

brand.setUpdateTime(LocalDateTime.now());

returnbrandMapper.updateByPrimaryKeySelective(brand);

}

publicvoiddeleteById(Integerid) {

brandMapper.logicalDeleteByPrimaryKey(id);

}

publicvoidadd(LitemallBrandbrand) {

brand.setAddTime(LocalDateTime.now());

brand.setUpdateTime(LocalDateTime.now());

brandMapper.insertSelective(brand);

}

publicList<LitemallBrand> all() {

LitemallBrandExampleexample = newLitemallBrandExample();

example.or().andDeletedEqualTo(false);

returnbrandMapper.selectByExample(example);

}

}

核心操作部分

Ps:其他核心操作同存储(商品信息、图片对象等)操作、物流查询服务类似,不在冗赘。

存储操作

/**

* 对象存储接口

*/

publicinterfaceStorage {

/**

* 存储一个文件对象

*

* @paraminputStream   文件输入流

* @paramcontentLength文件长度

* @paramcontentType   文件类型

* @paramkeyName       文件名

*/

voidstore(InputStreaminputStream, longcontentLength, StringcontentType, StringkeyName);

Stream<Path> loadAll();

Pathload(StringkeyName);

ResourceloadAsResource(StringkeyName);

voiddelete(StringkeyName);

StringgenerateUrl(StringkeyName);

}

物流查询服务

/**

* 物流查询服务

*

* 快递鸟即时查询API http://www.kdniao.com/api-track

*/

public class ExpressService {

//请求url

private String ReqURL = "http://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";

private ExpressProperties properties;

public ExpressProperties getProperties() {

return properties;

}

public void setProperties(ExpressProperties properties) {

this.properties = properties;

}

/**

* 获取物流供应商名

*

* @param vendorCode

* @return

*/

public String getVendorName(String vendorCode) {

for (Map<String, String> item : properties.getVendors()) {

if (item.get("code").equals(vendorCode))

return item.get("name");

}

return null;

}

/**

* 获取物流信息

*

* @param expCode

* @param expNo

* @return

*/

public ExpressInfo getExpressInfo(String expCode, String expNo) {

try {

String result = getOrderTracesByJson(expCode, expNo);

ObjectMapper objMap = new ObjectMapper();

ExpressInfo ei = objMap.readValue(result, ExpressInfo.class);

ei.setShipperName(getVendorName(expCode));

return ei;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* Json方式 查询订单物流轨迹

*

* @throws Exception

*/

private String getOrderTracesByJson(String expCode, String expNo) throws Exception {

if (!properties.isEnable()) {

return null;

}

String requestData = "{'OrderCode':'','ShipperCode':'" + expCode + "','LogisticCode':'" + expNo + "'}";

Map<String, String> params = new HashMap<String, String>();

params.put("RequestData", URLEncoder.encode(requestData, "UTF-8"));

params.put("EBusinessID", properties.getAppId());

params.put("RequestType", "1002");

String dataSign = encrypt(requestData, properties.getAppKey(), "UTF-8");

params.put("DataSign", URLEncoder.encode(dataSign, "UTF-8"));

params.put("DataType", "2");

String result = HttpUtil.sendPost(ReqURL, params);

//根据公司业务处理返回的信息......

return result;

}

/**

* MD5加密

*

* @param str     内容

* @param charset 编码方式

* @throws Exception

*/

private String MD5(String str, String charset) throws Exception {

MessageDigest md = MessageDigest.getInstance("MD5");

md.update(str.getBytes(charset));

byte[] result = md.digest();

StringBuffer sb = new StringBuffer(32);

for (int i = 0; i < result.length; i++) {

int val = result[i] & 0xff;

if (val <= 0xf) {

sb.append("0");

}

sb.append(Integer.toHexString(val));

}

return sb.toString().toLowerCase();

}

/**

* Sign签名生成

*

* @param content  内容

* @param keyValue Appkey

* @param charset  编码方式

* @return DataSign签名

*/

private String encrypt(String content, String keyValue, String charset) {

if (keyValue != null) {

content = content + keyValue;

}

byte[] src = new byte[0];

try {

src = MD5(content, charset).getBytes(charset);

return Base64Utils.encodeToString(src);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

迷你商城后端管理

应用程序主函数接口

@SpringBootApplication(scanBasePackages = {"org.linlinjava.litemall.db", "org.linlinjava.litemall.core", "org.linlinjava.litemall.admin"})

@MapperScan("org.linlinjava.litemall.db.dao")

@EnableTransactionManagement

@EnableScheduling

public class Application {

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

}

优惠卷管理

/**

* 检测优惠券过期情况

*/

@Component

public class CouponJob {

private final Log logger = LogFactory.getLog(CouponJob.class);

@Autowired

private LitemallCouponService couponService;

@Autowired

private LitemallCouponUserService couponUserService;

/**

* 每隔一个小时检查

* TODO

* 注意,因为是相隔一个小时检查,因此导致优惠券真正超时时间可能比设定时间延迟1个小时

*/

@Scheduled(fixedDelay = 60 * 60 * 1000)

public void checkCouponExpired() {

logger.info("系统开启任务检查优惠券是否已经过期");

List<LitemallCoupon> couponList = couponService.queryExpired();

for(LitemallCoupon coupon : couponList){

coupon.setStatus(CouponConstant.STATUS_EXPIRED);

couponService.updateById(coupon);

}

List<LitemallCouponUser> couponUserList = couponUserService.queryExpired();

for(LitemallCouponUser couponUser : couponUserList){

couponUser.setStatus(CouponUserConstant.STATUS_EXPIRED);

couponUserService.update(couponUser);

}

}

}

订单管理

/**

* 检测订单状态

*/

@Component

public class OrderJob {

private final Log logger = LogFactory.getLog(OrderJob.class);

@Autowired

private LitemallOrderGoodsService orderGoodsService;

@Autowired

private LitemallOrderService orderService;

@Autowired

private LitemallGoodsProductService productService;

/**

* 自动取消订单

* <p>

* 定时检查订单未付款情况,如果超时 LITEMALL_ORDER_UNPAID 分钟则自动取消订单

* 定时时间是每次相隔半个小时。

* <p>

* TODO

* 注意,因为是相隔半小时检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_UNPAID, 30 + LITEMALL_ORDER_UNPAID]

*/

@Scheduled(fixedDelay = 30 * 60 * 1000)

@Transactional

public void checkOrderUnpaid() {

logger.info("系统开启任务检查订单是否已经超期自动取消订单");

List<LitemallOrder> orderList = orderService.queryUnpaid(SystemConfig.getOrderUnpaid());

for (LitemallOrder order : orderList) {

// 设置订单已取消状态

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();

Short number = orderGoods.getNumber();

if (productService.addStock(productId, number) == 0) {

throw new RuntimeException("商品货品库存增加失败");

}

}

logger.info("订单 ID=" + order.getId() + " 已经超期自动取消订单");

}

}

/**

* 自动确认订单

* <p>

* 定时检查订单未确认情况,如果超时 LITEMALL_ORDER_UNCONFIRM 天则自动确认订单

* 定时时间是每天凌晨3点。

* <p>

* TODO

* 注意,因为是相隔一天检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_UNCONFIRM, 1 + LITEMALL_ORDER_UNCONFIRM]

*/

@Scheduled(cron = "0 0 3 * * ?")

public void checkOrderUnconfirm() {

logger.info("系统开启任务检查订单是否已经超期自动确认收货");

List<LitemallOrder> orderList = orderService.queryUnconfirm(SystemConfig.getOrderUnconfirm());

for (LitemallOrder order : orderList) {

// 设置订单已取消状态

order.setOrderStatus(OrderUtil.STATUS_AUTO_CONFIRM);

order.setConfirmTime(LocalDateTime.now());

if (orderService.updateWithOptimisticLocker(order) == 0) {

logger.info("订单 ID=" + order.getId() + " 数据已经更新,放弃自动确认收货");

} else {

logger.info("订单 ID=" + order.getId() + " 已经超期自动确认收货");

}

}

}

/**

* 可评价订单商品超期

* <p>

* 定时检查订单商品评价情况,如果确认商品超时 LITEMALL_ORDER_COMMENT 天则取消可评价状态

* 定时时间是每天凌晨4点。

* <p>

* TODO

* 注意,因为是相隔一天检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_COMMENT, 1 + LITEMALL_ORDER_COMMENT]

*/

@Scheduled(cron = "0 0 4 * * ?")

public void checkOrderComment() {

logger.info("系统开启任务检查订单是否已经超期未评价");

LocalDateTime now = LocalDateTime.now();

List<LitemallOrder> orderList = orderService.queryComment(SystemConfig.getOrderComment());

for (LitemallOrder order : orderList) {

order.setComments((short) 0);

orderService.updateWithOptimisticLocker(order);

List<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());

for (LitemallOrderGoods orderGoods : orderGoodsList) {

orderGoods.setComment(-1);

orderGoodsService.updateById(orderGoods);

}

}

}

}

上架商品管理服务

@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);

return ResponseUtil.okList(goodsList);

}

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();

//将生成的分享图片地址写入数据库

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);

}

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);

}

}

订单管理服务

@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;

@Autowired

private LogHelper logHelper;

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);

return ResponseUtil.okList(orderList);

}

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)});

logHelper.logOrderSucceed("退款", "订单编号 " + orderId);

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});

logHelper.logOrderSucceed("发货", "订单编号 " + orderId);

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();

}

}

操作日志管理服务

/**

* 这里的日志类型设计成四种(当然开发者需要可以自己扩展)

* 一般日志:用户觉得需要查看的一般操作日志,建议是默认的日志级别

* 安全日志:用户安全相关的操作日志,例如登录、删除管理员

* 订单日志:用户交易相关的操作日志,例如订单发货、退款

* 其他日志:如果以上三种不合适,可以选择其他日志,建议是优先级最低的日志级别

*

* 当然可能很多操作是不需要记录到数据库的,例如编辑商品、编辑广告品之类。

*/

@Component

public class LogHelper {

public final static Integer LOG_TYPE_GENERAL = 0;

public final static Integer LOG_TYPE_AUTH = 1;

public final static Integer LOG_TYPE_ORDER = 2;

public final static Integer LOG_TYPE_OTHER = 3;

@Autowired

private LitemallLogService logService;

public void logGeneralSucceed(String action){

logAdmin(LOG_TYPE_GENERAL, action, true, "", "");

}

public void logGeneralSucceed(String action, String result){

logAdmin(LOG_TYPE_GENERAL, action, true, result, "");

}

public void logGeneralFail(String action, String error){

logAdmin(LOG_TYPE_GENERAL, action, false, error, "");

}

public void logAuthSucceed(String action){

logAdmin(LOG_TYPE_AUTH, action, true, "", "");

}

public void logAuthSucceed(String action, String result){

logAdmin(LOG_TYPE_AUTH, action, true, result, "");

}

public void logAuthFail(String action, String error){

logAdmin(LOG_TYPE_AUTH, action, false, error, "");

}

public void logOrderSucceed(String action){

logAdmin(LOG_TYPE_ORDER, action, true, "", "");

}

public void logOrderSucceed(String action, String result){

logAdmin(LOG_TYPE_ORDER, action, true, result, "");

}

public void logOrderFail(String action, String error){

logAdmin(LOG_TYPE_ORDER, action, false, error, "");

}

public void logOtherSucceed(String action){

logAdmin(LOG_TYPE_OTHER, action, true, "", "");

}

public void logOtherSucceed(String action, String result){

logAdmin(LOG_TYPE_OTHER, action, true, result, "");

}

public void logOtherFail(String action, String error){

logAdmin(LOG_TYPE_OTHER, action, false, error, "");

}

public void logAdmin (Integer type, String action, Boolean succeed, String result, String comment){

LitemallLog log = new LitemallLog();

Subject currentUser = SecurityUtils.getSubject();

if(currentUser != null) {

LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();

if(admin != null) {

log.setAdmin(admin.getUsername());

}

else{

log.setAdmin("匿名用户");

}

}

else{

log.setAdmin("匿名用户");

}

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

if(request != null) {

log.setIp(IpUtil.getIpAddr(request));

}

log.setType(type);

log.setAction(action);

log.setStatus(succeed);

log.setResult(result);

log.setComment(comment);

logService.add(log);

}

}

顾客(会员)身份认证

public class AdminAuthorizingRealm extends AuthorizingRealm {

private static final Logger log = LoggerFactory.getLogger(AdminAuthorizingRealm.class);

@Autowired

private LitemallAdminService adminService;

@Autowired

private LitemallRoleService roleService;

@Autowired

private LitemallPermissionService permissionService;

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

if (principals == null) {

throw new AuthorizationException("PrincipalCollection method argument cannot be null.");

}

LitemallAdmin admin = (LitemallAdmin) getAvailablePrincipal(principals);

Integer[] roleIds = admin.getRoleIds();

Set<String> roles = roleService.queryByIds(roleIds);

Set<String> permissions = permissionService.queryByRoleIds(roleIds);

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

info.setRoles(roles);

info.setStringPermissions(permissions);

return info;

}

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

UsernamePasswordToken upToken = (UsernamePasswordToken) token;

String username = upToken.getUsername();

String password=new String(upToken.getPassword());

if (StringUtils.isEmpty(username)) {

throw new AccountException("用户名不能为空");

}

if (StringUtils.isEmpty(password)) {

throw new AccountException("密码不能为空");

}

List<LitemallAdmin> adminList = adminService.findAdmin(username);

Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");

if (adminList.size() == 0) {

throw new UnknownAccountException("找不到用户("+username+")的帐号信息");

}

LitemallAdmin admin = adminList.get(0);

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

if (!encoder.matches(password, admin.getPassword())) {

throw new UnknownAccountException("找不到用户("+username+")的帐号信息");

}

return new SimpleAuthenticationInfo(admin,password,getName());

}

}

广告管理控制器

Ps:其他控制器与此类似,不在一一冗赘

@RestController

@RequestMapping("/admin/ad")

@Validated

public class AdminAdController {

private final Log logger = LogFactory.getLog(AdminAdController.class);

@Autowired

private LitemallAdService adService;

@RequiresPermissions("admin:ad:list")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="查询")

@GetMapping("/list")

public Object list(String name, String content,

@RequestParam(defaultValue = "1") Integer page,

@RequestParam(defaultValue = "10") Integer limit,

@Sort @RequestParam(defaultValue = "add_time") String sort,

@Order @RequestParam(defaultValue = "desc") String order) {

List<LitemallAd> adList = adService.querySelective(name, content, page, limit, sort, order);

return ResponseUtil.okList(adList);

}

private Object validate(LitemallAd ad) {

String name = ad.getName();

if (StringUtils.isEmpty(name)) {

return ResponseUtil.badArgument();

}

String content = ad.getContent();

if (StringUtils.isEmpty(content)) {

return ResponseUtil.badArgument();

}

return null;

}

@RequiresPermissions("admin:ad:create")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="添加")

@PostMapping("/create")

public Object create(@RequestBody LitemallAd ad) {

Object error = validate(ad);

if (error != null) {

return error;

}

adService.add(ad);

return ResponseUtil.ok(ad);

}

@RequiresPermissions("admin:ad:read")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="详情")

@GetMapping("/read")

public Object read(@NotNull Integer id) {

LitemallAd ad = adService.findById(id);

return ResponseUtil.ok(ad);

}

@RequiresPermissions("admin:ad:update")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="编辑")

@PostMapping("/update")

public Object update(@RequestBody LitemallAd ad) {

Object error = validate(ad);

if (error != null) {

return error;

}

if (adService.updateById(ad) == 0) {

return ResponseUtil.updatedDataFailed();

}

return ResponseUtil.ok(ad);

}

@RequiresPermissions("admin:ad:delete")

@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="删除")

@PostMapping("/delete")

public Object delete(@RequestBody LitemallAd ad) {

Integer id = ad.getId();

if (id == null) {

return ResponseUtil.badArgument();

}

adService.deleteById(id);

return ResponseUtil.ok();

}

}

数据库操作部分

Ps:其他操作和对商品品牌数据库表操作类似,再次不在冗赘。

商品品牌数据库表操作

@Service

publicclassLitemallBrandService {

@Resource

privateLitemallBrandMapperbrandMapper;

privateColumn[] columns = newColumn[]{Column.id, Column.name, Column.desc, Column.picUrl, Column.floorPrice};

publicList<LitemallBrand> query(Integerpage, Integerlimit, Stringsort, Stringorder) {

LitemallBrandExampleexample = newLitemallBrandExample();

example.or().andDeletedEqualTo(false);

if (!StringUtils.isEmpty(sort) && !StringUtils.isEmpty(order)) {

example.setOrderByClause(sort + " " + order);

}

PageHelper.startPage(page, limit);

returnbrandMapper.selectByExampleSelective(example, columns);

}

publicList<LitemallBrand> query(Integerpage, Integerlimit) {

returnquery(page, limit, null, null);

}

publicLitemallBrandfindById(Integerid) {

returnbrandMapper.selectByPrimaryKey(id);

}

publicList<LitemallBrand> querySelective(Stringid, Stringname, Integerpage, Integersize, Stringsort, Stringorder) {

LitemallBrandExampleexample = newLitemallBrandExample();

LitemallBrandExample.Criteriacriteria = example.createCriteria();

if (!StringUtils.isEmpty(id)) {

criteria.andIdEqualTo(Integer.valueOf(id));

}

if (!StringUtils.isEmpty(name)) {

criteria.andNameLike("%" + name + "%");

}

criteria.andDeletedEqualTo(false);

if (!StringUtils.isEmpty(sort) && !StringUtils.isEmpty(order)) {

example.setOrderByClause(sort + " " + order);

}

PageHelper.startPage(page, size);

returnbrandMapper.selectByExample(example);

}

publicintupdateById(LitemallBrandbrand) {

brand.setUpdateTime(LocalDateTime.now());

returnbrandMapper.updateByPrimaryKeySelective(brand);

}

publicvoiddeleteById(Integerid) {

brandMapper.logicalDeleteByPrimaryKey(id);

}

publicvoidadd(LitemallBrandbrand) {

brand.setAddTime(LocalDateTime.now());

brand.setUpdateTime(LocalDateTime.now());

brandMapper.insertSelective(brand);

}

publicList<LitemallBrand> all() {

LitemallBrandExampleexample = newLitemallBrandExample();

example.or().andDeletedEqualTo(false);

returnbrandMapper.selectByExample(example);

}

}

核心操作部分

Ps:其他核心操作同存储(商品信息、图片对象等)操作、物流查询服务类似,不在冗赘。

存储操作

/**

* 对象存储接口

*/

publicinterfaceStorage {

/**

* 存储一个文件对象

*

* @paraminputStream   文件输入流

* @paramcontentLength文件长度

* @paramcontentType   文件类型

* @paramkeyName       文件名

*/

voidstore(InputStreaminputStream, longcontentLength, StringcontentType, StringkeyName);

Stream<Path> loadAll();

Pathload(StringkeyName);

ResourceloadAsResource(StringkeyName);

voiddelete(StringkeyName);

StringgenerateUrl(StringkeyName);

}

物流查询服务

/**

* 物流查询服务

*

* 快递鸟即时查询API http://www.kdniao.com/api-track

*/

public class ExpressService {

//请求url

private String ReqURL = "http://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";

private ExpressProperties properties;

public ExpressProperties getProperties() {

return properties;

}

public void setProperties(ExpressProperties properties) {

this.properties = properties;

}

/**

* 获取物流供应商名

*

* @param vendorCode

* @return

*/

public String getVendorName(String vendorCode) {

for (Map<String, String> item : properties.getVendors()) {

if (item.get("code").equals(vendorCode))

return item.get("name");

}

return null;

}

/**

* 获取物流信息

*

* @param expCode

* @param expNo

* @return

*/

public ExpressInfo getExpressInfo(String expCode, String expNo) {

try {

String result = getOrderTracesByJson(expCode, expNo);

ObjectMapper objMap = new ObjectMapper();

ExpressInfo ei = objMap.readValue(result, ExpressInfo.class);

ei.setShipperName(getVendorName(expCode));

return ei;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* Json方式 查询订单物流轨迹

*

* @throws Exception

*/

private String getOrderTracesByJson(String expCode, String expNo) throws Exception {

if (!properties.isEnable()) {

return null;

}

String requestData = "{'OrderCode':'','ShipperCode':'" + expCode + "','LogisticCode':'" + expNo + "'}";

Map<String, String> params = new HashMap<String, String>();

params.put("RequestData", URLEncoder.encode(requestData, "UTF-8"));

params.put("EBusinessID", properties.getAppId());

params.put("RequestType", "1002");

String dataSign = encrypt(requestData, properties.getAppKey(), "UTF-8");

params.put("DataSign", URLEncoder.encode(dataSign, "UTF-8"));

params.put("DataType", "2");

String result = HttpUtil.sendPost(ReqURL, params);

//根据公司业务处理返回的信息......

return result;

}

/**

* MD5加密

*

* @param str     内容

* @param charset 编码方式

* @throws Exception

*/

private String MD5(String str, String charset) throws Exception {

MessageDigest md = MessageDigest.getInstance("MD5");

md.update(str.getBytes(charset));

byte[] result = md.digest();

StringBuffer sb = new StringBuffer(32);

for (int i = 0; i < result.length; i++) {

int val = result[i] & 0xff;

if (val <= 0xf) {

sb.append("0");

}

sb.append(Integer.toHexString(val));

}

return sb.toString().toLowerCase();

}

/**

* Sign签名生成

*

* @param content  内容

* @param keyValue Appkey

* @param charset  编码方式

* @return DataSign签名

*/

private String encrypt(String content, String keyValue, String charset) {

if (keyValue != null) {

content = content + keyValue;

}

byte[] src = new byte[0];

try {

src = MD5(content, charset).getBytes(charset);

return Base64Utils.encodeToString(src);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

转载于:https://www.cnblogs.com/taue997/p/10937262.html

迷你商城后端管理系统 ———— stage2 项目的核心代码实现相关推荐

  1. ProGuard混淆Java项目的核心代码

    开发需求 众所周知,class文件可以进行反编译从而泄露核心代码,为了保护知识产权,需要对代码进行混淆再进行打包.现阶段采用proguard去做一个基本的混淆,使代码的可读性降低. 操作步骤 [模块目 ...

  2. 商城后台管理系统之普通查询_分页查询_商品的添加,单个删除,批量删除

    一.MVC开发模式和JavaEE经典三层结构 1.JSP开发模式一: jsp(接收请求,响应请求,展示数据)+javabean(处理业务逻辑) javaBean:可复用的java组件 -user -u ...

  3. 毕业设计商城后台管理系统

    文章目录 商城后端管理系统 1商品管理 1.1分页查询 1.11controller层 1.12 Service层 1.13查询封装的VO 1.14mapper实现 1.15 sql实现 2地址管理 ...

  4. SSM项目-商城后台管理系统

    SSM项目-商城后台管理系统 开发说明 开发环境 项目界面演示 项目功能 具体的技术指标 开发过程 1.搭建SSM框架 1.1.建库建表 1.2.新建Maven工程 1.3.配置pom.xml 1.4 ...

  5. 商城前后端原型、商城prd文档、商城后台管理系统、商城app文档、电商需求文档、限时秒杀、电商平台、促销助力、拼团抽奖、电商文档、prd文档、电商前后端原型、电商原型、Axure电商系统、rp原型

    商城前后端.商城prd文档.商城后台管理系统.商城app文档.电商需求文档.限时秒杀.电商平台.促销助力.拼团抽奖.电商文档.prd文档.电商前后端原型.电商原型.Axure电商系统.rp原型 Axu ...

  6. JavaWeb源码项目蔬菜网上商城+后台管理系统

    JavaWeb源码项目蔬菜网上商城+后台管理系统 大家好,小辰哥又来啦,今天给大家介绍一个蔬菜网上商城+后台管理系统 文章目录 JavaWeb源码项目蔬菜网上商城+后台管理系统 前言 一.项目简述 二 ...

  7. 京淘商城后台管理系统

    京淘商城 京淘商城后台管理系统 登录.注册界面 商品管理 新增商品 查询商品 规格参数 网站内容管理 内容分类管理 内容管理 账户管理 管理员账户管理 普通用户账户管理 个人信息 习得总结 实习总结 ...

  8. 视频教程-vuecli实战商城后台管理系统-Vue

    vuecli实战商城后台管理系统 帝莎学院创始人&CEO,目前主要从事全栈开发.Python.PHP.小程序.App.Web等技术的研究和开发.专注于实战类教程,授课风趣幽默,讲解条理清晰.通 ...

  9. vue小米商城源代码_微信商城信息管理系统(java后台+小程序)

    前一段时间在公众号中发布了一套微信商城信息管理系统(java后台+小程序),也许大家可能都还记得.今天再给大家重复呈现一遍.重新梳理下,方便大家理解这是做什么用的,以便日后使用.开始正题 01 编写目 ...

  10. 【Lilishop商城】No2-1.确定项目结构和数据结构(用户、商品、订单、促销等模块)

    仅涉及后端,全部目录看顶部专栏,代码.文档.接口路径在: [Lilishop商城]记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 首先先看一下项目的开发架构,都需要哪些技术,都按 ...

最新文章

  1. 基于大数据的Uber数据实时监控(Prat 3:使用Vert.x的实时仪表盘)
  2. 玩转Google开源C++单元测试框架Google Test系列(gtest)(总)
  3. 【深度学习笔记】‘module‘ object has no attribute ‘xfeatures2d‘
  4. Android环信爬坑指北(二)头像昵称好友备注显示
  5. Linux 终端部分重要快捷键
  6. 【学习笔记】智能制造问与答
  7. py获取前端的参数_微前端 qiankun 项目实践
  8. Windows 11 新版 22593 发布:文件资源管理器全新主页,开始菜单图标优化
  9. JS 获取浏览器信息,给出友情提示,避免部分兼容性问题
  10. linux 查看进程的信号,Linux 进程信号查看与控制
  11. 大数据之-Hadoop3.x_MapReduce_outputformat案例需求分析---大数据之hadoop3.x工作笔记0121
  12. 2021年双十一大复盘:众人唱衰双十一,我们却发现了这些机会
  13. Python破解百度翻js代码
  14. 前端为什么有的接口明明是成功回调却执行了.catch失败回调_前端知识整理
  15. swift学习笔记《5》- 实用
  16. vue前端页面数据加载添加loading效果
  17. 微信支付宝扫码支付相关接口
  18. android 焦点移动,Android TV 焦点上下左右移动
  19. 无线传输终端 无线通信模块 全网通5G/4G
  20. 浪潮齐鲁软件产业股份有限公司山东“金质工程”特种设备安全监察信息系统

热门文章

  1. #笔记#微信小程序的bindtap如何传参
  2. 谷歌浏览器调试工具使用进阶(二)
  3. 长期不上班,人会废掉吗?
  4. JustinMind
  5. SQL点滴25—T-SQL面试语句,练练手
  6. 26岁考计算机研究生,26岁考研究生好还是考公务员好?总结得太精辟了
  7. 人工智能技术涉及到的学科有哪些,22年最新
  8. 计算机二级word 文档排版,word排版操作指导(计算机二级2010版)
  9. 利用漏洞溢出掉360安全卫士逆向分析
  10. 与世界有怎样的关系,便拥有了怎样的自己