商品创建
先建立领域模型,避免直接进行数据库原型设计。
新建商品模型,MerchandiseModel类
package org.example.service.model;
import java.math.BigDecimal;
public class MerchandiseModel {
private Integer id;
//商品名称
private String title;
//商品价格
private BigDecimal price;
//商品库存
private Integer stock;
//商品的描述
private String description;
//商品销量
private Integer sales;
//商品描述图片的url
private String imgUrl;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
}
设计数据库——商品表 + 库存表
注意:商品库存与交易流水相关,对商品表的操作就是对库存表的操作,将库存设计到另外一张表中。
本项目中商品销量暂时用于展示,将其放在商品表中。当用户发生交易行为之后,通过异步的方式给商品销量+1,而不会影响下单。
使用mybatis-generator自动生成dataobject对象、dao文件、mapping文件
在pom.xml中修改配置
<!-- 允许自动覆盖文件 在实际的项目开发中不能这样设置 -->
<!-- <overwrite>true</overwrite>-->
<overwrite>false</overwrite>
在mybatis-generator.xml中生成对应的商品表和库存表及类名
<table tableName="merchandise" domainObjectName="MerchandiseDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false"></table>
<table tableName="merchandise_stock" domainObjectName="MerchandiseStockDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false"></table>
运行方式:在Run中配置新的Maven启动项
mybatis-generator根据数据库中的设置的精度自动生成的类型
默认情况下的转换规则为:
如果精度>0或者长度>18,就会使用java.math.BigDecimal
如果精度=0并且10<=长度<=18,就会使用java.lang.Long
如果精度=0并且5<=长度<=9,就会使用java.lang.Integer
如果精度=0并且长度<5,就会使用java.lang.Short
因为在设计商品表时,将price设置了默认值为0,同时其长度设置为10,所以自动生成为Long类型。
修改price的类型,同时重新生成相关文件
修改MerchandiseDOMapper.xml文件
<insert id="insert" parameterType="org.example.dataobject.MerchandiseDO" useGeneratedKeys="true" keyProperty="id">
<insert id="insertSelective" parameterType="org.example.dataobject.MerchandiseDO" useGeneratedKeys="true" keyProperty="id">
修改MerchandiseStockDOMapper.xml文件
<insert id="insert" parameterType="org.example.dataobject.MerchandiseStockDO" useGeneratedKeys="true" keyProperty="id">
<insert id="insertSelective" parameterType="org.example.dataobject.MerchandiseStockDO" useGeneratedKeys="true" keyProperty="id">
新建MerchandiesService类——维护一套最基础的接口
package org.example.service;
import org.example.service.model.MerchandiseModel;
import java.util.List;
public interface MerchandiseService {
//创建商品
MerchandiseModel createMerchandise(MerchandiseModel merchandiseModel);
//商品列表浏览
List<MerchandiseModel> listMerchandise();
//商品详情浏览
MerchandiseModel getMerchandiseById(Integer id);
}
新建MerchandiseServiceImpl类——实现相关的service
package org.example.service.impl;
import org.example.service.MerchandiseService;
import org.example.service.model.MerchandiseModel;
import org.example.validator.ValidatorImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class MerchandiseServiceImpl implements MerchandiseService {
@Autowired
private ValidatorImpl validator;
@Override
@Transactional //创建商品必须得在同一个事务当中
public MerchandiseModel createMerchandise(MerchandiseModel merchandiseModel) {
//校验入参
//转化MerchandiseModel->dataobject
//写入数据库
//返回创建完成的对象
return null;
}
@Override
public List<MerchandiseModel> listMerchandise() {
return null;
}
@Override
public MerchandiseModel getMerchandiseById(Integer id) {
return null;
}
}
修改MerchandiseModel.java
package org.example.service.model;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
public class MerchandiseModel {
private Integer id;
//商品名称
@NotBlank(message = "商品名称不能为空")
private String title;
//商品价格
@NotNull(message = "商品价格不能为空")
@Min(value = 0,message = "商品价格必须大于0")
private BigDecimal price;
//商品库存
@NotNull(message = "库存不能不填")
private Integer stock;
//商品的描述
@NotBlank(message = "商品描述信息不能为空")
private String description;
//商品销量(不属于入参范围,因为它不是创建商品的时候传进来的,而是通过别的方式统计进来的)
private Integer sales;
//商品描述图片的url
@NotBlank(message = "商品图片信息不能为空")
private String imgUrl;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
}
修改MerchandiseServiceImpl.java
double类型在Java内部传到前端会产生精度问题,比如说1.9的double对象变成1.99999....
@Service public class MerchandiseServiceImpl implements MerchandiseService { @Autowired private ValidatorImpl validator; @Autowired private MerchandiseDOMapper merchandiseDOMapper; @Autowired private MerchandiseStockDOMapper merchandiseStockDOMapper; //model->dataobject(DO)方法 private MerchandiseDO convertMerchandiseDOFromMerchandiseModel(MerchandiseModel merchandiseModel) { if (merchandiseModel == null) { return null; } MerchandiseDO merchandiseDO = new MerchandiseDO(); //不会copy类型不一样的对象 BeanUtils.copyProperties(merchandiseModel, merchandiseDO); merchandiseDO.setPrice((merchandiseModel.getPrice().doubleValue())); return merchandiseDO; } private MerchandiseStockDO convertMerchandiseStockDOFromMerchandiseModel(MerchandiseModel merchandiseModel) { if (merchandiseModel == null) { return null; } MerchandiseStockDO merchandiseStockDO = new MerchandiseStockDO(); merchandiseStockDO.setMerchandiseId(merchandiseModel.getId()); merchandiseStockDO.setStock(merchandiseModel.getStock()); return merchandiseStockDO; } @Override @Transactional //创建商品必须得在同一个事务当中 public MerchandiseModel createMerchandise(MerchandiseModel merchandiseModel) throws BusinessException { //校验入参 ValidationResult result = validator.validate(merchandiseModel); if (result.isHasErrors()){ throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,result.getErrMsg()); } //转化MerchandiseModel->dataobject MerchandiseDO merchandiseDO = this.convertMerchandiseDOFromMerchandiseModel(merchandiseModel); merchandiseModel.setId(merchandiseDO.getId()); MerchandiseStockDO merchandiseStockDO = this.convertMerchandiseStockDOFromMerchandiseModel(merchandiseModel); //写入数据库 merchandiseDOMapper.insertSelective(merchandiseDO); merchandiseStockDOMapper.insertSelective(merchandiseStockDO); //返回创建完成的对象 return this.getMerchandiseById(merchandiseModel.getId()); } @Override public List<MerchandiseModel> listMerchandise() { return null; } @Override public MerchandiseModel getMerchandiseById(Integer id) { MerchandiseDO merchandiseDO = merchandiseDOMapper.selectByPrimaryKey(id); if (merchandiseDO == null){ return null; } //操作获得库存数量 return null; } }
在MerchandiseStockDOMapper.xml中添加查询语句
<select id="selectByMerchandiseId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from merchandise_stock
where merchandise_id = #{merchandiseId,jdbcType=INTEGER}
</select>
在MerchandiseStockDOMapper.java中添加查询方法
MerchandiseStockDO selectByMerchandiseId(Integer merchandiseId);
最终MerchandiseServiceImpl.java实现
package org.example.service.impl;
import org.example.dao.MerchandiseDOMapper;
import org.example.dao.MerchandiseStockDOMapper;
import org.example.dataobject.MerchandiseDO;
import org.example.dataobject.MerchandiseStockDO;
import org.example.error.BusinessException;
import org.example.error.EmBusinessError;
import org.example.service.MerchandiseService;
import org.example.service.model.MerchandiseModel;
import org.example.validator.ValidationResult;
import org.example.validator.ValidatorImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
@Service
public class MerchandiseServiceImpl implements MerchandiseService {
@Autowired
private ValidatorImpl validator;
@Autowired
private MerchandiseDOMapper merchandiseDOMapper;
@Autowired
private MerchandiseStockDOMapper merchandiseStockDOMapper;
//model->dataobject(DO)方法
private MerchandiseDO convertMerchandiseDOFromMerchandiseModel(MerchandiseModel merchandiseModel) {
if (merchandiseModel == null) {
return null;
}
MerchandiseDO merchandiseDO = new MerchandiseDO();
//不会copy类型不一样的对象
BeanUtils.copyProperties(merchandiseModel, merchandiseDO);
merchandiseDO.setPrice((merchandiseModel.getPrice().doubleValue()));
return merchandiseDO;
}
private MerchandiseStockDO convertMerchandiseStockDOFromMerchandiseModel(MerchandiseModel merchandiseModel) {
if (merchandiseModel == null) {
return null;
}
MerchandiseStockDO merchandiseStockDO = new MerchandiseStockDO();
merchandiseStockDO.setMerchandiseId(merchandiseModel.getId());
merchandiseStockDO.setStock(merchandiseModel.getStock());
return merchandiseStockDO;
}
@Override
@Transactional //创建商品必须得在同一个事务当中
public MerchandiseModel createMerchandise(MerchandiseModel merchandiseModel) throws BusinessException {
//校验入参
ValidationResult result = validator.validate(merchandiseModel);
if (result.isHasErrors()){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,result.getErrMsg());
}
//转化MerchandiseModel->dataobject
MerchandiseDO merchandiseDO = this.convertMerchandiseDOFromMerchandiseModel(merchandiseModel);
merchandiseModel.setId(merchandiseDO.getId());
MerchandiseStockDO merchandiseStockDO = this.convertMerchandiseStockDOFromMerchandiseModel(merchandiseModel);
//写入数据库
merchandiseDOMapper.insertSelective(merchandiseDO);
merchandiseStockDOMapper.insertSelective(merchandiseStockDO);
//返回创建完成的对象
return this.getMerchandiseById(merchandiseModel.getId());
}
@Override
public List<MerchandiseModel> listMerchandise() {
return null;
}
@Override
public MerchandiseModel getMerchandiseById(Integer id) {
MerchandiseDO merchandiseDO = merchandiseDOMapper.selectByPrimaryKey(id);
if (merchandiseDO == null){
return null;
}
//操作获得库存数量
MerchandiseStockDO merchandiseStockDO = merchandiseStockDOMapper.selectByMerchandiseId(merchandiseDO.getId());
//将dataobject->model
MerchandiseModel merchandiseModel = convertModelFromDataboject(merchandiseDO,merchandiseStockDO);
return merchandiseModel;
}
//将dataobject->model
private MerchandiseModel convertModelFromDataboject(MerchandiseDO merchandiseDO,MerchandiseStockDO merchandiseStockDO){
MerchandiseModel merchandiseModel = new MerchandiseModel();
BeanUtils.copyProperties(merchandiseDO,merchandiseModel);
merchandiseModel.setPrice(new BigDecimal(merchandiseDO.getPrice()));
merchandiseModel.setStock(merchandiseStockDO.getStock());
return merchandiseModel;
}
}
新建MerchandiseVO.java(展示给前端用户)
package org.example.controller.viewobject;
import java.math.BigDecimal;
public class MerchandiseVO {
private Integer id;
//商品名称
private String title;
//商品价格
private BigDecimal price;
//商品库存
private Integer stock;
//商品的描述
private String description;
//商品销量(不属于入参范围,因为它不是创建商品的时候传进来的,而是通过别的方式统计进来的)
private Integer sales;
//商品描述图片的url
private String imgUrl;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
}
新建MerchandiseController.java
package org.example.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.example.controller.viewobject.MerchandiseVO;
import org.example.error.BusinessException;
import org.example.response.CommonReturnType;
import org.example.service.MerchandiseService;
import org.example.service.model.MerchandiseModel;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
@Controller("/merchandise") //控制器(注入服务),用于标注控制层
@RequestMapping("/merchandise")
@CrossOrigin(origins = "http://localhost:63342",allowCredentials = "true",allowedHeaders = "*")
@Api(value = "Swagger接口测试")
public class MerchandiseController extends BaseController{
@Autowired
private MerchandiseService merchandiseService;
//创建商品的controller
@RequestMapping(value = "/createMerchandise",method = {RequestMethod.GET},consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
@ApiOperation(value = "创建商品")
@ApiImplicitParams({
@ApiImplicitParam(name = "title", value = "商品名称",required = true),
@ApiImplicitParam(name = "price", value = "商品价格",required = true,dataType = "BigDecimal"),
@ApiImplicitParam(name = "stock", value = "商品库存",required = true,dataType = "Integer"),
@ApiImplicitParam(name = "description", value = "商品描述",required = true),
@ApiImplicitParam(name = "imgUrl", value = "商品描述图片的url",required = true)
})
public CommonReturnType createMerchandise(@RequestParam(name = "title") String title,
@RequestParam(name = "price") BigDecimal price,
@RequestParam(name = "stock") Integer stock,
@RequestParam(name = "description") String description,
@RequestParam(name = "imgUrl") String imgUrl) throws BusinessException {
//封装service请求用来创建商品
MerchandiseModel merchandiseModel = new MerchandiseModel();
merchandiseModel.setTitle(title);
merchandiseModel.setPrice(price);
merchandiseModel.setStock(stock);
merchandiseModel.setDescription(description);
merchandiseModel.setImgUrl(imgUrl);
MerchandiseModel merchandiseModelForReturn = merchandiseService.createMerchandise(merchandiseModel);
//把对应创建好的商品信息返回给前端
MerchandiseVO merchandiseVO = convertVOFromModel(merchandiseModelForReturn);
return CommonReturnType.create(merchandiseVO);
}
//在互联网的企业级应用中,VO与Model的定义完全不一样,而且许多都会用到聚合操作,很多时候为了前端交互逻辑上的方便,viewobject定义的比model层更大
private MerchandiseVO convertVOFromModel(MerchandiseModel merchandiseModel){
if (merchandiseModel == null){
return null;
}
MerchandiseVO merchandiseVO = new MerchandiseVO();
BeanUtils.copyProperties(merchandiseModel,merchandiseVO);
return merchandiseVO;
}
}
新建createimerchandise.html
<html>
<head>
<meta charset="UTF-8"> <!-- 非常重要,没有这个,许多中文在页面上显示都是乱码 -->
<!-- 引入css样式 -->
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/component.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<!-- 引入jquery的版本 -->
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
</head>
<body class="login">
<div class="content">
<h3 class="form-title">创建商品</h3>
<div class="form-group">
<label class="control-label">商品名称</label>
<div>
<input class="form-control" type="text" name="title" id="title"/>
</div>
</div>
<div class="form-group">
<label class="control-label">商品描述</label>
<div>
<input class="form-control" type="text" name="description" id="description"/>
</div>
</div>
<div class="form-group">
<label class="control-label">图片</label>
<div>
<input class="form-control" type="text" name="imgUrl" id="imgUrl"/>
</div>
</div>
<div class="form-group">
<label class="control-label">价格</label>
<div>
<input class="form-control" type="text" name="price" id="price"/>
</div>
</div>
<div class="form-group">
<label class="control-label">库存</label>
<div>
<input class="form-control" type="text" name="stock" id="stock"/>
</div>
</div>
<div class="form-actions">
<!-- 提交的button -->
<button class="btn btn-info" id="createMerchandise" type="submit"> <!-- 这个id是我们后面需要绑定createMerchandise的click(点击)事件 -->
提交创建
</button>
</div>
</div>
</body>
<!-- 用于解决一些动态请求的问题 -->
<script>
//所有 jQuery 函数位于一个 document ready 函数中
//所有的 jQuery 动态绑定元素必须在 jQuery 的一个document ready里面完成
jQuery(document).ready(function () {
//使用jQuery的id标签createMerchandise,绑定createMerchandise的click事件
$("#createMerchandise").on("click",function () {
var title = $("#title").val();
var description = $("#description").val();
var imgUrl = $("#imgUrl").val();
var price = $("#price").val();
var stock = $("#stock").val();
if (title == null || title == ""){
alert("商品名称不能为空");
return false;
}
if (description == null || description == ""){
alert("商品描述不能为空");
return false;
}
if (imgUrl == null || imgUrl == ""){
alert("图片URL不能为空");
return false;
}
if (price == null || price == ""){
alert("价格不能为空");
return false;
}
if (stock == null || stock == ""){
alert("库存不能为空");
return false;
}
//使用ajax方式发送异步请求完成业务,映射到后端@RequestMapping(value = "/createMerchandise",method = {RequestMethod.GET},consumes = {CONTENT_TYPE_FORMED})
$.ajax({
type:"GET",
contentType:"application/x-www-form-urlencoded",
url:"http://localhost:8090/merchandise/createMerchandise", //至此完成了ajax请求头,接下来解决传参
//ajax内部的传参
data:{
"title":title,
"description":description,
"imgUrl":imgUrl,
"price":price,
"stock":stock
},
//允许跨域授信请求,以使其session变成跨域可授信
xhrFields:{withCredentials:true},
//定义两个回调,分别是ajax请求成功和ajax请求失败
//只要被服务端正确的处理,必定会进success block
success:function (data) {
if (data.status == "success"){ //说明我们的业务逻辑请求处理成功了
alert("创建成功");
}else{
alert("创建失败,原因为" + data.data.errMsg);
}
},
//当http请求因为网络原因或者异常原因没有被正确处理,网络的请求会进入error的block
error:function (data) {
alert("创建失败,原因为" + data.responseText);
}
});
return false;
});
});
</script>
</html>
出现问题:merchandise 表中 id 的值与 merchandise_stock 表中 merchandise_id 的值不一样。
解决方法:修改MerchandiseServiceImpl.java
商品详情页浏览
在MerchandiseController类中添加通过商品id获取商品信息的controller
//商品详情页浏览
@RequestMapping(value = "/get",method = {RequestMethod.GET},consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
@ApiOperation(value = "通过商品id获取商品信息")
@ApiImplicitParam(name = "id", value = "商品id",required = true)
public CommonReturnType getMerchandise(@RequestParam(name = "id") Integer id){
MerchandiseModel merchandiseModel = merchandiseService.getMerchandiseById(id);
MerchandiseVO merchandiseVO = convertVOFromModel(merchandiseModel);
return CommonReturnType.create(merchandiseVO);
}
解决方法:删除consumes = {CONTENT_TYPE_FORMED}
商品列表浏览
在MerchandiseDOMapper.xml中添加语句
<select id="listMerchandise" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from merchandise order by sales DESC;/* 通过销量倒序排序 */
</select>
在MerchandiseDOMapper.java中添加方法
List<MerchandiseDO> listMerchandise();
在MerchandiseServiceImpl.java中 listMerchandise方法
@Override
public List<MerchandiseModel> listMerchandise() {
List<MerchandiseDO> merchandiseDOList = merchandiseDOMapper.listMerchandise();
List<MerchandiseModel> merchandiseModelList = merchandiseDOList.stream().map(merchandiseDO -> {
MerchandiseStockDO merchandiseStockDO = merchandiseStockDOMapper.selectByMerchandiseId(merchandiseDO.getId());
MerchandiseModel merchandiseModel = this.convertModelFromDataboject(merchandiseDO,merchandiseStockDO);
return merchandiseModel;
}).collect(Collectors.toList());
return merchandiseModelList;
}
在MerchandiseController类中添加商品列表页面浏览的controller
//商品列表页面浏览
@RequestMapping(value = "/list",method = {RequestMethod.GET})
@ResponseBody
@ApiOperation(value = "商品列表页面浏览")
public CommonReturnType listMerchandise(){
//通过调用merchandiseService的listMerchandise方法,拿到merchandiseModelList
List<MerchandiseModel> merchandiseModelList = merchandiseService.listMerchandise();
//使用stream API将list内的merchandiseModel转化为merchandiseVO
List<MerchandiseVO> merchandiseVOList = merchandiseModelList.stream().map(merchandiseModel -> {
MerchandiseVO merchandiseVO = this.convertVOFromModel(merchandiseModel);
return merchandiseVO;
}).collect(Collectors.toList());
return CommonReturnType.create(merchandiseVOList);
}
手动修改数据库中sales的值
主要参考: