SEO教程

SEO教程

Products

当前位置:首页 > SEO教程 >

如何优化佳木斯地区的网站流量,利用流量精灵app提升用户体验?

96SEO 2026-02-19 21:12 13


订单请求6、订单确认页模型抽取7、订单确认页vo封装8、Feign

惨痛教训9、Feign

异步调用请求头丢失问题10、查看库存状态11、模拟计算运费12、接口幂等性1什么是接口幂等性2哪些情况要防止接口幂等性3什么情况下需要幂等性4幂等性解决方案token

机制各种锁机制\-

全局唯一id13、订单确认页防重令牌14、提交订单二、分布式事务1、本地事务1事务的基本性质2事务的隔离级别3事务的传播行为SpringBoot

理论5

分布式事务的几种解决方案2PC模式柔性事务-TCC事务补偿型方案柔性事务-

Seata

远程调用问题四、定时关闭订单五、支付服务1、一些概念说明2、沙箱环境测试3、内网穿透4、整合阿里云支付服务5、支付成功同步回调6、异步通知内网穿透环境搭建7、支付完成8、收单六、秒杀1、定时任务与Cron表达式2、秒杀商品上架3、秒杀上架幂等性问题4、查询所有的秒杀商品时区BUG5、查询某一个秒杀

商品6、秒杀系统设计7、秒杀流程七、Sentinel1、熔断降级限流2、SpringBoot整合Sentinel3、实时监控4、自定义sentinel的返回信息5、RabbitTemplate

MyRabbitConfig

循环依赖问题6、熔断降级7、开启自定义的受保护资源8、网关流控9、自定义网关流控回调总结一、订单服务

视频来源:

订单模块是电商系统的枢纽在订单这个环节上需求获取多个模块的数据和信息同时对这

些信息进行加工处理后流向下个环节这一系列就构成了订单的信息流通

2、订单基本构成

用户信息包括用户账号、用户等级、用户的收货地址、收货人、收货人电话等组成用户账

户需要绑定手机号码但是用户绑定的手机号码不一定是收货信息上的电话。

用户可以添加

多个收货信息用户等级信息可以用来和促销系统进行匹配获取商品折扣同时用户等级

2、订单基础信息

订单基础信息是订单流转的核心其包括订单类型、父/子订单、订单编号、订单状态、订

1订单类型包括实体商品订单和虚拟订单商品等这个根据商城商品和服务类型进行区

2同时订单都需要做父子订单处理之前在初创公司一直只有一个订单没有做父子订

单处理后期需要进行拆单的时候就比较麻烦尤其是多商户商场和不同仓库商品的时候

3订单编号不多说了需要强调的一点是父子订单都需要有订单编号需要完善的时候

4订单状态记录订单每次流转过程后面会对订单状态进行单独的说明。

5订单流转时间需要记录下单时间支付时间发货时间结束时间/关闭时间等等

3、商品信息

优惠信息记录用户参与的优惠活动包括优惠促销活动比如满减、满赠、秒杀等用户使

用的优惠券信息优惠券满足条件的优惠券需要默认展示出来具体方式已在之前的优惠券

因为优惠信息只是记录用户使用的条目而支付信息需要加入数据进行计算所以做为区分。

5、支付信息

1支付流水单号这个流水单号是在唤起网关支付后支付通道返回给电商业务平台的支

2支付方式用户使用的支付方式比如微信支付、支付宝支付、钱包支付、快捷支付等。

3商品总金额每个商品加总后的金额运费物流产生的费用优惠总金额包括促

销活动的优惠金额优惠券优惠金额虚拟积分或者虚拟币抵扣的金额会员折扣的金额等

用户实付金额商品总金额运费-优惠总金额

物流信息包括配送方式物流公司物流单号物流状态物流状态可以通过第三方接口来

3、订单状态

用户提交订单后订单进行预下单目前主流电商网站都会唤起支付便于用户快速完成支

付需要注意的是待付款状态下可以对库存进行锁定锁定库存需要配置支付超时时间超

2、已付款/待发货

用户完成订单支付订单系统需要记录支付时间支付流水单号便于对账订单下放到

WMS

仓储将商品出库后订单进入物流环节订单系统需要同步物流信息便于用户实时知悉物

品物流状态

用户确认收货后订单交易完成。

后续支付侧进行结算如果订单存在问题进入售后状态

6、售后中

售后也同样存在各种状态当发起售后申请后生成售后订单售后订单状态为待审核等待

商家审核商家审核通过后订单状态变更为待退货等待用户将商品寄回商家收货后订单

状态更新为待退款状态退款到用户原账户后订单状态更新为售后成功。

4、订单流程

订单流程是指从订单产生到完成整个流转的过程从而行程了一套标准流程规则。

而不同的

产品类型或业务类型在系统中的流程会千差万别比如上面提到的线上实物订单和虚拟订单

O2O

不管类型如何订单都包括正向流程和逆向流程对应的场景就是购买商品和退换货流程正

向流程就是一个正常的网购步骤订单生成–支付订单–卖家发货–确认收货–交易成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-832bKPJ4-1675935821383)(https://images-1313160403.cos.ap-beijing.myqcloud.com/MarkDown/%E7%94%B5%E5%95%86%E8%AE%A2%E5%8D%95%E6%B5%81%E7%A8%8B%E5%9B%BE.png)]

1、订单创建与支付

、支付成功后需要进行拆单根据商品打包方式所在仓库物流等进行拆单

(5)

、修改订单用户没有提交订单可以对订单一些信息进行修改比如配送信息

优惠信息及其他一些订单可修改范围的内容此时只需对数据进行变更即可。

(2)

、订单取消用户主动取消订单和用户超时未支付两种情况下订单都会取消订

单而超时情况是系统自动关闭订单所以在订单支付的响应机制上面要做支付的

限时处理尤其是在前面说的下单减库存的情形下面可以保证快速的释放库存。

另外需要需要处理的是促销优惠中使用的优惠券权益等视平台规则进行相应补

(3)

、退款在待发货订单状态下取消订单时分为缺货退款和用户申请退款。

如果是

全部退款则订单更新为关闭状态若只是做部分退款则订单仍需进行进行同时生

成一条退款的售后订单走退款流程。

退款金额需原路返回用户的账户。

(4)

、发货后的退款发生在仓储货物配送在配送过程中商品遗失用户拒收用户

收货后对商品不满意这样情况下用户发起退款的售后诉求后需要商户进行退款

的审核双方达成一致后系统更新退款状态对订单进行退款操作金额原路返

回用户的账户同时关闭原订单数据。

仅退款情况下暂不考虑仓库系统变化。

如果

发生双方协调不一致情况下可以申请平台客服介入。

在退款订单商户不处理的情

况下系统需要做限期判断比如

订单确认页面再次之前增加拦截器判断用户是否登录如果没有登录跳转到登录界面进行登录。

public

request.getSession().getAttribute(AuthServerConstant.LOGIN_USER);if

(memberRespVo

{request.getSession().setAttribute(msg,请先登录);//

没有进行登录跳转到登录界面登录response.sendRedirect(http://auth.gulimall.com/login.html);return

false;}else{threadLocal.set(memberRespVo);return

true;}}

addInterceptors(InterceptorRegistry

registry)

OrderInterceptor()).addPathPatterns(/**);}

}6、订单确认页模型抽取

1、首先是收货人信息。

有更多地址即有多个收货地址其中有一个默认收货地址

4、优惠信息这里只使用京豆优惠

BigDecimal(item.getCount()))).reduce(BigDecimal::add).get():

new

orderService;GetMapping(/toTrade)public

String

保存订单确认vomodel.addAttribute(orderConfirmData,vo);return

confirm;}

memberFeignService;Autowiredprivate

CartFeignService

cartFeignService;Overridepublic

OrderConfirmVo

OrderInterceptor.threadLocal.get();//

address

memberFeignService.getMemberAddresses(memberRespVo.getId());//

远程调用ListOrderItemVo

cartFeignService.getUserCartItems();//

3、积分信息orderConfirmVo.setIntegration(memberRespVo.getIntegration());//

总价格、应付价格自动封装...//

orderConfirmVo;}MemberFeignService

public

*/GetMapping(member/memberreceiveaddress/{memberId}/getMemberAddresses)public

ListMemberAddressVo

getMemberAddresses(PathVariable(memberId)

Long

*/GetMapping(/getUserCartItems)public

ListOrderItemVo

*/GetMapping(/getUserCartItems)ResponseBodypublic

ListCartItem

cartService.getUserCartItems();}CartServiceImpl

Overridepublic

CartInterceptor.threadLocal.get();if

null)

过滤掉未勾选的商品。

并且商品的价格应该从数据库中查询assert

cartItems

cartItems.stream().filter(CartItem::isCheck).map(cartItem

{//

productFeignService.getSkuInfo(cartItem.getSkuId());SkuInfoVo

data

{});cartItem.setPrice(data.getPrice());return

cartItem;}).collect(Collectors.toList());return

collect;}else

null;}}MemberReceiveAddressController

