1环境搭建 pox.xml

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--mybatisplus依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependencies>

application.yml

spring:# thymeleaf配置thymeleaf:# 关闭缓存cache: false# 数据源配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seckill?
useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: roothikari:# 连接池名pool-name: DateHikariCP# 最小空闲连接数minimum-idle: 5# 空闲连接存活最大时间,默认600000(10分钟)idle-timeout: 180000# 最大连接数,默认10maximum-pool-size: 10# 从连接池返回的连接的自动提交auto-commit: true# 连接最大存活时间,0表示永久存活,默认1800000(30分钟)max-lifetime: 1800000# 连接超时时间,默认30000(30秒)connection-timeout: 30000# 测试连接是否可用的查询语句connection-test-query: SELECT 1
# Mybatis-plus配置
mybatis-plus:#配置Mapper映射文件mapper-locations: classpath*:/mapper/*Mapper.xml# 配置MyBatis数据返回类型别名(默认别名是类名)type-aliases-package: com.xxxx.seckill.pojo
## Mybatis SQL 打印(方法接口所在的包,不是Mapper.xml所在的包)
logging:level:com.xxxx.seckill.mapper: debug

1.1通用公共结果返回对象

RespBeanEnum.java
@Getter
@ToString
@AllArgsConstructor
public enum RespBeanEnum {//通用SUCCESS(200, "SUCCESS"),ERROR(500, "服务端异常"),//登录模块5002xxLOGIN_ERROR(500210, "用户名或密码不正确"),MOBILE_ERROR(500211, "手机号码格式不正确"),BIND_ERROR(500212, "参数校验异常"),MOBILE_NOT_EXIST(500213, "手机号码不存在"),PASSWORD_UPDATE_FAIL(500214, "密码更新失败"),SESSION_ERROR(500215, "用户不存在"),//秒杀模块5005xxEMPTY_STOCK(500500, "库存不足"),REPEATE_ERROR(500501, "该商品每人限购一件"),REQUEST_ILLEGAL(500502, "请求非法,请重新尝试"),ERROR_CAPTCHA(500503, "验证码错误,请重新输入"),ACCESS_LIMIT_REAHCED(500504, "访问过于频繁,请稍后再试"),//订单模块5003xxORDER_NOT_EXIST(500300, "订单信息不存在"),;private final Integer code;private final String message;
}
RespBean.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {private long code;private String message;private Object obj;/*** 功能描述: 成功返回结果**/public static RespBean success(){return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBeanEnum.SUCCESS.getMessage(),null);}/*** 功能描述: 成功返回结果** @param:* @return:**/public static RespBean success(Object obj){return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBean.success().getMessage(),obj);}/*** 功能描述: 失败返回结果** @param:* @return:**/public static RespBean error(RespBeanEnum respBeanEnum){return new RespBean(respBeanEnum.getCode(),respBeanEnum.getMessage(),null);}/*** 功能描述: 失败返回结果** @param:* @return:*/public static RespBean error(RespBeanEnum respBeanEnum,Object obj){return new RespBean(respBeanEnum.getCode(),respBeanEnum.getMessage(),obj);}}

1.2总体结构图

 2 分布式会话

2.1 实现登录功能

两次明文加密

客户端:PASS=MD5(明文+固定Salt)

服务端:PASS=MD5(用户属于+随机Salt)

导入MD5依赖

<!-- md5 依赖 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>

MD5工具类

MD5Util.java
@Component
public class MD5Util {public static String md5(String src){return DigestUtils.md5Hex(src);}private static final String salt="1a2b3c4d";public static String inputPassToFromPass(String inputPass){String str = "" +salt.charAt(0)+salt.charAt(2)+inputPass+salt.charAt(5)+salt.charAt(4);return md5(str);}public static String formPassToDBPass(String formPass,String salt){String str = "" +salt.charAt(0)+salt.charAt(2)+formPass+salt.charAt(5)+salt.charAt(4);return md5(str);}public static String inputPassToDBPass(String inputPass,String salt){String fromPass = inputPassToFromPass(inputPass);String dbPass = formPassToDBPass(fromPass, salt);return dbPass;}public static void main(String[] args) {// d3b1294a61a07da9b49b6e22b2cbd7f9System.out.println(inputPassToFromPass("123456"));System.out.println(formPassToDBPass("d3b1294a61a07da9b49b6e22b2cbd7f9","1a2b3c4d"));System.out.println(inputPassToDBPass("123456","1a2b3c4d"));}}

2.2 登录功能实现

逆向工程 生成实体类和mapper、service、controller、xml(参考Mybatis-plus)

网站链接:代码生成器(旧) | MyBatis-Plus

ValidatorUtil.Class 自定义手机号格式校验
public class ValidatorUtil {private static final Pattern mobile_pattern = Pattern.compile("[1]([3-9])[0-9]{9}$");public static boolean isMobile(String mobile){if (StringUtils.isEmpty(mobile)){return false;}Matcher matcher = mobile_pattern.matcher(mobile);return matcher.matches();}}
@Controller
@RequestMapping("/login")
@Slf4j
public class LoginController {@Autowiredprivate IUserService userService;/*** 跳转登录页** @return*/@RequestMapping("/toLogin")public String toLogin() {return "login";}/*** 登录* @return*/@RequestMapping("/doLogin")@ResponseBodypublic RespBean doLogin(LoginVo loginVo) {log.info(loginVo.toString());return userService.login(loginVo);}
}

2.2.1登录接口实现

VO实体类

LoginVo.Class
@Data
public class LoginVo {@NotNull@IsMobileprivate String mobile;@NotNull@Length(min = 32)private String password;
}
UserServiceImpl
 @Overridepublic RespBean login(LoginVo loginVo) {String mobile = loginVo.getMobile();String password = loginVo.getPassword();if (StringUtils.isEmpty(mobile)||StringUtils.isEmpty(password)){return RespBean.error(RespBeanEnum.LOGINVO_ERROR);}if (!ValidatorUtil.isMobile(mobile)){return RespBean.error(RespBeanEnum.MOBILE_ERROR);}//根据手机号获取用户User user = userMapper.selectById(mobile);if (null==user){return RespBean.error(RespBeanEnum.LOGINVO_ERROR);}//校验密码if
(!MD5Util.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){return RespBean.error(RespBeanEnum.LOGINVO_ERROR);}return RespBean.success();}

login.html

<!DOCTYPE html>
<html lang="en"xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>登录</title><!-- jquery --><script type="text/javascript" th:src="@{/js/jquery.min.js}"></script><!-- bootstrap --><link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}"/><script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script><!-- jquery-validator --><script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script><script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script><!-- layer --><script type="text/javascript" th:src="@{/layer/layer.js}"></script><!-- md5.js --><script type="text/javascript" th:src="@{/js/md5.min.js}"></script><!-- common.js --><script type="text/javascript" th:src="@{/js/common.js}"></script>
</head>
<body>
<form name="loginForm" id="loginForm" method="post" style="width:50%; margin:0 auto"><h2 style="text-align:center; margin-bottom: 20px">用户登录</h2><div class="form-group"><div class="row"><label class="form-label col-md-4">请输入手机号码</label><div class="col-md-5"><input id="mobile" name="mobile" class="form-control" type="text" placeholder="手机号码" required="true"minlength="11" maxlength="11"/></div><div class="col-md-1"></div></div></div><div class="form-group"><div class="row"><label class="form-label col-md-4">请输入密码</label><div class="col-md-5"><input id="password" name="password" class="form-control" type="password" placeholder="密码"required="true" minlength="6" maxlength="16"/></div></div></div><div class="row"><div class="col-md-5"><button class="btn btn-primary btn-block" type="reset" onclick="reset()">重置</button></div><div class="col-md-5"><button class="btn btn-primary btn-block" type="submit" onclick="login()">登录</button></div></div>
</form>
</body>
<script>function login() {$("#loginForm").validate({submitHandler: function (form) {doLogin();}});}function doLogin() {g_showLoading();var inputPass = $("#password").val();var salt = g_passsword_salt;var str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);var password = md5(str);$.ajax({url: "/login/doLogin",type: "POST",data: {mobile: $("#mobile").val(),password: password},success: function (data) {layer.closeAll();if (data.code == 200) {layer.msg("成功");window.location.href="/goods/toList";} else {layer.msg(data.message);}},error: function () {layer.closeAll();}});}
</script>
</html>

2.3参数校验

使用validation简化我们的代码

pom.xml

<!-- validation组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
自定义手机号码验证规则
IsMobileValidator.Class

*/
public class IsMobileValidator implements ConstraintValidator<IsMobile,String> {private boolean required = false;@Overridepublic void initialize(IsMobile constraintAnnotation) {required = constraintAnnotation.required();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (required){return ValidatorUtil.isMobile(value);}else {if (StringUtils.isEmpty(value)){return true;}else {return ValidatorUtil.isMobile(value);}}}
}

自定义注解

/**
* 验证手机号
*
* @author zhoubin
* @since 1.0.0
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {boolean required() default true;String message() default "手机号码格式错误";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

2.4 异常处理

异常处理主要分为编译时异常和运行时异常RuntimeException,前置时通过捕获异常获取到异常信息,后者是通过代码规范来减少运行时异常的发生,Springboot的全局异常处理有两种 

使用 @ControllerAdvice @ExceptionHandler 注解。
使用 ErrorController来实现
定义全局异常处理类
GlobalException 
/**
* 全局异常
*
* @author zhoubin
* @since 1.0.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GlobalException extends RuntimeException {private RespBeanEnum respBeanEnum; }
GlobalExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public RespBean ExceptionHandler(Exception e) {if (e instanceof GlobalException) {GlobalException ex = (GlobalException) e;return RespBean.error(ex.getRespBeanEnum());} else if (e instanceof BindException) {BindException ex = (BindException) e;RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);respBean.setMessage("参数校验异常:" +
ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());return respBean;}return RespBean.error(RespBeanEnum.ERROR);}
}

2.5 优化登录功能

UserArgumentResolver 判断用户是否登录 拦截器完成
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {@Autowiredprivate IUserService service;@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {Class<?> clazz = methodParameter.getParameterType();return clazz==User.class ;}@Overridepublic Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);HttpServletResponse response = nativeWebRequest.getNativeRequest(HttpServletResponse.class);String ticket = CookieUtil.getCookieValue(request, "userTicket");if (StringUtils.isEmpty(ticket)){return null;}return service.getUserByCookie(ticket,request,response);}
}
WebConfig 配置类
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate UserArgumentResolver userArgumentResolver;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(userArgumentResolver);}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");}
}

秒杀功能

通过Mybatis-Plus自动生成商品对象,秒杀商品对象,商品订单,秒杀商品订单

3.1商品列表页

GoodsMapper.java
/**
* <p>
* Mapper 接口
* </p>
*
* @author zhoubin
* @since 1.0.0
*/
public interface GoodsMapper extends BaseMapper<Goods> {/*** 获取商品列表* @return*/List<GoodsVo> findGoodsVo();
}
GoodsMapper.xml
 <select id="findGoodsVo" resultType="com.yrh.seckill.vo.GoodsVo">SELECT *FROM t_goods gLEFT JOIN t_seckill_goods sgON g.id=sg.goods_id</select>
GoodsController 商品控制层
@Controller
@RequestMapping("/goods")
public class GoodsController {@Autowiredprivate IGoodsService goodsService;/*** 跳转商品列表页** @return*/@RequestMapping("/toList")public String toLogin(Model model, User user) {model.addAttribute("user", user);model.addAttribute("goodsList", goodsService.findGoodsVo());return "goodsList";}
}

goodsList.html  商品信息

<!DOCTYPE html>
<html lang="en"xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>商品列表</title><!-- jquery --><script type="text/javascript" th:src="@{/js/jquery.min.js}"></script><!-- bootstrap --><link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}"/><script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script><!-- layer --><script type="text/javascript" th:src="@{/layer/layer.js}"></script><!-- common.js --><script type="text/javascript" th:src="@{/js/common.js}"></script>
</head>
<body>
<div class="panel panel-default"><div class="panel-heading">秒杀商品列表</div><table class="table" id="goodslist"><tr><td>商品名称</td><td>商品图片</td><td>商品原价</td><td>秒杀价</td><td>库存数量</td><td>详情</td></tr><tr th:each="goods,goodsStat : ${goodsList}"><td th:text="${goods.goodsName}"></td><td><img th:src="@{${goods.goodsImg}}" width="100" height="100"/></td><td th:text="${goods.goodsPrice}"></td><td th:text="${goods.seckillPrice}"></td><td th:text="${goods.stockCount}"></td><td><a th:href="'/goodsDetail.htm?goodsId='+${goods.id}">详情</a></td></tr></table>
</div>
</body>
</html>

效果图

 3.2 详情页面

