1 在gitee上添加.yml文件
1.1 添加good-server.yml文件
server:port: 8084
spring:datasource:url: jdbc:mysql://localhost:3306/shop_goods?serverTimezone=GMT%2B8driverClassName: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceusername: rootpassword: 123456
mybatis:configuration:default-fetch-size: 100default-statement-timeout: 3000map-underscore-to-camel-case: true
1.2 添加seckill-server.yml文件
server:port: 8085
spring:datasource:url: jdbc:mysql://localhost:3306/shop_seckill?serverTimezone=GMT%2B8driverClassName: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceusername: rootpassword: 123456
mybatis:configuration:default-fetch-size: 100default-statement-timeout: 3000map-underscore-to-camel-case: true
2 创建启动类
2.1 创建商品服务启动类
@SpringBootApplication
@EnableEurekaClient
public class GoodServerApp {public static void main(String[] args) {SpringApplication.run(GoodServerApp.class, args);}
}
2.2 创建秒杀启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SeckillServerApp {public static void main(String[] args) {SpringApplication.run(SeckillServerApp.class, args);}
}
3 编写前端商品页面
<!DOCTYPE html>
<html lang="en">
<head><title>商品列表</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><script type="text/javascript" src="/js/jquery.min.js"></script><link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" /><!-- bootstrap --><script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script><script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script> <!-- jquery-validator --><script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script><script type="text/javascript" src="/layer/layer.js"></script><!-- layer --><script type="text/javascript" src="/js/md5.min.js"></script><!-- md5.js --><script type="text/javascript" src="/js/common.js"></script><!-- common.js -->
</head>
<body>
<div class="panel panel-default"><div class="panel-heading">秒杀商品列表</div><table class="table" id="goodlist"><tr><td>商品名称</td><td>商品图片</td><td>商品原价</td><td>秒杀价</td><td>库存数量</td><td>详情</td></tr></table>
</div><script type="text/javascript">String.prototype.format=function () {if(arguments.length==0){return this;}var obj=arguments[0];var s = this;for(var key in obj){s= s.replace(new RegExp("\\{\\{"+key+"\\}\\}","g"),obj[key]);}return s;};var template="<tr><td>{{goodName}}</td>" +"<td><img src='{{goodImg}}' width='100px' height='100px' /> </td>" +"<td>{{goodPrice}}</td>" +"<td>{{seckillPrice}}</td>" +"<td>{{stockCount}}</td>" +"<td> <a href='good_detail.html?seckillId={{id}}'>详情</a> </td></tr>";$(function () {$.ajax({url: "http://localhost:9000/seckill/seckillGood/query",type: "get",xhrFields: {withCredentials: true}, //启用cookiesuccess:function (data) {if(data.code==200){//填充表格中的数据render(data.data);}else{layer.msg(data.msg)}}});});function render(goodlist) {for(var i=0;i<goodlist.length;i++){$("#goodlist").append(template.format(goodlist[i]));}}</script>
</body>
</html>
4 商品查询
由于在前端页面展示的信息来自不同的两张表,因此需要运用远程调用:
-
- 在单表查询 数据 t_seckill_good 数据 秒杀的商品 列表 SeckillGoodList
-
- 获取 good_id 集合 ids[1,2]
-
- 远程调用 good-server 传递参数 [1,2] 在商品表中查询 t_goods 数据 GoodList
4.1 创建实体类
1 创建商品类
@Data
public class Good implements Serializable {private Long id;private String goodName;private String goodTitle;private String goodImg;private String goodDetail;private BigDecimal goodPrice;private Integer goodStock;
}
2 创建秒杀类
@Data
public class SeckillGoods implements Serializable {private Long id;private Long goodId;private BigDecimal seckillPrice;private Integer stockCount;//时间的问题后续得处理 ----@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")private Date startDate;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")private Date endDate;
}
3 创建前端封装类(对应前端页面需要展示的数据)
@Data
public class SeckillGoodVo extends SeckillGood implements Serializable{private String goodName;private String goodTitle;private String goodImg;private String goodDetail;private BigDecimal goodPrice;
}
4.2 实现远程调用(通过远程调用传递的ids来查找商品信息)
1 创建断路器
public class GoodFeignHystrix implements GoodFeignApi {@Overridepublic Result<List<Good>> queryByIds(List<Long> ids) {return null;}
}
2 创建远程调用接口
@FeignClient(name = "good-server", fallbackFactory = GoodFeignHystrix.class)
public interface GoodFeignApi {@RequestMapping("/queryByIds")public Result<List<Good>> queryByIds(@RequestParam("ids") List<Long> ids);}
3 创建Mapper接口
@Mapper
public interface GoodMapper {/*** 根据id查询商品信息* @param ids* @return*/@SelectProvider(type = GoodMapperSQLProvider.class, method = "queryByIds")public List<Good> queryByIds(@Param("ids") List<Long> ids);/*** 由于没有mapper的配置文件,不能使用foreach标签,因此在这里实现SQL的循环*/class GoodMapperSQLProvider{/*** select * from t_goods where id in (x, x, x,....),将ids循环遍历到()内* @param ids* @return*/public String queryByIds(@Param("ids") List<Long> ids){StringBuilder sb = new StringBuilder();sb.append("select * from t_goods ");if (ids != null || ids.size() > 0){sb.append(" where id in (");for (int i = 0; i < ids.size(); i++) {if (i != 0){sb.append(",");}sb.append(ids.get(i));}sb.append(")");}return sb.toString();}}
}
4 创建service业务逻辑接口及其实现类
service业务逻辑接口
public interface GoodService {/*** 根据id查询商品信息* @param ids* @return*/public List<Good> queryByIds(List<Long> ids);
}
实现类
@Service
public class GoodServiceImpl implements GoodService {@Autowiredprivate GoodMapper goodMapper;@Overridepublic List<Good> queryByIds(List<Long> ids) {if (ids == null || ids.size() == 0){return Collections.emptyList();}return goodMapper.queryByIds(ids);}
}
5 创建controller层
@RestController
public class GoodFeignClient implements GoodFeignApi {@Autowiredprivate GoodService goodService;@Overridepublic Result<List<Good>> queryByIds(List<Long> ids) {List<Good> goodlist = goodService.queryByIds(ids);return Result.success(goodlist);}
}
4.3 数据聚合(把商品信息和秒杀信息聚合为前端页面所需的类)
1 创建秒杀的CodeMsg
public class SeckillCodeMsg extends CodeMsg {public SeckillCodeMsg() {}public SeckillCodeMsg(Integer code, String msg) {super(code, msg);}public static final SeckillCodeMsg PRODUCT_SERVER_ERROR= new SeckillCodeMsg(500010,"商品微服务繁忙");public static final SeckillCodeMsg LOGIN_TIMEOUT= new SeckillCodeMsg(500011,"登录信息过期了");public static final SeckillCodeMsg OP_ERROR= new SeckillCodeMsg(500012,"非法操作");}
2 创建Mapper接口
@Mapper
public interface SeckillGoodMapper {@Select("SELECT * FROM t_seckill_goods")public List<SeckillGood> query();
}
3 创建service业务逻辑接口及其实现类
service业务逻辑接口
public interface SeckillGoodService {/*** 查询商品数据* @return*/public List<SeckillGoodVo> query();
}
实现类
@Service
public class SeckillGoodServiceImpl implements SeckillGoodService {@Autowiredprivate SeckillGoodMapper seckillGoodMapper;@Autowiredprivate GoodFeignApi goodFeignApi;@Overridepublic List<SeckillGoodVo> query() {//1. 单表查询 数据 t_seckill_good 数据 秒杀的商品 列表 SeckillGoodListList<SeckillGood> seckillGoodList = seckillGoodMapper.query();//2. 获取 good_id 集合 ids[1,2]//3 远程调用 good-server 传递参数 [1,2] 在商品表中查询 t_goods 数据 GoodListList<SeckillGoodVo> seckillGoodVoList = getSeckillGoodVos(seckillGoodList);return seckillGoodVoList;}/*** 获取秒杀商品列表** @param seckillGoodList* @return*/private List<SeckillGoodVo> getSeckillGoodVos(List<SeckillGood> seckillGoodList) {//利用set集合来进行数据去重Set<Long> idSet = new HashSet<>();for (SeckillGood seckillGood : seckillGoodList) {//去除重复的goodididSet.add(seckillGood.getGoodId());}List<Long> ids = new ArrayList<>(idSet);//远程调用获取商品信息Result<List<Good>> result = goodFeignApi.queryByIds(ids);//远程调用失败if (result == null || result.hasError()) {throw new BusinessException(SeckillCodeMsg.PRODUCT_SERVER_ERROR);}//远程调用成功List<Good> goodList = result.getData();//获取商品信息存到Map中Map<Long, Good> goodMap = new HashMap<>();for (Good good : goodList) {goodMap.put(good.getId(), good);}//将商品信息和秒杀信息聚合List<SeckillGoodVo> seckillGoodVoList = new ArrayList<>();for (SeckillGood seckillGood : seckillGoodList) {//获取商品Good good = goodMap.get(seckillGood.getGoodId());//聚合SeckillGoodVo vo = new SeckillGoodVo();vo.setGoodDetail(good.getGoodDetail());vo.setGoodImg(good.getGoodImg());vo.setGoodName(good.getGoodName());vo.setGoodPrice(good.getGoodPrice());vo.setGoodTitle(good.getGoodTitle());//秒杀的结束时间vo.setEndDate(seckillGood.getEndDate());vo.setGoodId(good.getId());vo.setId(seckillGood.getId());//场次idvo.setStartDate(seckillGood.getStartDate());//秒杀开始时间vo.setStockCount(seckillGood.getStockCount());//秒杀商品的数量vo.setSeckillPrice(seckillGood.getSeckillPrice());//秒杀价格//添加到集合中seckillGoodVoList.add(vo);}return seckillGoodVoList;}
}
4 创建controller层
@RestController
@RequestMapping("/seckillGood")
public class SeckillGoodController {@Autowiredprivate SeckillGoodService seckillGoodService;@RequestMapping("/query")public Result query(){List<SeckillGoodVo> seckillGoodVoList = seckillGoodService.query();return Result.success(seckillGoodVoList);}
}