GetMapping(/{memberId}/getMemberAddresses)

public

getMemberAddresses(PathVariable(memberId)

Long

memberReceiveAddressService.getMemberAddresses(memberId);

}MemberReceiveAddressServiceImpl

Overridepublic

QueryWrapperMemberReceiveAddressEntity().eq(member_id,memberId));}8、Feign

远程调用请求头丢失问题

{Bean(requestInterceptor)public

RequestInterceptor

RequestInterceptor(){Overridepublic

void

RequestContextHolder.getRequestAttributes();HttpServletRequest

request

attributes.getRequest();requestTemplate.header(Cookie,request.getHeader(Cookie));}};}

ThreadLocal

org.thymeleaf.exceptions.TemplateInputException:

Error

OrderInterceptor.threadLocal.get();CompletableFutureVoid

addressFuture

memberFeignService.getMemberAddresses(memberRespVo.getId());orderConfirmVo.setAddress(address);},

threadPoolExecutor);CompletableFutureVoid

orderItemsFuture

Feign在远程调用之前会构造请求。

ListOrderItemVo

items

cartFeignService.currentUserCartItems();orderConfirmVo.setItems(items);},

3、积分信息orderConfirmVo.setIntegration(memberRespVo.getIntegration());CompletableFuture.allOf(addressFuture,orderItemsFuture).get();//

总价格、应付价格自动封装...//

原因就是当没有使用异步任务的时候无论是Controller、Service、Dao、拦截器都由一个线程执行。

因此可以共享

ThreadLocal

*/PostMapping(ware/waresku/hasStock)public

HashMapLong,

wareInfoService.getFare(addrId);return

R.ok().setData(fare);}3、计算运费的方式手机号的最后一位如有需要可对接顺丰、圆通…等各开放平台。

Overridepublic

memberFeignService.info(addrId);MemberAddressVo

data

r.getData(memberReceiveAddress,

new

BigDecimal(fareString);fareVo.setFare(fare);fareVo.setAddress(data);return

fareVo;}return

{RequestMapping(member/memberreceiveaddress/info/{id})public

Long

接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的不会因

为多次点击而产生了副作用比如说支付场景用户购买了商品支付扣款成功但是返回结

果的时候网络异常此时钱已经扣了用户再次点击按钮此时会进行第二次扣款返回结

果成功用户查询余额返发现多扣钱了流水记录也变成了两条,这就没有保证接口

2哪些情况要防止接口幂等性

、token.equals、redis.del(token)如果这两个操作不是原子可能导

致高并发下都

悲观锁使用时一般伴随事务一起使用数据锁定时间可能会很长需要根据实际情况选用。

数据库乐观锁

2但返回给订单服务出现了问题订单服务又一次发起调用库存服务当订

version

如果多个机器可能在同一时间同时处理相同的数据比如多台机器定时任务都拿到了相同数

据处理我们就可以加分布式锁锁定此数据处理完成后释放锁。

获取到锁的必须先判断

各种唯一约束

插入数据应该按照唯一索引进行插入比如订单号相同的订单就不可能有两条记录插入。

insert

如果是分库分表场景下路由规则要保证相同请求下落地在同一个数据库和同一表中要

不然数据库主键约束就不起效果了因为是不同的数据库和表主键不相关。

redis防重

他们在同一个事务中。

这个保证了重复请求时因为去重表有唯一约束导致请求失败避

免了幂等问题。

这里要注意的是去重表和业务表应该在同一库中这样就保证了在同一个

事务即使业务操作失败了也会把去重表的数据回滚。

这个很好的保证了数据一致性。

redis

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mU4EZDFU-1675935821387)(https://images-1313160403.cos.ap-beijing.myqcloud.com/MarkDown/%E8%AE%A2%E5%8D%95%E7%A1%AE%E8%AE%A4%E9%A1%B5%E6%B5%81%E7%A8%8B.png)]

TODO

UUID.randomUUID().toString().replace(-,);orderConfirmVo.setOrderToken(token);redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIXmemberRespVo.getId(),token);14、提交订单

收集订单确认面的数据为创建订单为准备验证令牌是否是重复的请求。

【获取令牌、验证令牌必须保证原子性操作。

可使用LUA脚本或者Redisson】、验证令牌成功就是

创建订单的数据。

包括订单信息【用户信息、收货人信息、积分信息、优惠信息】订单中每个订单项信息【订单信息、spu信息、sku信息、优惠信息、积分信息、价格信息】运费、订单总金额订单创建成功进行验价比较订单支付的金额与提交订单的金额是否一致。

如果不一致说明价格发生变化提醒用户确认订单、验价完成就是锁定库存判断仓库中是否有足够的库存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t6LYPAfq-1675935821387)(https://images-1313160403.cos.ap-beijing.myqcloud.com/MarkDown/%E6%9C%AA%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6(41)].png)

锁定库存流程

NO_STOCK_EXCEPTION(21000,商品库存不足),6、

controller

*/PostMapping(/submitOrder)public

String

orderService.submitOrder(vo);if

{//

创建订单成功跳转到支付页面model.addAttribute(submitOrderResp,responseVo);return

pay;}else

商品库存不足;break;}attributes.addFlashAttribute(msg,msg);return

redirect:http://order.gulimall.com/toTrade;}}

catch

((NoStockException)e).getMessage();attributes.addFlashAttribute(msg,message);}return

redirect:http://order.gulimall.com/toTrade;}}7、验证令牌、创建订单、验证价格、锁定库存

OverrideTransactional(rollbackFor

保证事务回滚public

NoStockException{OrderSubmitResponseVo

responseVo

OrderSubmitResponseVo();MemberRespVo

memberRespVo

OrderInterceptor.threadLocal.get();//

1、验证令牌【必须保证获取令牌、删除令牌的原子性】String

orderToken

Long.class),Collections.singletonList(OrderConstant.USER_ORDER_TOKEN_PREFIX

memberRespVo.getId()),orderToken);if

(execute

令牌验证失败responseVo.setCode(1);return

responseVo;}else{//

orderTo.getOrder().getPayAmount();if

(Math.abs(payAmount.subtract(payPrice).doubleValue())

0.01)

3、验价成功保存订单saveOrder(orderTo);//

4、锁定库存库存不足抛出异常并回滚事务WareSkuLockVo

wareSkuLockVo

WareSkuLockVo();ListOrderItemVo

locks

createOrderItems(orderTo.getOrder().getOrderSn()).stream().map(item

{OrderItemVo

OrderItemVo();orderItemVo.setCount(item.getSkuQuantity());orderItemVo.setSkuId(item.getSkuId());orderItemVo.setTitle(item.getSkuName());return

orderItemVo;}).collect(Collectors.toList());wareSkuLockVo.setOrderSn(orderTo.getOrder().getOrderSn());wareSkuLockVo.setLocks(locks);R

wareFeignService.orderLockStock(wareSkuLockVo);if

(r.getCode()

锁定成功responseVo.setOrder(orderTo.getOrder());return

responseVo;}else

锁定失败.抛出异常responseVo.setCode(3);throw

new

验价失败responseVo.setCode(2);return

responseVo;}}//

redisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX

(orderToken

orderTo.getOrder();order.setCreateTime(new

Date());//

保存订单项orderItemService.saveBatch(orderTo.getItems());}/**

CreatedOrderTo*

IdWorker.getTimeId();orderEntity.setOrderSn(orderSn);//

items

null;computePrice(items,orderEntity);orderTo.setOrder(orderEntity);orderTo.setItems(items);return

orderTo;}/**

computePrice(ListOrderItemEntity

items,

循环叠加每一个订单项的优惠价格、订单总价格、积分、成长信息for

(OrderItemEntity

coupon.add(orderItem.getCouponAmount());promotion

promotion.add(orderItem.getPromotionAmount());integration

integration.add(orderItem.getIntegrationAmount());//

订单总价格total

total.add(orderItem.getRealAmount());//积分信息和成长值信息integrationTotal

orderItem.getGiftIntegration();growthTotal

设置订单的总价格、优惠价格、积分信息orderEntity.setTotalAmount(total);orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));orderEntity.setCouponAmount(coupon);orderEntity.setPromotionAmount(promotion);orderEntity.setIntegrationAmount(integration);orderEntity.setIntegration(integrationTotal);orderEntity.setGrowth(growthTotal);//设置删除状态(0-未删除1-已删除)orderEntity.setDeleteStatus(0);}/**

OrderEntity*

createOrderEntity(OrderSubmitVo

vo)

wareFeignService.fare(vo.getAddrId());if

(r.getCode()

{});orderEntity.setFreightAmount(fareVo.getFare());orderEntity.setMemberId(fareVo.getAddress().getMemberId());orderEntity.setReceiverPhone(fareVo.getAddress().getPhone());orderEntity.setReceiverCity(fareVo.getAddress().getCity());orderEntity.setReceiverName(fareVo.getAddress().getName());orderEntity.setReceiverDetailAddress(fareVo.getAddress().getDetailAddress());orderEntity.setReceiverPostCode(fareVo.getAddress().getPostCode());orderEntity.setReceiverProvince(fareVo.getAddress().getProvince());orderEntity.setReceiverRegion(fareVo.getAddress().getRegion());}//

2、设置订单相关的状态信息orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());orderEntity.setAutoConfirmDay(7);orderEntity.setConfirmStatus(0);return

orderEntity;}/**

cartFeignService.currentUserCartItems();if

orderItemVos

orderItemEntityListOrderItemEntity

items

orderItemVos.stream().map(orderItemVo

{//

createOrderItem(orderItemVo);orderItemEntity.setOrderSn(orderSn);return

orderItemEntity;}).collect(Collectors.toList());return

items;}return

productFeignService.getSpuInfoBySkuId(orderItemVo.getSkuId());if

(r.getCode()

TypeReferenceSpuInfoVo(){});orderItemEntity.setSpuId(spuInfoVo.getId());orderItemEntity.setSpuName(spuInfoVo.getSpuName());orderItemEntity.setCategoryId(spuInfoVo.getCatelogId());orderItemEntity.setSpuBrand(spuInfoVo.getBrandId().toString());}//

3、sku信息orderItemEntity.setSkuId(orderItemVo.getSkuId());orderItemEntity.setSkuName(orderItemVo.getTitle());orderItemEntity.setSkuPic(orderItemVo.getDefaultImage());//

spring提供的可以将list集合按照指定字符拼接成string字符串orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(orderItemVo.getSkuAttr(),;));orderItemEntity.setSkuQuantity(orderItemVo.getCount());orderItemEntity.setSkuPrice(orderItemVo.getPrice());//

4、优惠信息

5、积分信息orderItemEntity.setGiftIntegration(orderItemVo.getPrice().intValue());orderItemEntity.setGiftGrowth(orderItemVo.getPrice().intValue());//

sku价格

商品促销分解金额orderItemEntity.setPromotionAmount(BigDecimal.ZERO);//

优惠券优惠分解金额orderItemEntity.setCouponAmount(new

BigDecimal(0.0));//

积分优惠分解金额orderItemEntity.setIntegrationAmount(new

BigDecimal(0.0));//

orderItemEntity.getSkuPrice().multiply(new

BigDecimal(orderItemEntity.getSkuQuantity()));BigDecimal

finalPrice

initPrice.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getIntegrationAmount());orderItemEntity.setRealAmount(finalPrice);return

orderItemEntity;}

*/GetMapping(product/spuinfo/{skuId})public

getSpuInfoBySkuId(PathVariable(skuId)Long

skuId);

*/PostMapping(ware/waresku/hasStock)public

HashMapLong,

*/GetMapping(ware/wareinfo/fare)public

addrId);/**

*/PostMapping(ware/wareinfo/lock/order)public

WareSkuLockVo

getSpuInfoBySkuId(PathVariable(skuId)Long

skuId)

spuInfoService.getSpuInfoBySkuId(skuId);return

R.ok().setData(spuInfo);}Overridepublic

SpuInfoEntity

skuInfoService.getById(skuId);SpuInfoEntity

spuInfo

this.getById(skuInfo.getSpuId());return

spuInfo;}10、ware

wareSkuService.getSkusHasStock(skuIds);return

map;}/**

baseMapper.getSkusHasStock(skuId);map.put(skuId,count

null

resultTypejava.lang.IntegerSELECT

SUM(stock

*/PostMapping(/lock/order)public

WareSkuLockVo

wareSkuService.orderLockStock(vo);return

R.ok();}

R.error(BizCodeEnum.NO_STOCK_EXCEPTION.getCode(),BizCodeEnum.NO_STOCK_EXCEPTION.getMessage());}}}/**

计算运费*

wareInfoService.getFare(addrId);return

R.ok().setData(fare);}Overridepublic

FareVo

memberFeignService.info(addrId);MemberAddressVo

data

r.getData(memberReceiveAddress,

new

BigDecimal(fareString);fareVo.setFare(fare);fareVo.setAddress(data);return

fareVo;}return

*/OverrideTransactional(rollbackFor

)public

vo.getLocks();ListSkuWareHasStock

hasStocks

SkuWareHasStock();stock.setSkuId(item.getSkuId());//

wareIds

baseMapper.listWareIdHasStock(item.getSkuId());stock.setWareId(wareIds);stock.setNum(item.getCount());return

stock;}).collect(Collectors.toList());//

2、锁定库存for

baseMapper.lockSkuStock(skuId,wareId,hasStock.getNum());if

(count

NoStockException(skuId);}}return

