新增收货地址
1 新增收货地址-数据表创建
CREATE TABLE t_address ( aid INT AUTO_INCREMENT COMMENT '收货地址id', uid INT COMMENT '归属的用户id', name VARCHAR(20) COMMENT '收货人姓名', province_name VARCHAR(15) COMMENT '省-名称', province_code CHAR(6) COMMENT '省-行政代号', city_name VARCHAR(15) COMMENT '市-名称', city_code CHAR(6) COMMENT '市-行政代号', area_name VARCHAR(15) COMMENT '区-名称', area_code CHAR(6) COMMENT '区-行政代号', zip CHAR(6) COMMENT '邮政编码', address VARCHAR(50) COMMENT '详细地址', phone VARCHAR(20) COMMENT '手机', tel VARCHAR(20) COMMENT '固话', tag VARCHAR(6) COMMENT '标签', is_default INT COMMENT '是否默认:0-不默认,1-默认', created_user VARCHAR(20) COMMENT '创建人', created_time DATETIME COMMENT '创建时间', modified_user VARCHAR(20) COMMENT '修改人', modified_time DATETIME COMMENT '修改时间', PRIMARY KEY (aid) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
t_address.sql
2 新增收货地址-创建实体类
创建一个类Address ,在类中对应表中的相关字段,采用驼峰命名方式。最后再去继承BaseEntity 类
**entity | Address **
package com.cy.store.entity; import java.io.Serializable; import java.util.Objects; /** 收货地址数据的实体类 */ public class Address extends BaseEntity implements Serializable { private Integer aid; private Integer uid; private String name; private String provinceName; private String provinceCode; private String cityName; private String cityCode; private String areaName; private String areaCode; private String zip; private String address; private String phone; private String tel; private String tag; private Integer isDefault; //contructor get set equals hashcode toString //ALT+INSert //.......... }
3 新增收货地址-持久层
3.1 各功能的开发顺序
当前的收货地址功能模块:列表的展示、修改、删除、设置默认、新增收货地址。
开发顺序:新增收货地址-列表展示-设置默认收货地址-删除-修改收货地址
3.2 规划需要执行的SQL语句
1.对应的是插入语句:
insert into t_address(除了adi外字段列表) values(字段值列表)
2.一个用户的收货地址规定最多只能有20条数据对应。
在插入用户数据之前先做查询操作。
收货地址逻辑控制方面的一个异常。超过20–异常
select count(*) from t_address where uid=?
3.3 接口与抽象方法
1.创建一个新的接口AddressMapper,在这个接口中来定义两个SQL语句抽象方法。
package com.cy.store.mapper; import com.cy.store.entity.Address; /** 收货地址持久层的接口*/ public interface AddressMapper { /** * 插入用户的收货地址数据 * @param address 收货地址数据 * @return 影响的行数 */ Integer insert(Address address); /** * 根据用户的id统计收货地址数量 * @param uid 用户的id * @return 当前用户的收货地址总数 */ Integer countByUid(Integer uid); }
3.4 配置SQL映射
创建一个AddressMapper.xml映射文件,在这个文件中添加抽象方法的映射SQL语句。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cy.store.mapper.AddressMapper"> <resultMap id="AddressEntityMap" type="com.cy.store.entity.Address"> <id column="aid" property="aid"/> <result column="province_code" property="provinceCode"/> <result column="province_name" property="provinceName"/> <result column="city_code" property="cityCode"/> <result column="city_name" property="cityName"/> <result column="area_code" property="areaCode"/> <result column="area_name" property="areaName"/> <result column="is_default" property="isDefault"/> <result column="created_user" property="createdUser"/> <result column="created_time" property="createdTime"/> <result column="modified_user" property="modifiedUser"/> <result column="modified_time" property="modifiedTime"/> </resultMap> <insert id="insert" useGeneratedKeys="true" keyProperty="aid"> INSERT INTO t_address ( uid, name, province_name, province_code, city_name, city_code, area_name, area_code, zip, address, phone, tel, tag, is_default, created_user, created_time, modified_user, modified_time )VALUES ( #{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode}, #{areaName}, #{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag}, #{isDefault}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime} ) </insert> <select id="countByUid" resultType="java.lang.Integer"> SELECT count(*) from t_address WHERE uid=#{uid} </select> </mapper>
AddressMapper
测试
2.在test下mapper下AddressMapperTests
package com.cy.store.mapper; import com.cy.store.entity.Address; import com.cy.store.entity.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Date; //@SpringBootTest:表示标注当前的类是测试类,不会随同项目一块打包 @SpringBootTest //@RunWith:表示启动这个单元测试类(单元测试类是不能够运行的),需要传递一个参数,必须是SpringRunner的实例类型 //@RunWith(SpringRunner.class) public class AddressMapperTests { @Autowired private AddressMapper addressMapper; @Test public void insert(){ Address address=new Address(); address.setUid(8); address.setPhone("17858802974"); address.setName("女朋友"); addressMapper.insert(address); } @Test public void countByUid(){ Integer count = addressMapper.countByUid(8); System.out.println(count); } }
AddressMapperTests–新增收货地址
4 新增收货地址-业务层
4.1 规划异常
如果用户是第一次插入用户的收货地址,规则:当用户插入的地址是第一条时,需要将第一条地址作为默认收货地址,如果查询的统计总数为0,则将当前的地址的is_default值设置为1。查询统计的结果为0不代表异常。查询到的结果大于20,这时候需要抛出业务控制的异常AddressCountLimitException异常。自行创建这个异常。
package com.cy.store.service.ex; /* 收货地址数量达到上限的异常 */ public class AddressCountLimitException extends ServiceException{ public AddressCountLimitException() { super(); } public AddressCountLimitException(String message) { super(message); } public AddressCountLimitException(String message, Throwable cause) { super(message, cause); } public AddressCountLimitException(Throwable cause) { super(cause); } protected AddressCountLimitException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
插入数据时产生的异常InsertException,不再需要重复创建。
4.2 接口与抽象方法
1.创建一个AddressService接口,在其中第定义业务的抽象方法
package com.cy.store.service; import com.cy.store.entity.Address; /**收货地址业务层的接口*/ public interface IAddressService { void addNewAddress(Integer uid,String username,Address address); }
2.创建一个AddressServiceImpl实现类,去实现接口的抽象方法。
在配置文件中定义数据。
# Spring 读取配置文件的数据:@Value(${user.address.max-count}) user.address.max-count=20
测试
3.测试业务层的过程是否正常。AddressServiceTests测试业务功能。
package com.cy.store.service; import com.cy.store.entity.Address; import com.cy.store.entity.User; import com.cy.store.service.ex.ServiceException; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; //@SpringBootTest:表示标注当前的类是测试类,不会随同项目一块打包 @SpringBootTest //@RunWith:表示启动这个单元测试类(单元测试类是不能够运行的),需要传递一个参数,必须是SpringRunner的实例类型 //@RunWith(SpringRunner.class) public class AddressServiceTests { @Autowired private IAddressService addressService; @Test public void addNewAddress(){ Address address=new Address(); address.setUid(8); address.setPhone("17858809999"); address.setName("女朋友"); addressService.addNewAddress(8,"管理员",address); } }
AddressServiceTests–addNewAddress
5 新增收货地址-控制器
5.1 处理异常
业务层抛出了收货地址总数超标的异常,在BaseController中进行处理。
package com.cy.store.controller; import com.cy.store.controller.ex.*; import com.cy.store.service.ex.*; import com.cy.store.util.JsonResult; import org.springframework.web.bind.annotation.ExceptionHandler; import javax.servlet.http.HttpSession; /** * 控制层类的基类 */ public class BaseController { /**操作成功的状态码 */ public static final int OK=200; //请求处理方法,这个方法的返回值就是需要传递给前端的数据 //自动将异常对象传递给此方法的参数列表上 //当前项目中产生的异常会被拦截到此方法中,这个方法此时充当的是请求处理方法,方法的返回值直接给前端 @ExceptionHandler({ServiceException.class,FileUploadException.class})//用于统一处理抛出的异常 public JsonResult<Void> handleException(Throwable e){ JsonResult<Void> result=new JsonResult<>(e); if (e instanceof UsernameDuplicatedException){ result.setState(4000); result.setMessage("用户名已经被占用的异常"); }else if (e instanceof UserNotFoundException){ result.setState(4001); result.setMessage("用户数据不存在的异常"); }else if (e instanceof PasswordNotMatchException){ result.setState(4002); result.setMessage("用户密码错误的异常"); }else if (e instanceof AddressCountLimitException){ result.setState(4003); result.setMessage("用户的收货地址超出上限异常"); } else if (e instanceof InsertException){ result.setState(5000); result.setMessage("插入数据时产生未知的异常"); }else if (e instanceof UpdateException){ result.setState(5001); result.setMessage("更新数据时产生未知的异常"); }else if (e instanceof FileEmptyException) { result.setState(6000); } else if (e instanceof FileSizeException) { result.setState(6001); } else if (e instanceof FileTypeException) { result.setState(6002); } else if (e instanceof FileStateException) { result.setState(6003); } else if (e instanceof FileUploadIOException) { result.setState(6004); } return result; } /** * 获取session对象的uid * @param session session对象 * @return 当前登录得到用户uid的值 */ protected final Integer getuidFromSession(HttpSession session){ return Integer.valueOf(session.getAttribute("uid").toString()); } /** * 获取session对象的username * @param session session对象 * @return 当前登录得到用户username的值 * * 在实现类中重写了父类中的toString()方法,不是句柄信息的输出@xxx */ protected final String getUsernameFromSession(HttpSession session){ return session.getAttribute("username").toString(); } }
BaseController–AddressCountLimitException
5.2 设计请求
/address/add_new_address post Address address,HttpSession session JsonResult<Void>
5.3 处理请求
在控制层创建AddressController来处理用户收货地址的请求和响应。
package com.cy.store.controller; import com.cy.store.entity.Address; import com.cy.store.service.IAddressService; import com.cy.store.util.JsonResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; @RequestMapping("address") @RestController public class AddressController extends BaseController{ @Autowired private IAddressService addressService; @RequestMapping("add_new_address") public JsonResult<Void> addNewAddress(Address address, HttpSession session) { Integer uid =getuidFromSession(session); String username=getUsernameFromSession(session); addressService.addNewAddress(uid,username,address); return new JsonResult<>(OK); } }
AddressController–addNewAddress
测试
先登录用户,再进行测试
http://localhost:8080/address/add_new_address?name=tom&phone=17858802222
6 新增收货地址-前端页面
addAddress.html
<script type="text/javascript"> $("#btn-add-new-address").click(function() { $.ajax({ url: "/addresses/add_new_address", type: "POST", data: $("#form-add-new-address").serialize(), dataType: "JSON", success: function(json) { if (json.state == 200) { alert("新增收货地址成功!"); location.href = "address.html"; } else { alert("新增收货地址失败!" + json.message); } }, error: function(xhr) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status); location.href = "login.html"; } }); }); </script>
addAddress–add_new_address
测试
README–新增收货地址