GoodsMapper
/**
* 根据商品id获取商品详情
* @param goodsId
* @return
*/
GoodsVo findGoodsVoByGoodsId(Long goodsId);
GoodsMapper.xml
<select id="findGoodsVoGoodsId" resultType="com.yrh.seckill.vo.GoodsVo">SELECT *FROM t_goods gLEFT JOIN t_seckill_goods sgON g.id=sg.goods_idwhere g.id=#{goodsId}</select>
GoodsController
根据商品id跳转到商品详情
/**
* 跳转商品详情页
*
* @param model
* @param user
* @param goodsId
* @return
*/
@RequestMapping("/toDetail/{goodsId}")
public String toDetail(Model model, User user, @PathVariable Long goodsId) {model.addAttribute("user", user);GoodsVo goods = goodsService.findGoodsVoByGoodsId(goodsId);model.addAttribute("goods", goods);Date startDate = goods.getStartDate();Date endDate = goods.getEndDate();Date nowDate = new Date();//秒杀状态int secKillStatus = 0;//剩余开始时间int remainSeconds = 0;//秒杀还未开始if (nowDate.before(startDate)) {remainSeconds = (int) ((startDate.getTime()-nowDate.getTime())/1000);// 秒杀已结束} else if (nowDate.after(endDate)) {secKillStatus = 2;remainSeconds = -1;// 秒杀中} else {secKillStatus = 1;remainSeconds = 0;}model.addAttribute("secKillStatus",secKillStatus);model.addAttribute("remainSeconds",remainSeconds);return "goodsDetail"; }

详情页面

goodsDetail.html
<!DOCTYPE html>
<html lang="en"xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>商品详情</title><!-- jquery --><script type="text/javascript" th:src="@{/js/jquery.min.js}"></script><!-- bootstrap --><link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}"/><script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script><!-- layer --><script type="text/javascript" th:src="@{/layer/layer.js}"></script><!-- common.js --><script type="text/javascript" th:src="@{/js/common.js}"></script>
</head>
<body>
<div class="panel panel-default"><div class="panel-heading">秒杀商品详情</div><div class="panel-body"><span th:if="${user eq null}"> 您还没有登录,请登陆后再操作<br/></span><span>没有收货地址的提示。。。</span></div><table class="table" id="goods"><tr><td>商品名称</td><td colspan="3" th:text="${goods.goodsName}"></td></tr><tr><td>商品图片</td><td colspan="3"><img th:src="@{${goods.goodsImg}}" width="200" height="200"/></td></tr><tr><td>秒杀开始时间</td><td th:text="${#dates.format(goods.startDate,'yyyy-MM-dd HH:mm:ss')}"></td><td id="seckillTip"><input type="hidden" id="remainSeconds" th:value="${remainSeconds}"><span th:if="${secKillStatus eq 0}">秒杀倒计时:<span id="countDown" th:text="${remainSeconds}"></span>秒</span><span th:if="${secKillStatus eq 1}">秒杀进行中</span><span th:if="${secKillStatus eq 2}">秒杀已结束</span></td><td><form id="secKillForm" method="post" action="/secKill/doSecKill"><input type="hidden" name="goodsId" th:value="${goods.id}"><button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button></form></td></tr><tr><td>商品原价</td><td colspan="3" th:text="${goods.goodsPrice}"></td></tr><tr><td>秒杀价</td><td colspan="3" th:text="${goods.seckillPrice}"></td></tr><tr><td>库存数量</td><td colspan="3" th:text="${goods.stockCount}"></td></tr></table>
</div>
</body>
<script>$(function () {countDown();});function countDown() {var remainSeconds = $("#remainSeconds").val();var timeout;//秒杀还未开始if (remainSeconds > 0) {$("#buyButton").attr("disabled", true);timeout = setTimeout(function () {$("#countDown").text(remainSeconds - 1);$("#remainSeconds").val(remainSeconds - 1);countDown();}, 1000);// 秒杀进行中} else if (remainSeconds == 0) {if (timeout) {clearTimeout(timeout);}$("#buyButton").attr("disabled", false);$("#seckillTip").html("秒杀进行中")} else {$("#seckillTip").html("秒杀已经结束");$("#buyButton").attr("disabled", true);}};</script>
</html>

效果图 秒杀未开始

秒杀进行中

秒杀结束

 

 

 3.3 秒杀功能实现

IOrderService.java

   /*** 秒杀* @param user* @param goods* @return*/Order seckill(User user, GoodsVo goods);

订单接口实现类

OrderServiceImpl
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
IOrderService {@Autowiredprivate ISeckillGoodsService seckillGoodsService;@Autowiredprivate IGoodsService goodsService;@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate ISeckillOrderService seckillOrderService;/*** 秒杀* @param user* @param goods* @return*/@Override@Transactionalpublic Order seckill(User user, GoodsVo goods) {//秒杀商品表减库存SeckillGoods seckillGoods = seckillGoodsService.getOne(new
QueryWrapper<SeckillGoods>().eq("goods_id",goods.getId()));seckillGoods.setStockCount(seckillGoods.getStockCount()-1);seckillGoodsService.updateById(seckillGoods);//生成订单Order order = new Order();order.setUserId(user.getId());order.setGoodsId(goods.getId());order.setDeliveryAddrId(0L);order.setGoodsName(goods.getGoodsName());order.setGoodsCount(1);order.setGoodsPrice(seckillGoods.getSeckillPrice());order.setOrderChannel(1);order.setStatus(0);order.setCreateDate(new Date());orderMapper.insert(order);//生成秒杀订单SeckillOrder seckillOrder = new SeckillOrder();seckillOrder.setOrderId(order.getId());seckillOrder.setUserId(user.getId());seckillOrder.setGoodsId(goods.getId());seckillOrderService.save(seckillOrder);return order;}
}
SeckillController 秒杀控制层
思路:判断用户是否登录
           判断商品是否还有库存
           判断改商品用户是否重复购买
@Controller
@RequestMapping("/seckill")
public class SeckillController {@Autowiredprivate IGoodsService goodsService;@Autowiredprivate ISeckillOrderService seckillOrderService;@Autowiredprivate IOrderService orderService;@RequestMapping("/doSeckill")public String doSeckill(Model model, User user, Long goodsId) {if (user == null) {return "login";}model.addAttribute("user", user);GoodsVo goods = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goods.getStockCount() < 1) {model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());return "seckillFail";}//判断是否重复抢购SeckillOrder seckillOrder = seckillOrderService.getOne(new
QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id",goodsId));if (seckillOrder != null) {model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "seckillFail";}Order order = orderService.seckill(user, goods);model.addAttribute("order",order);model.addAttribute("goods",goods);return "orderDetail";}
}
订单详情页
<html lang="en"xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>商品详情</title><!-- jquery --><script type="text/javascript" th:src="@{/js/jquery.min.js}"></script><!-- bootstrap --><link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}"/><script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script><!-- layer --><script type="text/javascript" th:src="@{/layer/layer.js}"></script><!-- common.js --><script type="text/javascript" th:src="@{/js/common.js}"></script>
</head>
<body>
<div class="panel panel-default"><div class="panel-heading">秒杀订单详情</div><table class="table" id="order">   <tr>           <td>商品名称</td>   <td th:text="${goods.goodsName}" colspan="3"></td>       </tr>       <tr>           <td>商品图片</td>           <td colspan="2"><img th:src="@{${goods.goodsImg}}" width="200"height="200"/></td>       </tr>       <tr>           <td>订单价格</td>           <td colspan="2" th:text="${order.goodsPrice}"></td>       </tr>       <tr>           <td>下单时间</td>           <td th:text="${#dates.format(order.createDate, 'yyyy-MM-dd HH:mm:ss')}" colspan="2"></td>       </tr>       <tr>           <td>订单状态</td>         <td><span th:if="${order.status eq 0}">未支付</span><span th:if="${order.status eq 1}">待发货</span><span th:if="${order.status eq 2}">已发货</span><span th:if="${order.status eq 3}">已收货</span><span th:if="${order.status eq 4}">已退款</span><span th:if="${order.status eq 5}">已完成</span></td>           <td>               <button class="btn btn-primary btn-block" type="submit" id="payButton">立即支付</button>           </td>       </tr>     <tr>           <td>收货人</td>           <td colspan="2">XXX 18012345678</td></tr><tr><td>收货地址</td><td colspan="2">上海市浦东区世纪大道</td></tr></table>
</div>
</body>
</html>

4 页面优化

4.1 页面缓存