true;}}/*

#{num}/update!--查询哪些仓库有对应商品的库存--select

idlistWareIdHasStock

在提交订单中使用本地事务进行事务回滚但是在分布式下可能会出现一些问题

1、远程服务假失败

本地事务只能保证在同一个服务同一个数据库中的回滚事务无法感知其他服务中出现的异常。

1、本地事务

原子性一系列的操作整体不可拆分要么同时成功要么同时失败一致性数据在事务的前后业务整体一致。

200

隔离性事务之间互相隔离。

持久性一旦事务成功数据一定会落盘在数据库。

在以往的单体应用中我们多个业务操作使用同一条连接操作不同的数据表一旦有异常

Storage库存业务代码扣库存

比如买东西业务扣库存下订单账户扣款是一个整体必须同时成功或者失败

READ

该隔离级别的事务会读到其它未提交事务的数据此现象也称之为脏读。

READ

一个事务可以读取另一个已提交的事务多次读取会造成不一样的结果此现象称为不可重

复读问题Oracle

1、PROPAGATION_REQUIRED如果当前没有事务就创建一个新事务如果当前存在事务

2、PROPAGATION_SUPPORTS支持当前事务如果当前存在事务就加入该事务如果当

3、PROPAGATION_MANDATORY支持当前事务如果当前存在事务就加入该事务如果

4、PROPAGATION_REQUIRES_NEW创建新事务无论当前存不存在事务都创建新事务。

5、PROPAGATION_NOT_SUPPORTED以非事务方式执行操作如果当前存在事务就把当

6、PROPAGATION_NEVER以非事务方式执行如果当前存在事务则抛出异常。

7、PROPAGATION_NESTED如果当前存在事务则在嵌套事务内执行。

如果当前没有事务

则执行与

共享一个事务。

c是一个新事物Transactional(timeout

20)public

事务中的所有配置都没有用。

Transactional(propagation

2)public

Propagation.REQUIRES_NEW)public

void

在同一个类里面编写两个方法内部调用的时候会导致事务设置失效。

原因是没有用到

b、c

{b();c();}Transactional(propagation

2)public

{System.out.println(b);}Transactional(propagation

Propagation.REQUIRES_NEW)public

void

{System.out.println(b);System.out.println(c);}解决使用动态代理调用方法

0、导入

1、EnableTransactionManagement(proxyTargetClass

true)

2、EnableAspectJAutoProxy(exposeProxytrue)

20)public

AopContext.currentProxy();orderService.b();orderService.c();}Transactional(propagation

2)public

{System.out.println(b);}Transactional(propagation

Propagation.REQUIRES_NEW)public

void

{System.out.println(c);}2、分布式事务

1为什么出现分布式事务

分布式事务是企业集成中的一个技术难点也是每一个分布式系统架构中都会涉及到的一个

2CAP

在分布式系统中的所有数据备份在同一时刻是否同样的值。

等同于所有节点访

在集群中一部分节点故障后集群整体是否还能响应客户端的读写请求。

对数据

分区容错性Partition

大多数分布式系统都分布在多个子网络。

每个子网络就叫做一个区partition。

分区容错的意思是区间通信可能失败。

比如一台服务器放在中国另一台服务

CAP

领导者。

所有系统中的请求都是通过领导者然后领导者发送给其他随从者。

2、raft

动画演示http://***secretlivesofdata.com/raft/

对于多数大型互联网应用的场景主机众多、部署分散而且现在的集群规模越来越大所

99.99999%N

基本可用是指分布式系统在出现故障的时候允许损失部分可用性例如响应时间、

功能上的可用性允许损失部分可用性。

需要注意的是基本可用绝不等价于系

0.5

软状态是指允许系统存在中间状态而该中间状态不会影响系统整体可用性。

分布

式存储中一般一份数据会有多个副本允许不同副本同步的延时就是软状态的体

replication

最终一致性是指系统中的所有数据副本经过一定时间后最终能够达到一致的状

态。

弱一致性和强一致性相反最终一致性是弱一致性的一种特殊情况。

2PC模式

第一阶段事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作并反映是

其中如果有任何一个数据库否决此次提交那么所有数据库都会被要求回滚它们在此事务

无法满足高并发场景XA

3PC引入了超时机制无论协调者还是参与者在向对方发送请求后若长时间

未收到回应则做出相应处理

与刚性事务不同柔性事务允许一定时间内不同节点的数据不一致但要求最终一致。

一阶段

按规律进行通知不保证数据一定能通知成功但会提供可查询操作接口进行核对。

这种

方案主要用在与第三方系统通讯时比如调用微信或支付宝支付后的支付结果通知。

这种

http

案例银行通知、商户通知等各大交易业务平台间的商户通知多次通知、查询校对、对

柔性事务-

实现业务处理服务在业务事务提交之前向实时消息服务请求发送消息实时消息服务只

记录消息数据而不是真正的发送。

业务处理服务在业务事务提交之后向实时消息服务确

3、Seata

是一款开源的分布式事务解决方案致力于提供高性能和简单易用的分布式事务服务。

Seata

AT、TCC、SAGA

(TC事务协调器维护全局事务的运行状态负责协调并驱动全局事务的提交或回滚

Transaction

™控制全局事务的边界负责开启一个全局事务并最终发起全局提交或全局回滚的决议

Resource

(RM控制分支事务负责分支注册、状态汇报并接收事务协调器的指令驱动分支本地事务的提交和回滚

Business

SpringCloudAlibaba、SpringBoot、SpringCloud

版本对照说明

、eureka、redis、zk、consul、etcd3、sofa

seata

、eureka、redis、zk、consul、etcd3、sofatype

nacosnacos

seataServer.properties}file.conf

seata

--------------------------------

The

--------------------------------

***

VARCHAR(32),transaction_service_group

VARCHAR(128),timeout

中创建seataServer.properties配置文件分组名、dataId、命名空间

要和上面在

id文件类型选择properties。

若是使用seata1.4.2之前的版本以下的每个配置项在nacos中就是一个条目需要使用script/config-center/nacos/下的nacos-config.shlinux或者windows下装git或者nacos-config.pypython脚本执行上传注册

develop

https://seata.io/zh-cn/docs/user/configurations.html

#Transport

transport.enableTmClientBatchSendRequestfalse

transport.enableRmClientBatchSendRequesttrue

transport.enableTcServerBatchSendResponsefalse

transport.rpcRmRequestTimeout30000

transport.rpcTmRequestTimeout30000

transport.rpcTcRequestTimeout30000

transport.threadFactory.bossThreadPrefixNettyBoss

transport.threadFactory.workerThreadPrefixNettyServerNIOWorker

transport.threadFactory.serverExecutorThreadPrefixNettyServerBizHandler

transport.threadFactory.shareBossWorkerfalse

transport.threadFactory.clientSelectorThreadPrefixNettyClientSelector

transport.threadFactory.clientSelectorThreadSize1

transport.threadFactory.clientWorkerThreadPrefixNettyClientWorkerThread

transport.threadFactory.bossThreadSize1

transport.threadFactory.workerThreadSizedefault

service

service.vgroupMapping.default_tx_groupdefault

#If

service.default.grouplist127.0.0.1:8091

service.disableGlobalTransactionfalse#Transaction

rule

client.rm.asyncCommitBufferLimit10000

client.rm.lock.retryPolicyBranchRollbackOnConflicttrue

client.rm.tableMetaCheckEnabletrue

client.rm.tableMetaCheckerInterval60000

client.rm.reportSuccessEnablefalse

client.rm.sagaBranchRegisterEnablefalse

client.rm.sagaJsonParserfastjson

client.rm.tccActionInterceptorOrder-2147482648

client.tm.defaultGlobalTransactionTimeout60000

client.tm.degradeCheckAllowTimes10

client.tm.degradeCheckPeriod2000

client.tm.interceptorOrder-2147482648

client.undo.logSerializationjackson

client.undo.onlyCareUpdateColumnstrue

server.undo.logDeletePeriod86400000

client.undo.compress.enabletrue

client.undo.compress.threshold64k

#For

tcc.fence.logTableNametcc_fence_log

rule

store.file.maxBranchSessionSize16384

store.file.maxGlobalSessionSize512

store.file.fileWriteBufferCacheSize16384

store.file.sessionReloadReadSize100#

store.db.dbTypemysql

store.db.driverClassNamecom.mysql.cj.jdbc.Driver

ip:3306/seata?useUnicodetruerewriteBatchedStatementstrue

store.db.useryour

store.db.globalTableglobal_table

store.db.branchTablebranch_table

store.db.distributedLockTabledistributed_lock

store.redis.single.host127.0.0.1

store.redis.sentinel.masterName

store.redis.sentinel.sentinelHosts

store.redis.minConn1

server.recovery.committingRetryPeriod1000

server.recovery.asynCommittingRetryPeriod1000

server.recovery.rollbackingRetryPeriod1000

server.recovery.timeoutRetryPeriod1000

server.maxRollbackRetryTimeout-1

server.rollbackRetryTimeoutUnlockEnablefalse

server.distributedLockExpireTime10000

server.xaerNotaRetryTimeout60000

server.session.branchAsyncQueueSize5000

server.session.enableBranchAsyncRemovefalse

server.enableParallelRequestHandlefalse#

Metrics

metrics.exporterProme***usPort98983双击

启动启动之前确保

!--seata--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusions!--

--exclusiongroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.4.2/version/dependency3、yaml

中配置

trueenable-auto-data-source-proxy:

true

#是否开启数据源自动代理,默认为truetx-service-group:

default_tx_group

#registry根据seata服务端的registry配置type:

nacos

seata.servicenacos:server-addr:

localhost:8848

seataServer.properties##配置自己的dataId,由于搭建服务端时把客户端的配置也写在了seataServer.properties,所以这里用了和服务端一样的配置文件,实际客户端和服务端的配置文件分离出来更好

4、在启动类上使用自动代理

name给定全局事务实例的名称随便取唯一即可rollbackFor当发生什么样的异常时进行回滚noRollbackFor发生什么样的异常不进行回滚。

Transactional

!--seata分布式事务--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-all/artifactIdversion0.9.0/version/dependency3、使用分布式事务的服务拷贝

conf

spring.cloud.alibaba.seata.tx-service-groupmy_test_tx_group5、手动配置数据源seata1.4.2直接使用注解即可低版本需要手动注入

Configuration

dataSource(DataSourceProperties

properties)

properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();if

(StringUtils.hasText(properties.getName()))

{dataSource.setPoolName(properties.getName());}return

new

}8、在业务的方法入口使用全局事务注解GlobalTransactiona

TC、TM、RM

哪个微服务使用了**GlobalTransactional**哪个就是TMRM事务的参与者一个数据库就是一个RM。

三、使用延迟队列自动解锁库存

使用加锁方式使业务变成串行化比较适用于后台管理系统这种并发性不高的分布式事务问题。

当库存锁定成功时向交换机发送一条消息【订单号、锁定库存状态、锁了几个库存…】根据

路由键交换机将订单消息转发到

之后会判断订单状态是否需要解锁库存。

需要解锁库存将消息转发到解锁库存的服务即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pyQ51jiU-1675935821391)(https://images-1313160403.cos.ap-beijing.myqcloud.com/MarkDown/%E8%AE%A2%E5%8D%95%E6%B5%81%E7%A8%8B%E5%9B%BE.png)]

1、在

Jackson2JsonMessageConverter();}RabbitListener(queues

stock.release.stock.queue)public

void

TopicExchange(stock-event-exchange,true,false,null);}Beanpublic

Queue

Queue(stock.release.stock.queue,true,false,false,null);}Beanpublic

Queue

设置与队列相连的死信交换机arguments.put(x-dead-letter-exchange,stock-event-exchange);//

路由键arguments.put(x-dead-letter-routing-key,stock.release);//

TTL。

超过30s就表示未支付订单准备关闭arguments.put(x-message-ttl,12000);return

new

Queue(stock.delay.queue,true,false,false,arguments);}Beanpublic

Binding

Binding(stock.delay.queue,Binding.DestinationType.QUEUE,stock-event-exchange,stock.locked,null);}Beanpublic

Binding

Binding(stock.release.stock.queue,Binding.DestinationType.QUEUE,stock-event-exchange,stock.release.#,null);}

}3、在锁定库存时需要保存库存的工作单表示该库存操作的状态是否成功是否需要回滚。

锁定库存成功后向

2、成功创建订单锁定库存但是由于接下来的业务出现异常需要自动解锁库存。

*

*/OverrideTransactional(rollbackFor

Boolean

WareOrderTaskEntity();taskEntity.setOrderSn(vo.getOrderSn());wareOrderTaskService.save(taskEntity);//

locks

vo.getLocks();ListSkuWareHasStock

hasStocks

SkuWareHasStock();stock.setSkuId(item.getSkuId());//

wareIds

baseMapper.listWareIdHasStock(item.getSkuId());stock.setWareId(wareIds);stock.setNum(item.getCount());return

stock;}).collect(Collectors.toList());//

2、锁定库存for

保存工作单详情信息WareOrderTaskDetailEntity

taskDetailEntity

WareOrderTaskDetailEntity();taskDetailEntity.setWareId(wareId);taskDetailEntity.setSkuId(skuId);taskDetailEntity.setSkuNum(hasStock.getNum());taskDetailEntity.setTaskId(taskEntity.getId());taskDetailEntity.setLockStatus(1);wareOrderTaskDetailService.save(taskDetailEntity);//

