1 在gitee上添加.yml文件
1.1 添加good-server.yml文件
server:
port: 8084
spring:
datasource:
url: jdbc:mysql://localhost:3306/shop_goods?serverTimezone=GMT%2B8
driverClassName: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: 123456
mybatis:
configuration:
default-fetch-size: 100
default-statement-timeout: 3000
map-underscore-to-camel-case: true
1.2 添加seckill-server.yml文件
server:
port: 8085
spring:
datasource:
url: jdbc:mysql://localhost:3306/shop_seckill?serverTimezone=GMT%2B8
driverClassName: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: 123456
mybatis:
configuration:
default-fetch-size: 100
default-statement-timeout: 3000
map-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}, //启用cookie
success: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 {
@Override
public 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 {
@Autowired
private GoodMapper goodMapper;
@Override
public 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 {
@Autowired
private GoodService goodService;
@Override
public 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 {
@Autowired
private SeckillGoodMapper seckillGoodMapper;
@Autowired
private GoodFeignApi goodFeignApi;
@Override
public List<SeckillGoodVo> query() {
//1. 单表查询 数据 t_seckill_good 数据 秒杀的商品 列表 SeckillGoodList
List<SeckillGood> seckillGoodList = seckillGoodMapper.query();
//2. 获取 good_id 集合 ids[1,2]
//3 远程调用 good-server 传递参数 [1,2] 在商品表中查询 t_goods 数据 GoodList
List<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) {
//去除重复的goodid
idSet.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());//场次id
vo.setStartDate(seckillGood.getStartDate());//秒杀开始时间
vo.setStockCount(seckillGood.getStockCount());//秒杀商品的数量
vo.setSeckillPrice(seckillGood.getSeckillPrice());//秒杀价格
//添加到集合中
seckillGoodVoList.add(vo);
}
return seckillGoodVoList;
}
}
4 创建controller层
@RestController
@RequestMapping("/seckillGood")
public class SeckillGoodController {
@Autowired
private SeckillGoodService seckillGoodService;
@RequestMapping("/query")
public Result query(){
List<SeckillGoodVo> seckillGoodVoList = seckillGoodService.query();
return Result.success(seckillGoodVoList);
}
}