/*** @author: yrh* @Description: 商品列表*  * window优化前 吞吐量:452.55558136798504*  页面静态化*  * window优化后 吞吐量:950.8475220913575* @return: null*/@RequestMapping(value = "/toList",produces = "text/html;charset=utf-8")@ResponseBodypublic String toList(Model model,User user,HttpServletRequest request,HttpServletResponse response){
//        if (StringUtils.isEmpty(ticket)){
//            return "login";
//        }
//        User user = service.getUserByCookie(ticket, request, response);
//        if (null==user){
//            return "login";
//        }//获取缓存ValueOperations operations = redisTemplate.opsForValue();String html = (String) operations.get("goodsList");if (!StringUtils.isEmpty(html)){return html;}model.addAttribute("user",user);model.addAttribute("goodsList",goodsService.findGoodsVo());//缓存为空WebContext context=new WebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap());html= thymeleafViewResolver.getTemplateEngine().process("goodsList",context);if (!StringUtils.isEmpty(html)){operations.set("goodsList",html,60, TimeUnit.SECONDS);}return html;}

详情页面缓存

 /*** @author: yrh* @Description: 商品详情** @return: null*/@RequestMapping(value = "/toDetail2/{goodsId}",produces = "text/html;charset=utf-8")@ResponseBodypublic String toDetail2(Model model,User user,@PathVariable Long goodsId,HttpServletRequest request,HttpServletResponse response){ValueOperations operations = redisTemplate.opsForValue();//是否在缓存当中String html = (String) operations.get("goodsDetail:" + goodsId);if (!StringUtils.isEmpty(html)){return html;}model.addAttribute("user",user);GoodsVo goodsVo = goodsService.findGoodsVoGoodsId(goodsId);Date startDate = goodsVo.getStartDate();Date endDate = goodsVo.getEndDate();Date nowDate = new Date();//秒杀状态int secKillStatus = 0;//秒杀倒计时int remainSeconds = 0;//秒杀还未开始if (nowDate.before(startDate)) {remainSeconds = ((int) ((startDate.getTime() - nowDate.getTime()) / 1000));} else if (nowDate.after(endDate)) {//	秒杀已结束secKillStatus = 2;remainSeconds = -1;} else {//秒杀中secKillStatus = 1;remainSeconds = 0;}model.addAttribute("goods",goodsVo);model.addAttribute("secKillStatus",secKillStatus);model.addAttribute("remainSeconds",remainSeconds);WebContext context=new WebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap());html= thymeleafViewResolver.getTemplateEngine().process("goodsDetail",context);if (!StringUtils.isEmpty(html)){operations.set("goodsDetail:"+ goodsId,html,60,TimeUnit.SECONDS);}return html;}

4.2 解决库存超卖

减库存时判断库存是否足够
OrderServiceImpl.java
//秒杀商品表减库存
SeckillGoods seckillGoods = seckillGoodsService.getOne(new
QueryWrapper<SeckillGoods>().eq("goods_id",goods.getId()));
seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);
seckillGoodsService.update(new UpdateWrapper<SeckillGoods>().set("stock_count", 
seckillGoods.getStockCount()).eq("id", seckillGoods.getId()).gt("stock_count", 
0));
// seckillGoodsService.updateById(seckillGoods);
解决同一用户同时秒杀多件商品。
可以通过数据库建立唯一索引避免

商品表创建唯一索引 

将秒杀订单存放在redis中,方便查询是否重复
OrderServiceImpl.java
 @Override@Transactionalpublic Order seckill(User user, GoodsVo goods) {ValueOperations valueOperations = redisTemplate.opsForValue();//秒杀商品表减库存SeckillGoods seckillGoods = seckillGoodsService.getOne(new QueryWrapper<SeckillGoods>().eq("goods_id", goods.getId()));seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);
//        seckillGoodsService.updateById(seckillGoods);//修改后的秒杀商品表减库存boolean seckillGoodsResult = seckillGoodsService.update(new UpdateWrapper<SeckillGoods>().setSql("stock_count=" + "stock_count-1").eq("goods_id", goods.getId()).gt("stock_count", 0));if (seckillGoods.getStockCount() < 1) {//判断是否还有库存valueOperations.set("isStockEmpty:" + goods.getId(), "0");return null;}//生成订单Order order = new Order();order.setUserId(user.getId());order.setGoodsId(goods.getId());order.setDeliveryAddrId(0L);order.setGoodsName(goods.getGoodsName());order.setGoodsCount(1);order.setGoodsPrice(seckillGoods.getSeckillPrice());order.setOrderChannel(1);order.setStatus(0);order.setCreateDate(new Date());orderMapper.insert(order);//生成秒杀订单//生成秒杀订单SeckillOrder seckillOrder = new SeckillOrder();seckillOrder.setOrderId(order.getId());seckillOrder.setUserId(user.getId());seckillOrder.setGoodsId(goods.getId());seckillOrderService.save(seckillOrder);//存入缓存redisTemplate.opsForValue().set("order:" + user.getId() + ":" + goods.getId(), JsonUtil.object2JsonStr(seckillOrder));return order;}
seckillController.java
@RequestMapping("/seckill")
public class SeckillController {@Autowiredprivate IGoodsService goodsService;@Autowiredprivate ISeckillOrderService seckillOrderService;@Autowiredprivate IOrderService orderService;@Autowiredprivate RedisTemplate redisTemplate;@RequestMapping(value = "/doSeckill", method = RequestMethod.POST)@ResponseBodypublic RespBean doSeckill(User user, Long goodsId) {if (user == null) {return RespBean.error(RespBeanEnum.SESSION_ERROR);}GoodsVo goods = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goods.getStockCount() < 1) {return RespBean.error(RespBeanEnum.EMPTY_STOCK);}//判断是否重复抢购// SeckillOrder seckillOrder = seckillOrderService.getOne(new 
QueryWrapper<SeckillOrder>().eq("user_id",//       user.getId()).eq(//       "goods_id",//       goodsId));String seckillOrderJson = (String) 
redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (!StringUtils.isEmpty(seckillOrderJson)) {return RespBean.error(RespBeanEnum.REPEATE_ERROR);}Order order = orderService.seckill(user, goods);if (null != order) {return RespBean.success(order);}return RespBean.error(RespBeanEnum.ERROR);}
}

5. 接口优化

思路:减少数据库的访问
1,系统初始化的时候把商品库存压入到缓存当中
2,收到请求redis预减库存,库存不足时直接返回,否则进入下一步
3,请求入队,立刻返回排队中
4,请求出队,生成订单和秒杀订单
5,客服端轮询,是否秒杀成功

5.1 Redis操作库存

实现InitializingBean接口 重新初始方法 向redis中压入库存

public class SecKillController implements InitializingBean /*** @author: yrh* @Description: 系统初始化,把商品库存数量加载到Redis* throws Exception*/@Overridepublic void afterPropertiesSet() throws Exception {List<GoodsVo> list = goodsService.findGoodsVo();//压入缓存if (CollectionUtils.isEmpty(list)) {return;}list.forEach(goodsVo -> {redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());EmptyStockMap.put(goodsVo.getId(), false);});}

5.2 RabbitMQ秒杀

SeckillMessage.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SeckillMessage {private User user;private Long goodsId;
}

       配置RabbitMQ队列和交换机规则

RabbitTopicConfig.class
@Configuration
public class RabbitTopicConfig {private static final String QUEUE="seckillQueue";private static final String EXCHANGE="seckillExchange";private static final String ROUTINGKEY="seckill.#";//秒杀队列@Beanpublic Queue queue(){return new Queue(QUEUE);}//声明交换机@Beanpublic TopicExchange topicExchange(){return new TopicExchange(EXCHANGE);}//绑定交换机和队列@Beanpublic Binding binding(){return BindingBuilder.bind(queue()).to(topicExchange()).with(ROUTINGKEY);}
}

信息发送者

MQSender.class
@Service
@Slf4j
public class MQSender {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendSecKillMessage(Object msg){log.info("发送消息:"+msg);rabbitTemplate.convertAndSend("seckillExchange","seckill.msg",msg);}
}

消息消费者 用来异步下单

MQReceiver.class
@Service
@Slf4j
public class MQReceiver {@Autowiredprivate IGoodsService goodsService;@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate IOrderService orderService;//    @RabbitListener(queues = "queue")
//    public void receive(Object msg){
//        log.info("接收消息:"+msg);
//    }
//    @RabbitListener(queues = "queue_topic03")
//    public void receive01(Object msg){
//        log.info("接收消息:"+msg);
//    }
//    @RabbitListener(queues = "queue_topic04")
//    public void receive02(Object msg){
//        log.info("接收消息:"+msg);
//    }@RabbitListener(queues = "seckillQueue")public void receive(String msg) {log.info("接收消息:" + msg);//json转对象SeckillMessage message = JsonUtil.jsonStr2Object(msg, SeckillMessage.class);User user = message.getUser();Long goodsId = message.getGoodsId();GoodsVo goodsVo = goodsService.findGoodsVoGoodsId(goodsId);//判断库存if (goodsVo.getStockCount() < 1) {return;}//判断是否是重复抢购String seckillOrderJson = (String) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (!StringUtils.isEmpty(seckillOrderJson)){return;}orderService.seckill(user,goodsVo);}
}

修改秒杀接口 下单操作先进入队列

@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {@Autowiredprivate IGoodsService goodsService;@Autowiredprivate ISeckillOrderService seckillOrderService;@Autowiredprivate IOrderService orderService;@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate MQSender mqSender;private Map<Long, Boolean> EmptyStockMap = new HashMap<>();* @param user* @param goodsId* @return*/@RequestMapping(value = "/doSeckill", method = RequestMethod.POST)@ResponseBodypublic RespBean doSeckill(User user, Long goodsId) {if (user == null) {return RespBean.error(RespBeanEnum.SESSION_ERROR);}GoodsVo goods = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goods.getStockCount() < 1) {return RespBean.error(RespBeanEnum.EMPTY_STOCK);}String seckillOrderJson = (String) 
redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (!StringUtils.isEmpty(seckillOrderJson)) {return RespBean.error(RespBeanEnum.REPEATE_ERROR);}Order order = orderService.seckill(user, goods);if (null != order) {return RespBean.success(order);}*/ValueOperations valueOperations = redisTemplate.opsForValue();//判断是否重复抢购String seckillOrderJson = (String) valueOperations.get("order:" +
user.getId() + ":" + goodsId);if (!StringUtils.isEmpty(seckillOrderJson)) {return RespBean.error(RespBeanEnum.REPEATE_ERROR);}//内存标记,减少Redis访问if (EmptyStockMap.get(goodsId)) {return RespBean.error(RespBeanEnum.EMPTY_STOCK);}//预减库存Long stock = valueOperations.decrement("seckillGoods:" + goodsId);if (stock < 0) {EmptyStockMap.put(goodsId,true);valueOperations.increment("seckillGoods:" + goodsId);return RespBean.error(RespBeanEnum.EMPTY_STOCK);}// 请求入队,立即返回排队中SeckillMessage message = new SeckillMessage(user, goodsId);mqSender.sendsecKillMessage(JsonUtil.object2JsonStr(message));return RespBean.success(0);}

5.3 客户端轮询秒杀结果

SeckillController.class
 /*** @author: yrh* @Description: 轮询秒杀接口* @return: orderId :成功,-1失败,0排队中*/@RequestMapping(value = "/result", method = RequestMethod.GET)@ResponseBodypublic RespBean result(User user, Long goodsId) {if (null == user) {return RespBean.error(RespBeanEnum.SESSION_ERROR);}Long orderId = seckillOrderService.getResult(user, goodsId);return RespBean.success(orderId);}
ISeckillOrderService.class
Long getResult(User user, Long goodsId);

接口实现类

SeckillOrderServiceImpl.class
 @Overridepublic Long getResult(User user, Long goodsId) {SeckillOrder seckillOrder = seckillOrderMapper.selectOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id", goodsId));if (null != seckillOrder) {return seckillOrder.getOrderId();} else {if ( redisTemplate.hasKey("isStockEmpty:" + goodsId)){return -1L;}else {return 0L;}}}

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 征文投稿丨使用轻量应用服务器搭建扫码测试项目

    编者按&#xff1a;本文来自轻量应用服务器征文活动用户投稿&#xff0c;已获得作者&#xff08;昵称初七&#xff09;授权发布。 公司现有WEB项目已经集成进企业微信自建应用里&#xff0c;还需要对项目功能进行扩充&#xff0c;需要调用扫一扫功能进行条码或二维码的识别&…...

    2024/4/14 11:33:35
  2. GBase 8c AMT表空间管理

    表空间是用于存储数据的物理空间&#xff0c;包括数据库对象&#xff08;如索引、表等&#xff09;。系统将用于物理存储的表空间和用于逻辑存储的模式分开管理&#xff0c;二者之间并无耦合关系。GBase 8c数据库创建后默认包含两个表空间&#xff1a;pg_default和pg_global。其…...

    2024/4/14 11:33:40
  3. 元宇宙技术指南:高质量单视角RGB人体重建

    ©作者 | 江告 01 人体重建简介 在元世界中构造真实的3D人体模型是构建虚拟数字人的首要任务之一。三维人体重建旨在从2D人体信息恢复三维几何&#xff0c;例如对RGB输入进行三维重建。 重建后的三维人体&#xff0c;可以用于影视特效制作&#xff0c;实现包括动作驱动等…...

    2024/4/14 11:33:50
  4. 生化危机8游戏资源分享(更新一波游戏~)

    大家好&#xff0c;我是璐画同学 本来我是打算这个账号一直更新技术系列的&#xff0c;毕竟都是从下边摸爬滚打上来的&#xff0c;所以就是想着将spring系列更新完后就从Java基础一点点将我之前写的笔记什么的分享下&#xff0c;但是今天清内存时&#xff0c;偶然间看到了以前…...

    2024/4/14 11:34:10
  5. SQLServer查询整个数据库中某个特定值

    --查询整个数据库中某个特定值所在的表和字段SQL declare str varchar(100) set str大道至简 --要搜索的字符串declare s varchar(8000) declare tb cursor local for select sif exists(select 1 from [b.name] where [a.name] like %str%) print [b.name].[a.name] from sy…...

    2024/4/14 11:33:35
  6. 【Java集合】HashMap源码分析

    HashMap 1、类图 public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { }2、属性 // 默认初始化容量16 static final int DEFAULT_INITIAL_CAPACITY 1 << 4;// 最大容量 2的30次方1073741824 stat…...

    2024/4/14 11:33:55
  7. 类与对象(下)

    类与对象 再谈构造函数 1、构造函数赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _m…...

    2024/4/16 7:58:23
  8. 8、Redis中BitMaps

    Bitmaps 位存储 统计疫情感染人数&#xff1a;0 1 统计用户信息&#xff1a;活跃&#xff0c;不活跃&#xff01;登录和未登录。 使用bitmap来记录&#xff0c;周一到周日的打卡 周一&#xff1a;1&#xff0c;周二&#xff1a;0&#xff0c;周三&#xff1a;1… 127.0.0.1…...

    2024/4/14 11:34:35
  9. 翻译:https://acs.jxnu.edu.cn/problem/HDU1000

    计算AB 每行输入AB的值&#xff0c;按回车输出 输出AB的值...

    2024/4/7 5:37:49
  10. 7、Redis中Hyperloglog的使用

    Hyperloglog 什么是基数&#xff1f; A{1,3,5,7,8,7} B{1,3,5,7,8} 基数&#xff08;不重复的元素&#xff09;&#xff0c;可以接受误差 简介 Hyperloglog是做基数统计的算法 网页的UV(一个人访问一个网站多次&#xff0c;还是算作一个人) 传统的方式&#xff1a;set保存用…...

    2024/4/14 11:34:30
  11. 今日工作总结: 2022-01-20

    vpn 的测试, 估计就得下周继续操作测试...

    2024/4/18 10:42:47
  12. go语言gin框架下GORM连接mysql数据库的简单小示例

    gin项目目录&#xff1a; 本地数据库文件: SET FOREIGN_KEY_CHECKS0;-- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS user; CREATE TABLE user (id int(11) NOT NULL AUTO_INCREMENT,name varchar(60) D…...

    2024/4/18 13:59:08
  13. 内网穿透简单实现 - 钉钉免费方案 + ngrok.cc

    效果图 左边本地某个端口的服务&#xff0c; 右边是钉钉提供的在线端口映射服务 -- 内容同步 对比 1&#xff09;钉钉内网穿透工具不要钱&#xff0c;也不用注册&#xff0c;速度还行。 2&#xff09;ngrok.cc现在要实名验证&#xff0c;收费&#xffe5;2&#xff0c;但是其…...

    2024/4/17 21:41:37
  14. 常用类(Object类、Scanner、String)

    Object类 概述&#xff1a;是java中所有类的父类&#xff0c;所有的类都继承自这个类&#xff0c;由于Object类是属于java.lang包下的&#xff0c;所以我们使用的时候不需要导包 构造方法&#xff1a;Object()&#xff1b; 我们之前说过&#xff0c;在子类的构造方法中&…...

    2024/4/18 12:01:48
  15. Premiere 添加字幕

    1.字幕&#xff0c;主要分为隐藏式字幕&#xff08;又称 CC 字幕&#xff09;和开放式字幕两大类。 a.隐藏式字幕通常是由观众来决定是否需要显示字幕。 b.开放式字幕总是可见的。中国大陆的多数影视节目均采用开放式字幕。 2.新建字幕 a.Pr菜单&#xff1a;文件/新建/字幕 或…...

    2024/4/14 11:35:45
  16. 5、Redis五大基本数据类型

    1、官方的介绍 2、基本命令 set name dxz#设置键值对namedxz get name #得到name所对应的值 exists name#查看name是否存在 move name 1#移除name属性 key * #查看所有的key expire name 10 #设置name键值对有效期为10s ttl name #查看name键值对的有效时间3、String&#xff…...

    2024/4/19 14:23:52
  17. SQL 开窗函数

    SQL 开窗函数⭐️前言&#x1f3c6;开窗函数--Mysql&#x1f4dd; 建表语句✌ ROW_NUMBER()&#x1f356;定义&#x1f9c0; SQL&#x1f35f; 说明✌ RANK&#xff08;&#xff09;&#x1f356;定义&#x1f9c0; SQL&#x1f35f; 说明✌ DENSE_RANK&#xff08;&#xff09…...

    2024/4/19 15:11:37
  18. Python编程挑战100题:07计算数字范围中所以的偶数

    输入开始和结束值&#xff08;不包含&#xff09;&#xff0c;得到所有偶数 偶数&#xff1a;能被2所整除的整数&#xff0c;是2的倍数 输入&#xff1a;begin4&#xff0c;end15 返回&#xff1a;【4,6,8,10,12,14,】 begin4 end15 def get_even_numbers(begin,end):result[]…...

    2024/4/23 19:10:49
  19. Git后续学习和命令操作

    常见的 5 种开源许可协议 BSD&#xff08;Berkeley Software Distribution&#xff09; Apache Licence 2.0 GPL&#xff08;GNU General Public License&#xff09; 具有传染性的一种开源协议&#xff0c;不允许修改后和衍生的代码做为闭源的商业软件发布和销售 使用 GPL …...

    2024/4/15 3:01:01
  20. GBase 8c AMT 服务器操作

    服务器用来与后台数据库实例连接。已创建连接的服务器下默认包含数据库内容、表空间和登陆/组角色信息。每个数据库下默认包含发布、订阅、事件触发器、外部数据封装器、强制转换、扩展、模式、目录和语言。...

    2024/4/7 5:37:43

最新文章

  1. 基于SSM的物业管理系统(含源码+sql+视频导入教程+文档+PPT)

    &#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的物业管理系统2拥有三种角色 管理员&#xff1a;用户管理、物业管理、房产信息管理、小区概况管理、开发商管理、收费标准管理、物业公司管理等 物业&#xff1a;住户管理、收费…...

    2024/4/26 14:46:04
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. STL--vector有哪些应用场景

    vector 在 C 中是一种非常灵活和强大的容器&#xff0c;适用于多种不同的应用场景。以下是一些常见的应用场景&#xff1a; 1 动态数据集合&#xff1a;当你不确定数据集的大小&#xff0c;或者数据集的大小会随时间变化时&#xff0c;vector 是理想的选择。例如&#xff0c;在…...

    2024/4/23 21:36:23
  4. 【python】Flask Web框架

    文章目录 WSGI(Web服务器网关接口)示例Web应用程序Web框架Flask框架创建项目安装Flask创建一个基本的 Flask 应用程序调试模式路由添加变量构造URLHTTP方法静态文件模板—— Jinja2模板文件(Template File)<...

    2024/4/21 20:26:20
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/25 11:51:20
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/25 18:39:24
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/25 18:38:39
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/25 18:39:23
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/25 18:39:22
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/25 18:39:22
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/25 18:39:20
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/25 16:48:44
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/25 13:39:44
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/25 18:39:16
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/25 0:00:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/25 4:19:21
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/25 18:39:12
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/25 2:10:52
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/25 13:19:01
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/25 18:38:58
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/25 18:38:57
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57