stockLockedTo

StockLockedTo();stockLockedTo.setTaskId(taskEntity.getId());//

只封装

如果一共有三件商品前俩件锁定成功第三件锁定失败。

那么本地事务是会将这三件库存都会回滚。

因此如果只保存id查不到任何信息。

//

stockLockedTo.setTaskDetailId(detailEntity.getId());StockDetailLockedTo

stockDetailLockedTo

StockDetailLockedTo();BeanUtils.copyProperties(taskDetailEntity,

stockDetailLockedTo);stockLockedTo.setTaskDetail(stockDetailLockedTo);rabbitTemplate.convertAndSend(stock-event-exchange,

stock.locked,

NoStockException(skuId);}}return

true;}4、MQ

需要有消费者接收并进行自动解锁。

并且使用手动确认机制解锁库存失败重新将消息放回队列。

等待

Service

wareSkuService;RabbitHandlerpublic

void

handleStockedRelease(StockLockedTo

to,

{System.out.println(收到库存解锁通知...);wareSkuService.unLock(to);//

手动确认channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}

catch

有任何异常都是解锁失败channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);e.printStackTrace();}}

}/***

如果没有这个订单需要解锁有可能订单创建成功后库存锁定成功接着创建订单又调用其他方法把自己搞回滚了。

*

如果有订单还需要判断订单的支付状态。

如果支付成功也无需解锁。

支付失败或者取消支付进行解锁。

*

应该使用手动确认机制解锁失败重新将信息放回队列。

*/Overridepublic

void

查询是否有工作单详情信息WareOrderTaskDetailEntity

wareOrderTaskDetailService.getById(taskDetail.getId());if

null)

{//查出wms_ware_order_task工作单的信息WareOrderTaskEntity

orderTaskInfo

wareOrderTaskService.getById(to.getTaskId());//

获取订单号查询订单状态R

orderFeignService.getOrderByOrderSn(orderTaskInfo.getOrderSn());if

(r.getCode()

OrderStatusEnum.CANCLED.getCode())

{//

(orderTaskDetailEntity.getLockStatus()

{//

没有订单或者取消订单需要自动解锁unLockStock(taskDetail.getSkuId(),

taskDetail.getWareId(),taskDetail.getSkuNum(),taskDetail.getId());}}}else

{//

RuntimeException(解锁失败...重新入队);}}//

如果没有这个工作单无需自动解锁}/***

库存解锁后更新库存工作单状态WareOrderTaskDetailEntity

new

WareOrderTaskDetailEntity();orderTaskDetailEntity.setId(detailId);//

变为已解锁orderTaskDetailEntity.setLockStatus(2);wareOrderTaskDetailService.updateById(orderTaskDetailEntity);}mapper

映射文件

idunLockStockUPDATEwms_ware_sku

SET

spring.rabbitmq.listener.simple.acknowledge-modemanual解决

远程调用问题

orderFeignService.getOrderByOrderSn(orderTaskInfo.getOrderSn());Could

not

中设置了一个登录拦截器想要访问订单都必须登录才可以而我们远程调用无需登录。

Order服务中的登录拦截器中做一个匹配映射如果是远程调用的请求直接放行。

解决远程调用

AntPathMatcher().match(order/order/status/**,

requestURI);if

true;}我的解决方案不知道为什么我按照老师的解决方案没有管用依然报这个错误配置AntPathMatcher没有起到作用因此我直接在注册拦截器时将这个路径排除了。

四、定时关闭订单

Exchange、Queue、Binding...服务启动会自动向RabbitMQ创建。

*

*///

设置与队列相连的死信交换机arguments.put(x-dead-letter-exchange,order-event-exchange);//

路由键arguments.put(x-dead-letter-routing-key,order.release.order);//

TTL。

超过1min就表示未支付订单准备关闭arguments.put(x-message-ttl,60000);return

new

Queue(order.delay.queue,true,false,false,arguments);}//

Queue

Queue(order.release.order.queue,true,false,false,null);}//

交换机Beanpublic

TopicExchange(order-event-exchange,true,false);}//

设置绑定关系:

Binding(order.delay.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.create.order,null);}//

设置绑定关系:

——》order.release.order.queueBeanpublic

Binding

Binding(order.release.order.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.release.order,null);}

2、创建订单成功向MQ发送消息

创建订单成功向RabbitMQ发送消息rabbitTemplate.convertAndSend(order-event-exchange,order.create.order,orderTo.getOrder());3、监听器接受消息

Component

orderService;RabbitHandlerpublic

void

{System.out.println(订单超时未支付即将关闭订单:

{orderService.closeOrder(order);//

手动确认channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}

catch

{channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);e.printStackTrace();}}

}4、

OrderStatusEnum.CREATE_NEW.getCode())

{//

OrderEntity();entity.setId(orderEntity.getId());//

设置订单状态为取消entity.setStatus(OrderStatusEnum.CANCLED.getCode());this.updateById(entity);}}当超过指定时间后修改订单的状态由于

关闭订单与

存在一定的时间差正常情况下在订单创建成功后会隔一段时间去检查订单状态解锁库存

但是这种方式还有可能会出现问题假如订单解锁有延迟解锁库存先执行完订单就不会被释放了。

设置绑定关系:

——》stock.release.stock.queueBeanpublic

Binding

Binding(stock.release.stock.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.release.o***r.#,null);}6、关闭订单后向MQ在发送一次消息

/**

OrderStatusEnum.CREATE_NEW.getCode())

{//

OrderEntity();entity.setId(orderEntity.getId());//

设置订单状态为取消entity.setStatus(OrderStatusEnum.CANCLED.getCode());this.updateById(entity);//

TODO

OrderTo();BeanUtils.copyProperties(orderEntity,orderTo);rabbitTemplate.convertAndSend(order-event-exchange,order.release.o***r,orderTo);}}7、增加监听器接收消息

/**

handleOrderCloseRelease(OrderTo

order,

{System.out.println(订单关闭即将解锁库存....);wareSkuService.unLock(order);//

手动确认channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}

catch

有任何异常都是解锁失败channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);e.printStackTrace();}}8、库存解锁解锁时需要判断工作单的锁定状态防止重复解锁

/***

wareOrderTaskService.getOrderTaskByOrderSn(orderEntity.getOrderSn());//

只查询锁定的工作单详情防止重复解锁。

ListWareOrderTaskDetailEntity

taskDetailEntityList

wareOrderTaskDetailService.list(new

QueryWrapperWareOrderTaskDetailEntity().eq(task_id,

taskEntity.getId()).eq(lock_status,

1));for

解锁库存unLockStock(orderTaskDetailEntity.getSkuId(),

orderTaskDetailEntity.getWareId(),orderTaskDetailEntity.getSkuNum(),orderTaskDetailEntity.getId());}}五、支付服务

1、一些概念说明

我们使用一对公私钥中的一个密钥来对数据进行加密而使用另一个密钥来进行解

给我们将要发送的数据做上一个唯一签名类似于指纹用来互相验证接收方和发送方的身份

一个数据对应一个唯一的签名如果数据被修改签名也会更改验签就是验证原数据是否被修改过。

2、沙箱环境测试

Demo测试下载https://docs.open.alipay.com/270/106291/

3、内网穿透

使用内网穿透只需要安装内网穿透服务商的软件别人即可访问我们的电脑网站

1、下载

https://dl-cdn.oray.com/hsk/windows/HskDDNS_8.6.0.48614.exe

2、增加映射

dependencygroupIdcom.alipay.sdk/groupIdartifactIdalipay-sdk-java/artifactIdversion4.35.37.ALL/version

alipay)

MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCSgX/nTQ0lDS8ObaM5LGZ1hiz18GXnNpqPLhJCym4xOpn35FNPHrPkDGEoMKrZ5LJeA4cZulckD8AtpvBCpeyIkrj/i1WVmSg10hVX67MlVets4UecCHZv2hKAN0/iId76kozdqrd7Csp/YgXPquN9Np0NFotggTrmiBANkvcpTF9SCGrDq/isOoCvClfbvVJjApfLLOel3yECe5K/SZ8puiWILVm1NxEXAqJ8z0ipPZVGrXsT6Bo0pEyCPcEL0SqaC9WT0zdWQzdUknCzZV9W2wKjEXBJG9hqxay5kPaKm9leBatSkDAaDxH/N5g36HRfY7BmklwRZsp17lHinxAgMBAAECggEAfnnfck35WBKFc90a9D0FXlzrZGEV3uzKIIsb46UXFlrzC5HoVkvEWOCiJCjHiIpvbGr8xED43TZgk/IwLC/JxQLM0kVJGWo6fWoSVOIP2YSLNe620APBvaq3BdkFiMJfSYBBg2J7mkIR39SE8Nvu3j3QWmYzSNJbE2spINnwTzNBL1OPaB5h3hSjyI07KaUcOjhTBF0EZl83NlBDsxmQvy0NmuOIWAcIXXvGoIbwkA774J3LhwLVS4W2FpQj4FlxvDlPu24GeNWN7oO66T3Jp9bweO120ObhuKwZQosDGkJq0975zVSJX5QtUWHMM/QDPO8Pk24n2AoPcACQcQKBgQDS6kqDsK8dDBpkmxYopA1gJJATnur0RHFZJb5webOhnEZnePhB1hhhGvKFcrdY2hcYeQiUZkHMsnWItNUe9E9ccp4m6KKG0iV/BQda7zx1zMTTZUMvSbO282Q31YnQu7Yz6BSk4f/U5Qbu61AK53Tv1ejSAgQhXt1Pwq8KD7QKBgQCx0pkqW453tY2o4iPqFGjKYI2yk5bAH5etmOvW51OZ4Slsq/aUJKBVG6fOpRVKkiXulHhrp5csZH0/C7kaj4Hy7TjgUKSWvwlv7i7jgN0dq/bhVJz82yN9pENWvy5J0I8Kt67XH6JDEGWjlV58auifMRSx5mRJNn5pM6qrFlQKBgFyZWm/JV1fv1xVyoLjlXlTvBsbO7kMH/jpgqFwtAk1n/x3VEShJ1kayIbTOjotWSopMvCFJG9tqM0cyxWLatkELXWifAIsNpqRuYWah1FbZD2fukxLNtM0aYyCUUvZeg2cUnIOraWupxbp9e13eMpvdmWMiWXfhM18CRWEwdAoGAUwT0l076EhgUQJwm1JML0jY94eCfpmLbnNJgRe1qysEPrB1s2IslA7cOqC5we0kyRmmwsuoibQpZYwbRG7JmRAk2pZtgzDRSbpxv7a0rDoBLmbXMOU0Hraqw2Bf3v2SMc79/9FWnIvrC4EyBYZZPwGOpsNAZRSdEUQX9qrceUCgYB99OOtFFt1ixzyTCyUj3Fuiw7BsPhdI3nuMSoNTPIDNpzRBp/KFXyv/FNJ2CjTAsX3OR3D6KmEYihqUfrYeb0P5zoybcQLMxbXxKec6F2o6U2iqFIq0MKwHUqsb9X3pj4qE0ZHbFgRtIHnL2/QGV5PFJdmIZIBKZcvB8fW6ztDA;//

支付宝公钥,查看地址https://openhome.alipay.com/platform/keyManage.htm

String

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyQQceVUChTJGtF/a8SXufhSxDTKporieTq9NO7yDZSpDlAX1zVPT/nf0KWAlxq1TYappWMIYtyrOABhJyn6flNP6vuSBiM5lYsepHvYrtRHqlFiJruEkiaCgEZBKL5aCfBHYj0oqgQn9MpNV/PEH4cBYAVaiI4VX8CBUQfeEGjgN6OkpLULZ3X0JUkmSnVvCNJ1m3PD68IIlbOfEZXJUKCqmZhzprGR5VWswjxAg87cMwvijL4gdkSy/daG62Bz5vApcmmMkuX1k1fMWP4ajZCASVw8HDMSLRhd8We9F97gd8CW0TavzbdRmTS5H4yEgO8F9HRAsbkhV9yu0yQIDAQAB;//

需http://格式的完整路径不能加?id123这类自定义参数必须外网可以正常访问//

支付宝会悄悄的给我们发送一个请求告诉我们支付成功的信息private

String

需http://格式的完整路径不能加?id123这类自定义参数必须外网可以正常访问//同步通知支付成功一般跳转到成功页private

String

https://openapi.alipaydev.com/gateway.doprivate

String

https://openapi.alipaydev.com/gateway.do;public

String

DefaultAlipayClient(AlipayTemplate.gatewayUrl,

AlipayTemplate.merchant_private_key,

json,

AlipayTemplate.alipay_public_key,

AlipayTemplate.sign_type);//1、根据支付宝的配置生成一个支付客户端AlipayClient

alipayClient

DefaultAlipayClient(gatewayUrl,app_id,

json,charset,

//设置请求参数AlipayTradePagePayRequest

alipayRequest

AlipayTradePagePayRequest();alipayRequest.setReturnUrl(return_url);alipayRequest.setNotifyUrl(notify_url);//商户订单号商户网站订单系统中唯一订单号必填String

out_trade_no

vo.getOut_trade_no();//付款金额必填String

total_amount

vo.getTotal_amount();//订单名称必填String

subject

vo.getBody();alipayRequest.setBizContent({\out_trade_no\:\

out_trade_no

\product_code\:\FAST_INSTANT_TRADE_PAY\});String

result

alipayClient.pageExecute(alipayRequest).getBody();//会收到支付宝的响应响应的是一个页面只要浏览器显示这个页面就会自动来到支付宝的收银台页面System.out.println(支付宝的响应result);return

result;}

alipayTemplate;AutowiredOrderService

orderService;ResponseBodyGetMapping(/payOrder)public

String

payOrder(RequestParam(orderSn)String

orderSn){PayVo

orderService.getOrderPay(orderSn);try{//

catch

com.atguigu.gulimall.order.vo.PayVo

支付订单*/Overridepublic

this.getOrderByOrderSn(orderSn);PayVo

payVo

PayVo();payVo.setOut_trade_no(orderSn);//

orderItemEntities

QueryWrapperOrderItemEntity().eq(order_sn,

orderSn));payVo.setSubject(orderItemEntities.get(0).getSpuName());//

订单标题//

指定后面2位小数payVo.setTotal_amount(orderEntity.getPayAmount().setScale(2).toString());payVo.setBody(orderItemEntities.get(0).getSpuName());

备注return

actionhttps://openapi.alipaydev.com/gateway.do?charsetutf-8methodalipay.trade.page.paysignf0ALyx8f46iNFZjfTjpFgSQRrEwbXjMy5LDVKbBVylxvr%2F8V%2FhOyTisGAtu7%2Bpo6RjVOi3NzbX%2FfAHRmjegly52mninirGTBLN5FntlPn4PGXa7Isi0sWGgAvfnb%2BoQ3IiefoN6Pt3BY7QdXywoE2BHfoz8bXkV%2F%2BfjZFjhi2W5uZeDaoIlS%2BBogF5B%2FbwcCM0AUrtjIoHmrngvzoPFj0exFQ2PP2FE4xrqJyfZxoEh7tcaUzDa37u4KgG7%2BU4luio9CZryuS29jLU%2B4rKOWE7LKKw2L5v1GvIBC6sGlpyNs%2Bbxj9LCGMaa5363EjecgudDHOdm2cVirvxb2u4wUnw%3D%3Dreturn_urlhttp%3A%2F%2Fmember.gulimall.com%2FmemberOrder.htmlnotify_urlhttp%3A%2F%2F63i857228t.goho.co%2Fpayed%2Fnotifyversion1.0app_id2021000122615169sign_typeRSA2timestamp2023-02-0520%3A31%3A56alipay_sdkalipay-sdk-java-4.35.37.ALLformatjson

input

value{quot;out_trade_noquot;:quot;202302052031434361622211365567483906quot;,quot;total_amountquot;:quot;104484.00quot;,quot;subjectquot;:quot;华为Mate30

Proquot;,quot;bodyquot;:quot;华为Mate30

Proquot;,quot;product_codequot;:quot;FAST_INSTANT_TRADE_PAYquot;}

input

scriptdocument.forms[0].submit();/script修改

controller

alipayTemplate;AutowiredOrderService

orderService;//

MediaType.TEXT_HTML_VALUE)public

String

orderService.getOrderPay(orderSn);//

支付String

alipayTemplate.pay(payVo);return

pay;}}5、支付成功同步回调

{GetMapping(/memberOrder.html)public

String

需http://格式的完整路径不能加?id123这类自定义参数必须外网可以正常访问//

http://63i857228t.goho.co/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jspprivate

String

https://4287b772c5.imdo.co/payed/notify;2、修改

IP:80

NginxHOST请求头不匹配找不到对应的服务网因此手动精确匹配如果是

/payed/

http://gulimall;}3、通过外网访问订单服务POST

请求https://4287b772c5.imdo.co/payed/notify

/payed/notify

LoginUserInterceptor()).addPathPatterns(/**).excludePathPatterns(/order/order/status/**).excludePathPatterns(/payed/notify);

7、支付完成

alipayTemplate;PostMapping(/payed/notify)public

String

String(valueStr.getBytes(ISO-8859-1),

valueStr);}boolean

AlipaySignature.rsaCheckV1(params,

alipayTemplate.getAlipay_public_key(),alipayTemplate.getCharset(),

alipayTemplate.getSign_type());

//调用SDK验证签名if

{System.out.println(签名验证成功...);//去修改订单状态String

result

orderService.handlePayResult(vo);return

result;}

{System.out.println(签名验证失败...);return

error;}}

PaymentInfoEntity();paymentInfo.setOrderSn(vo.getOut_trade_no());paymentInfo.setAlipayTradeNo(vo.getTrade_no());paymentInfo.setTotalAmount(new

BigDecimal(vo.getBuyer_pay_amount()));paymentInfo.setSubject(vo.getBody());paymentInfo.setPaymentStatus(vo.getTrade_status());paymentInfo.setCreateTime(new

Date());paymentInfo.setCallbackTime(vo.getNotify_time());//

添加到数据库中this.paymentInfoService.save(paymentInfo);//

修改订单状态//

(tradeStatus.equals(TRADE_SUCCESS)

tradeStatus.equals(TRADE_FINISHED))

{//支付成功状态String

//获取订单号this.updateOrderStatus(orderSn,OrderStatusEnum.PAYED.getCode());}return

success;}/***

{this.baseMapper.updateOrderStatus(orderSn,status);}mapper

update

订单在支付页不支付一直刷新订单过期了才支付订单状态改为已支付了但是库

增加一个

http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html

cron表达式在线生成:

出现在日和周几的位置为了防止日和周冲突在周和日上如果要写通配符使

(cron“*

spring.task.scheduling.pool.size5

4使用异步任务注解*

{e.printStackTrace();}log.info(hello);}

}2、秒杀商品上架

上架最近三天的所有秒杀商品、上架流程1、查询最近三天的所有秒杀场次

GetMapping(/findLatest3DaysSessions)

public

seckillSessionService.findLatest3DaysSessions();return

}Overridepublic

QueryWrapperSeckillSessionEntity().between(start_time,startTime(),endTime()));}//

秒杀场次开始时间private

dateTime.format(DateTimeFormatter.ofPattern(yyyy-MM-dd

HH:mm:ss));}//

dateTime.format(DateTimeFormatter.ofPattern(yyyy-MM-dd

HH:mm:ss));}2、

couponFeignService;Autowiredprivate

StringRedisTemplate

productFeignService;Autowiredprivate

RedissonClient

couponFeignService.findLatest3DaysSessions();if

(r.getCode()

TypeReferenceListSeckillSessionsWithSkus()

{});if

中this.saveSessionsInfos(sessionsWithSkus);//

3、保存秒杀商品信息到

中this.saveSessionsSkusInfos(sessionsWithSkus);}else

{System.out.println(没有秒杀场次);}}}/**

保存秒杀场次信息到redis*

sessionsWithSkus){sessionsWithSkus.stream().forEach(item

-{//

item.getStartTime().getTime();long

end

item.getEndTime().getTime();String

key

item.getRelationSkus().stream().map(sku

sku.getSkuId().toString()).collect(Collectors.toList());redisTemplate.opsForList().leftPushAll(key,skuIds);});}/**

*/private

sessionsWithSkus){sessionsWithSkus.stream().forEach(item

-{//

准备hash操作BoundHashOperationsString,

Object,

redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);item.getRelationSkus().stream().forEach(relationSku

-{//

将商品信息保存到redis中SeckillSkuRedisTo

seckillSkuRedisTo

1、保存秒杀商品的秒杀信息BeanUtils.copyProperties(relationSku,seckillSkuRedisTo);//

productFeignService.info(relationSku.getSkuId());if

(r.getCode()

{});seckillSkuRedisTo.setSkuInfoVo(skuInfo);}//

3、保存秒杀的开始、结束时间seckillSkuRedisTo.setStartTime(item.getStartTime().getTime());seckillSkuRedisTo.setEndTime(item.getEndTime().getTime());//

token

UUID.randomUUID().toString().replace(-,

);seckillSkuRedisTo.setRandomCode(token);//

保存到

中ops.put(relationSku.getSkuId().toString(),

JSON.toJSONString(seckillSkuRedisTo));//

当大量请求秒杀时不可能实时去查询数据库这样会给数据库造成很大的压力//

通过将redis中设置信号量来限制秒杀商品的数量RSemaphore

semaphore

redissonClient.getSemaphore(SEMAPHORE_CACHE_PREFIX

token);//

将秒杀的数量作为信号量semaphore.trySetPermits(relationSku.getSeckillCount().intValue());});});}

}公共返回类

SeckillSessionsWithSkus保存秒杀的场次信息以及每场包含的skuId

Data

如果定时任务在分布式的情况下可能会造成商品重复上架等问题。

因此在上架商品时使用分布式锁解决保证同一时间只有一个服务执行定时任务

1、使用分布式锁

seckillService;Autowiredprivate

RedissonClient

redissonClient.getLock(UPLOAD_LOCK);lock.lock(10,

{log.info(准备上架商品...);seckillService.UploadSeckillSkuLatest3Days();log.info(上架成功...);}

finally

couponFeignService;Autowiredprivate

StringRedisTemplate

productFeignService;Autowiredprivate

RedissonClient

couponFeignService.findLatest3DaysSessions();if

(r.getCode()

TypeReferenceListSeckillSessionsWithSkus()

{});if

中this.saveSessionsInfos(sessionsWithSkus);//

3、保存秒杀商品信息到

中this.saveSessionsSkusInfos(sessionsWithSkus);}

else

{System.out.println(没有秒杀场次);}}}/**

保存秒杀场次信息到redis*

saveSessionsInfos(ListSeckillSessionsWithSkus

sessionsWithSkus)

{sessionsWithSkus.stream().forEach(item

{//

item.getStartTime().getTime();long

end

item.getEndTime().getTime();String

key

item.getRelationSkus().stream().map(sku

item.getId()

sku.getSkuId().toString()).collect(Collectors.toList());redisTemplate.opsForList().leftPushAll(key,

skuIds);}});}/**

saveSessionsSkusInfos(ListSeckillSessionsWithSkus

sessionsWithSkus)

{sessionsWithSkus.stream().forEach(item

{//

准备hash操作BoundHashOperationsString,

Object,

redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);item.getRelationSkus().stream().forEach(relationSku

{//

UUID.randomUUID().toString().replace(-,

);//

(!ops.hasKey(relationSku.getPromotionSessionId()_relationSku.getSkuId().toString())){//

将商品信息保存到redis中SeckillSkuRedisTo

seckillSkuRedisTo

1、保存秒杀商品的秒杀信息BeanUtils.copyProperties(relationSku,

productFeignService.info(relationSku.getSkuId());if

(r.getCode()

{});seckillSkuRedisTo.setSkuInfoVo(skuInfo);}//

3、保存秒杀的开始、结束时间seckillSkuRedisTo.setStartTime(item.getStartTime().getTime());seckillSkuRedisTo.setEndTime(item.getEndTime().getTime());//

4、保存秒杀商品的随机码seckillSkuRedisTo.setRandomCode(token);//

保存到

中。

keysessionId_skuIdops.put(relationSku.getPromotionSessionId()_relationSku.getSkuId().toString(),

JSON.toJSONString(seckillSkuRedisTo));//

当大量请求秒杀时不可能实时去查询数据库这样会给数据库造成很大的压力//

通过将redis中设置信号量来限制秒杀商品的数量RSemaphore

semaphore

redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE

token);//

将秒杀的数量作为信号量semaphore.trySetPermits(relationSku.getSeckillCount().intValue());}});});}

RestController

*/GetMapping(/getCurrentSeckillSkus)public

getCurrentSeckillSkus(){ListSeckillSkuRedisTo

list

seckillService.getCurrentSeckillSkus();return

}/**

redisTemplate.keys(SESSION_CACHE_PREFIX

*);for

seckill:sessions:1675844252000_1675958400000

需要分割String[]

key.replace(SESSION_CACHE_PREFIX,

).split(_);Long

redisTemplate.opsForList().range(key,

-100,

k1:Mapk2,k3BoundHashOperationsString,

String,

redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);//

由于场次信息的value与商品信息的key是一致的.可以通过场次信息的value作为商品信息的key取出valueListString

skus

seckillSkuRedisTo.setRandomCode();

seckillSkuRedisTo;}).collect(Collectors.toList());return

null;}时区BUG

从数据库封装到java中的实体类时时间可能会有差距数据库默认采用UTC建议改成东八区

SELECT

redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);//

keys

hashOps.get(key);SeckillSkuRedisTo

seckillSkuRedisTo

如果该商品正在秒杀就将随机码发送给前端页面。

如果没有就无需发送Long

start

seckillSkuRedisTo.getStartTime();Long

end

seckillSkuRedisTo.getEndTime();long

current

{seckillSkuRedisTo.setRandomCode();}return

null;}2、controller

*/GetMapping(/sku/seckill/{skuId})public

Long

seckillService.getSkuSeckillInfo(skuId);return

R.ok().setData(seckillSkuRedisTo);}

3、product

*/GetMapping(/sku/seckill/{skuId})public

Long

seckillFeignService.getSkuSeckillInfo(skuId);if

(r.getCode()

{});skuItemVo.setSeckillInfo(data);}},

登录拦截器

request.getSession().getAttribute(AuthServerConstant.LOGIN_USER);if

(memberRespVo

{request.getSession().setAttribute(msg,请先登录);//

没有进行登录跳转到登录界面登录response.sendRedirect(http://auth.gulimall.com/login.html);return

false;}else{threadLocal.set(memberRespVo);return

true;}}

addInterceptors(InterceptorRegistry

registry)

LoginUserInterceptor()).addPathPatterns(/kill);}

}7、秒杀流程

秒杀到创建订单没有操作数据库只是向MQ发送一条消息数据库的压力是非常轻松的。

controller

http://seckill.gulimall.com/kill?killId2-11keyf7bde6d931e34b7f8677b3395157d426num1*

String

seckillService.kill(killId,key,num);return

R.ok().setData(orderSn);}SeckillServiceImpl

/**

LoginUserInterceptor.threadLocal.get();BoundHashOperationsString,

String,

redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);String

json

skuRedisTo.getPromotionSessionId().toString()

skuRedisTo.getSkuId().toString();//

2、对随机码、对应关系进行校验if

skuRedisTo.getSkuId().toString();//

expir

redisTemplate.opsForValue().setIfAbsent(redisKey,

num.toString(),

redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE

key);try

{e.printStackTrace();}}}}}}return

null;}订单创建完毕向

Queue(order.seckill.order.queue,

true,

orderSecKillOrrderQueueBinding()

{//String

Binding(order.seckill.order.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.seckill.order,null);return

/**

LoginUserInterceptor.threadLocal.get();BoundHashOperationsString,

String,

redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);String

json

skuRedisTo.getPromotionSessionId().toString()

skuRedisTo.getSkuId().toString();//

2、对随机码、对应关系进行校验if

skuRedisTo.getSkuId().toString();//

expir

redisTemplate.opsForValue().setIfAbsent(redisKey,

num.toString(),

redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE

key);try

IdWorker.getTimeId();SeckillOrderTo

orderTo

SeckillOrderTo();orderTo.setOrderSn(timeId);orderTo.setMemberId(respVo.getId());orderTo.setNum(num);orderTo.setPromotionSessionId(skuRedisTo.getPromotionSessionId());orderTo.setSkuId(skuRedisTo.getSkuId());orderTo.setSeckillPrice(skuRedisTo.getSeckillPrice());//

TODO

向MQ发送消息rabbitTemplate.convertAndSend(order-event-exchange,order.seckill.order,orderTo);long

System.currentTimeMillis();log.info(耗时...

(s2

{e.printStackTrace();}}}}}}return

Data

orderService;RabbitHandlerpublic

void

{orderService.createSeckillOrder(orderTo);channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}

catch

{channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);}}}5、

/**

createSeckillOrder(SeckillOrderTo

orderTo)

OrderEntity();orderEntity.setOrderSn(orderTo.getOrderSn());orderEntity.setMemberId(orderTo.getMemberId());orderEntity.setCreateTime(new

Date());BigDecimal

orderTo.getSeckillPrice().multiply(BigDecimal.valueOf(orderTo.getNum()));orderEntity.setPayAmount(totalPrice);orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());//保存订单this.save(orderEntity);//保存订单项信息OrderItemEntity

orderItem

OrderItemEntity();orderItem.setOrderSn(orderTo.getOrderSn());orderItem.setRealAmount(totalPrice);orderItem.setSkuQuantity(orderTo.getNum());//保存订单项数据orderItemService.save(orderItem);}七、Sentinel

1、熔断降级限流

服务熔断会导致服务降级,但是和服务降级的区别就是服务熔断会恢复调用链路。

在互联网系统中当下游服务因访问压力过大而响应变慢或失败上游服务为了保护系统整体的可用性可以暂时切断对下游服务的调用。

一旦下游服务C因某些原因变得不可用积压了大量请求服务B的请求线程也随之阻塞。

线程资源逐渐耗尽使得服务B也变得不可用。

紧接着服务

服务降级

整个网站处于流量高峰期服务器压力剧增根据当前业务情况及流量对一些服务和

页面进行有策略的降级[停止服务所有的调用直接返回降级数据]。

以此缓解服务器资源的

的压力以保证核心业务的正常运行同时也保持了客户和大部分客户的得到正确的相应。

相同点

对打入服务的请求流量进行控制使服务能够承担不超过自己能力的流量压力

sentinel官方文档:

--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId/dependency2、下载控制台Releases

alibaba/Sentinel

3、配置服务与控制台的连接地址以及每个服务与控制台交互数据的端口

#sentinel

spring.cloud.sentinel.transport.dashboardlocalhost:8333

spring.cloud.sentinel.transport.port87193、在控制台中配置规则

增加流控规则

management.security.enabledfalse。

暴露的

endpoint

management.endpoints.web.exposure.include*。

暴露的

endpoint

spring-boot-starter-actuator而不是在

common

!--sentinel控制台实时监控信息审计--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactId/dependency2、暴露端点

actuator

management.endpoints.web.exposure.include*4、自定义sentinel的返回信息

自定义响应信息

WebCallbackManager.setUrlBlockHandler(new

UrlBlockHandler()

httpServletResponse.setCharacterEncoding(UTF-8);//

数据格式//

httpServletResponse.setContentType(application/json);//

error

R.error(BizCodeEnum.TOO_MANY_REQUEST.getCode(),

BizCodeEnum.TOO_MANY_REQUEST.getMessage());//

响应到页面//

httpServletResponse.getWriter().write(JSON.toJSONString(error));////

}//

urlBlockHandler(){UrlBlockHandler

urlBlockHandler

响应编码httpServletResponse.setCharacterEncoding(UTF-8);//

数据格式httpServletResponse.setContentType(application/json);R

error

R.error(BizCodeEnum.TOO_MANY_REQUEST.getCode(),

BizCodeEnum.TOO_MANY_REQUEST.getMessage());//

响应到页面httpServletResponse.getWriter().write(JSON.toJSONString(error));}};WebCallbackManager.setUrlBlockHandler(urlBlockHandler);return

urlBlockHandler;}

需要rabbitTemplate,而rabbitTemplate又要使用到rabbitConfig

*/Autowiredprivate

Jackson2JsonMessageConverter();}/**

设置发布确认机制*

spring.rabbitmq.publisher-confirmstrue*

2、ReturnCallback

spring.rabbitmq.publisher-returnstrue*

ReturnCallback*

spring.rabbitmq.template.mandatorytrue*

*/PostConstruct

{rabbitTemplate.setConfirmCallback(new

RabbitTemplate.ConfirmCallback()

{/***

{System.out.println(Broker接收消息成功,

correlationData:

{System.out.println(Broker接收消息失败,

correlationData:

cause);}}});rabbitTemplate.setReturnCallback(new

RabbitTemplate.ReturnCallback()

{/***

MessageConverter而MessageConverter由依赖于

RabbitTemplate

这里直接注入MessageConverter会有循环依赖的问题*

RabbitTemplate

Jackson2JsonMessageConverter();//

}/**

spring.rabbitmq.publisher-confirmstrue*

2、ReturnCallback

spring.rabbitmq.publisher-returnstrue*

ReturnCallback*

spring.rabbitmq.template.mandatorytrue*

*/PostConstruct

{rabbitTemplate.setConfirmCallback(new

RabbitTemplate.ConfirmCallback()

{/***

{System.out.println(Broker接收消息成功,

correlationData:

{System.out.println(Broker接收消息失败,

correlationData:

cause);}}});rabbitTemplate.setReturnCallback(new

RabbitTemplate.ReturnCallback()

{/***

Jackson2JsonMessageConverter();}

}6、熔断降级

现代微服务架构都是分布式的由非常多的服务组成。

不同服务之间相互调用组成复杂的调用链路。

复杂链路上的某一环不稳定就可能会层层级联最终导致整个链路都不可用。

因此我们需要对不稳定的弱依赖服务调用进行熔断降级暂时切断不稳定调用避免局部不稳定因素导致整体的雪崩。

熔断降级作为保护自身的手段通常在**客户端调用端**进行配置。

熔断策略

spring-cloud-starter-openfeign、

spring-cloud-starter-alibaba-sentinel

2、配置文件打开

feign.sentinel.enabledtrue3、简单实例

远程服务被降级

*/GetMapping(/sku/seckill/{skuId})public

Long

R.error(BizCodeEnum.TOO_MANY_REQUEST.getCode(),

BizCodeEnum.VALID_EXCEPTION.getMessage());}

1.5.0

资源名可使用任意有业务语义的字符串比如方法名、接口名或其它可唯一标识的字符串。

try

!--sentinel与Gateway整合--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-sentinel-gateway/artifactIdversion2.1.0.RELEASE/version/dependency9、自定义网关流控回调

/***

{GatewayCallbackManager.setBlockHandler(new

{//网关限流了请求就会调用此回调Overridepublic

MonoServerResponse

handleRequest(ServerWebExchange

exchange,

R.error(BizCodeEnum.TOO_MANY_REQUEST.getCode(),

BizCodeEnum.TOO_MANY_REQUEST.getMessage());String

errorJson

JSON.toJSONString(error);MonoServerResponse

body

ServerResponse.ok().body(Mono.just(errorJson),

String.class);return



SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback