Store网上商城项目
用户注册
1 创建数据表
2 创建用户的实体类
3 注册-持久层
3.1 规划需要执行
1.用户的注册功能。相当于在做数据的插入操作
insert into t_user (username,password) values (值列表)
2.在用户注册时要首先去查询当前的用户名是否存在,如果存在则不能进行注册。相当于是一条查询语句
select * from t_user where username=?
3.2 设计接口和抽象方法
定义Mapper接口。在项目的目录下创建mapper包,在这个包下根据不同的功能模块来创建mapper接口。创建一个UserMapper的接口。要在接口中定义这两个抽象方法。
public interface UserMapper {
/**
* 插入用户的数据
* @param user 用户的数据
* @return 受影响的行数(增,删,改,都用受影响行数作为返回值)
*/
Integer insert(User user);
/**
* 根据用户名来查询用户的数据
* @param username 用户名
* @return 如果找到对应的用户则返回这个用户的数据,如果没有则返回null
*/
User findByUsername(String username);
}
3.3 编写映射
1.定义xml映射文件,与对应的接口进行关联。所有的映射文件需要放在resources目录下,在这个目录下创建一个mapper文件夹,然后在这个文件夹下存放Mapper映射文件
2.创建接口对应的映射文件,遵循和接口的名称保存一致即可。创建一个UserMapper.xml文件。
3.配置接口中的方法对应上SQL语句上。需要借助标签来完成,insert/update/delete/select,对应的是SQL语句的增删改查操作。
4.resultMap 定义匹配不同命名规则的属性,并把resultMap的id传入到标签的resultMap属性里面
<?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.UserMapper">
<resultMap id="UserEntityMap" type="com.cy.store.entity.User">
<id column="uid" property="uid"></id>
<result column="is_delete" property="isDelete"></result>
<result column="created_user" property="createdUser"></result>
<result column="created_time" property="createdTime"></result>
<result column="modified_user" property="modifiedUser"></result>
<result column="modified_time" property="modifiedTime"></result>
</resultMap>
<insert id="insert" useGeneratedKeys="true" keyProperty="uid">
INSERT INTO t_user(username, password, salt, phone, email, gender, avatar, isDelete,createdUser, createdTime, modifiedUser, modifiedTime)
VALUES (#{username}, #{password}, #{salt}, #{phone}, #{email}, #{gender}, #{avatar}, #{isDelete},#{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
</insert>
<select id="findByUsername" resultMap="UserEntityMap">
SELECT *
FROM t_user
WHERE username = #{username}
</select>
</mapper>
3.4 单元测试
每个独立的层编写完毕后都需要写单元测试方法,来测试当前功能。在test包结构下创建一个mapper包,在这个包下再创建持久层的功能测试。
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserMapperTests {
@Autowired
private UserMapper userMapper;
@Test
public void insert() {
User user = new User();
user.setUsername("tim");
user.setPassword("123");
Integer rows = userMapper.insert(user);
System.out.println(rows);
}
@Test
public void findByUsername(){
User user = userMapper.findByUsername("tim");
System.out.println(user);
}
}
4 注册-业务层
4.1 规划异常
1.RuntimeException异常:作为这类异常的子类,然后再去定义具体的异常类型来继承这个异常。业务层异常的基类,ServiceException异常。这个异常继承RuntimeException异常。异常机制的建立。
public class ServiceException extends RuntimeException{
public ServiceException() {
super();
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(Throwable cause) {
super(cause);
}
protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
根据业务层不同的功能来详细定义具体的异常的类型,统一的去继承ServiceException异常类。
2.业务异常:用户在进行注册时候可能会产生用户名被占用的错误,抛出一个异常:UsernameDepulitedException异常
public class UsernameDuplicatedException extends ServiceException{
public UsernameDuplicatedException() {
super();
}
public UsernameDuplicatedException(String message) {
super(message);
}
public UsernameDuplicatedException(String message, Throwable cause) {
super(message, cause);
}
public UsernameDuplicatedException(Throwable cause) {
super(cause);
}
protected UsernameDuplicatedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
3.其他异常:正在执行数据插入操作的时候,服务器宕机。处于正在执行插入的过程中所产生的异常InsertException
4.2 设计接口和抽象方法
1.在service包下创建一个IUserService接口。
public interface IUserService {
/**
* 用户注册方法
* @param user 用户的数据对象
*/
void reg(User user);
}
2.创建一个实现类UserServiceImpl类,需要实现这个接口,并且实现抽象方法
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public void reg(User user) {
String username = user.getUsername();
User result = userMapper.findByUsername(username);
if (result != null){
throw new UsernameDuplicatedException("用户名被占用");
}else {
String oldPassword =user.getPassword();
String salt = UUID.randomUUID().toString().toUpperCase();
String md5Password = getMD5Password(oldPassword, salt);
user.setPassword(md5Password);
user.setSalt(salt);
user.setIsDelete(0);
user.setCreatedUser(user.getUsername());
user.setModifiedUser(user.getUsername());
Date date = new Date();
user.setCreatedTime(date);
user.setModifiedTime(date);
Integer rows = userMapper.insert(user);
if(rows != 1){
throw new InsertException("在用户注册过程中产生了未知异常");
}
}
}
private String getMD5Password(String password,String salt){
for (int i = 0; i < 3; i++) {
password=DigestUtils.md5DigestAsHex((salt+password+salt).getBytes(StandardCharsets.UTF_8)).toUpperCase();
}
return password;
}
}
3.在单元测试包下创建一个UserServiceTests类,在这个类中添加单元测试的功能。
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTests {
@Autowired
private IUserService userService;
@Test
public void reg(){
try {
User user=new User();
user.setUsername("yuanxin03");
user.setPassword("123");
userService.reg(user);
System.out.println("ok");
}catch (ServiceException e){
System.out.println(e.getClass().getSimpleName());
System.out.println(e.getMessage());
}
}
}
5 注册-控制层
5.1 创建响应
状态码,状态描述信息,数据。这部分功能封装在一个类中,将这类作为方法返回值,返回给前端浏览器。
public class JsonResult<E> implements Serializable {
// 状态码
private Integer state;
// 描述信息
private String message;
// 数据
private E data;
}
5.2 设计请求
根据当前的业务功能模块,进行请求的设计
请求路径:/users/reg
请求参数:User user
请求类型:Post
请求结果:JsonResult<void>
5.3 处理请求
1.创建控制层对应的类UserController类,依赖于业务层的接口。
2.异常捕获
5.4 控制层优化设计
在控制层抽离父类,在这个父类中统一的去处理关于异常的相关操作。编写一个BaseController类,统一处理异常。
6 注册-前端页面
1.在register页面中编写发送请求的方法,点击事件来完成。选中对应的按钮(/((“选择器”)),再去添加点击的事件,/).ajax()函数发送异步请求。
2.JQuery封装了一个函数,称之为$ajax()函数,通过对象调用ajax()函数,可以异步加载相关的请求。依靠的是JavaScript提供的一个对象XHR(XmlHttpResponse),封装了这个对象。
3.ajax()的使用方式。需要传递一个方法体作为方法的参数来使用:
$.ajax({
url:"",
type:"",
data:"",
dataType:"",
success:function(){
},
error:function(){
}
});
4.ajax()函数参数的含义:
5.js代码可以独立在一个js的文件里或声明在一个script标签中
用户登录
1 登录-持久层
1.1 规划需要执行的SQL语句
依据用户提交的用户名和密码做select查询。
select * from t_user where username=?
1.2 接口设计和方法
2 登录-业务层
2.1 规划异常
1.用户名对应的密码错误,密码匹配失败的异常:PasswordNotMatchException,运行时异常,业务异常。
2.用户名没有找到:UsernameNotFoundException,运行时异常,业务异常。
3.异常的编写
2.2 设计业务层接口和抽象方法
1.直接在IUserService接口中编写抽象方法,login(String username,String password)。将当前登录成功的用户数据以当前用户对象的形式返回。状态管理:可以将数据保存在cookie或者session中,可以避免重复度很高的数据多次频繁操作数据进行获取(用户id-在session中,用户的头像-cookie中)。
2.在实现类中实现父接口的抽象方法
3.在测试类中测试业务层登录的方法是否可以执行通过。
4.如果一个类没有手动创建直接将这个类复制到项目,idea找不到这个类。解决:重新构建
2.3 抽象方法实现
3 登录-控制层
3.1 处理异常
业务层抛出的异常是什么,需要在统一异常处理类中进行统一的捕获和处理,如果业务层抛出的异常类型在已经在统一异常处理类中曾经处理过,则不需要重复处理
3.2 设计请求
请求路径:/users/login
请求参数:String username, String password
请求类型:Post
请求结果:JsonResult<User>
3.3 处理请求
在UserController类中编写处理请求的方法。
@RequestMapping("/login")
public JsonResult<User> login(String username,String password){
User login = userService.login(username, password);
return new JsonResult<>(OK,login);
}
4 登录-前端页面
1.在login.html页面中依据前面所设置的请求来发送ajax请求。
2.访问页面进行用户的登录操作。
用户会话session
session对象主要存在服务器端,可以用于保存服务器的临时数据的对象,所保存的数据可以在整个项目中都可以通过访问来获取,把session的数据看作一个共享的数据源。首次登陆的时候所获取的用户数据,转移到session对象即可。
session.getAttrbute(“key”)可以将获取session中的数据的这种行为进行封装,封装在BaseController类中。
-
封装session对象中数据的获取,数据的设置。(用户登录成功后,进行数据的设置,设置到全局的session对象)
-
在父类中封装两个数据:获取uid和获取username对应的两个方法。用户头像暂不考虑,封装在cookie中
-
在登录的方法中将数据封装在session对象中。服务器本身自动创建有session对象,已经是一个全局的session对象。SpringBoot直接使用session对象,直接将HttpSession类型的对象作为请求处理方法的参数,会自动将全局的session对象注入到请求处理方法的session形参上
@RequestMapping("/login") public JsonResult<User> login(String username, String password, HttpSession session){ User login = userService.login(username, password); session.setAttribute("uid",login.getUid()); session.setAttribute("username",login.getUsername()); System.out.println(getUidFromSession(session)); System.out.println(getUsernameFromSession(session)); return new JsonResult<>(OK,login); }
拦截器
拦截器:首先将所有的请求统一拦截到拦截器中,开发者可以在拦截器中定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发),推荐使用重定向。
在SpringBoot项目中实现拦截器的定义和使用。SpringBoot是依靠SpringMVC来完成的。SpringMVC提供了一个接口HandlerInterceptor接口,用于表示定义一个拦截器。首先,自定义一个类,再用这个类去实现这个接口。
1.自定义一个类,用这个类实现HandlerInterceptoer接口。
/**
* 定义一个拦截器
*/
public class LoginInterceptor implements HandlerInterceptor {
/**
* 检测全局session对象中是否有uid数据,如果有则放行,如果没有则重定向到登录页面
* @param request 请求对象
* @param response 响应对象
* @param handler 处理器
* @return 如果返回值为true表示放行当前的请求,如果返回值为false则表示拦截当前的请求
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object uid = request.getSession().getAttribute("uid");
if (uid==null){
response.sendRedirect("/web/login.html");
return false;
}
return true;
}
}
2.注册过滤器:添加白名单(哪些资源可以在不登录的情况下访问:login.html/register.html/login/reg/index.html/product.html)、添加黑名单(登录时才可以访问的页面资源)
3.注册过滤技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项目的config包结构下。
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
WebMvcConfigurer.super.addInterceptors(registry);
}
}
修改密码
需要用户提交原始密码和新密码,再根据当前登录的用户进行信息的修改操作。
1 修改密码-持久层
1.1 规划需要执行的SQL语句
根据用户的uid修改用户的password值。
update t_user set password=?,modified_user?=modified_time=?where uid=?
根据uid查询用户的数据。在修改密码之前,首先要保证当前这用户的数据存在(is_delete),检测是否被标记为删除、检测输入的原始密码是否正确。
select * from t_user where uid=?
1.2 UserMapper接口定义方法
1.3 UserMapper.xml完善SQL语句
1.4 单元测试
2 修改密码-业务层
2.1 规划可能产生的异常
1 用户输入的原密码错误 PasswordNotMatch异常
2 用户已删除
3 uid找不到用户
4 update更新时产生的未知的异常,UpadateException
2.2 设计接口和抽象方法
执行用户修改密码的核心方法
2.3 编写单元测试
3 修改密码-控制层
3.1 处理异常
UpdateException需要配置在统一的异常处理方法中。
3.2 设计请求
/users/changPassword
POST
String oldPassword,String newPassword
JsonResult<void>
3.3 处理请求
@RequestMapping("/change_password")
public JsonResult<Void> changePassword(String oldPassword,String newPassword,HttpSession session){
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
userService.changePassword(uid,username,oldPassword,newPassword);
return new JsonResult<>(OK);
}
4 修改密码-前端页面
个人资料
1.个人资料-持久层
1.1 需要规划的SQL语句
1.更新用户信息的SQL语句
update t_user set phone=?,email=?,gender=?,modified_user=?,modified_time=? where uid=?
2.根据用户名来查询用户的数据
select * from t_user where uid=?
1.2 接口与抽象方法
更新用户的信息方法的定义。
/**
* 更新用户的数据信息
* @param user 用户的数据
* @return 返回值为受影响的行数
*/
Integer updateInfoByUid(User user);
1.3 抽象方法的映射
UserMapper.xml文件
<update id="updateInfoByUid">
UPDATE t_user
SET <if test="phone!=null">phone=#{phone},</if>
<if test="email!=null">email=#{email},</if>
<if test="gender!=null">gender=#{gender},</if>
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
WHERE uid=#{uid}
</update>
1.4 测试
@Test
public void updateInfoByUid(){
User user=new User();
user.setUid(11);
user.setPhone("1232");
user.setEmail("98670@qq.com");
user.setGender(1);
Integer integer = userMapper.updateInfoByUid(user);
System.out.println(integer);
}
2.个人资料-业务层
2.1 异常规划
-
设计两个功能:
-
当打开页面时,获取用户的信息并填充到对应的文本框中。
-
检测用户是否点击了修改按钮,如果检测到则执行修改用户信息的操作。
-
-
打开页面的时候可能找不到用户数据,点击删除按钮之前需要再次的去检测用户的数据是否存在
2.2 接口和抽象方法
主要有两个功能的模块,对应的是两个抽象的方法的设计。
/**
* 根据用户的uid查询用户数据
* @param uid
* @return 用户的数据
*/
User getByUid(Integer uid);
/**
* 修改用户信息
* @param session
* @param user
*/
void changeInfo(Session session,User user);
2.3 实现抽象方法
在UserServiceImpl类中添加两个抽象方法的具体实现。
@Override
public User getByUid(Integer uid) {
User result = userMapper.findByUid(uid);
if (result==null||result.getIsDelete()==1){
throw new UsernameNotFoundException("用户数据不存在");
}else {
User user=new User();
user.setEmail(result.getEmail());
user.setPhone(result.getPhone());
user.setGender(result.getGender());
return user;
}
}
@Override
public void changeInfo(Integer uid,String username, User user) {
User result = userMapper.findByUid(uid);
if (result==null||result.getIsDelete()==1){
throw new UsernameNotFoundException("用户数据不存在");
}else {
user.setUid(uid);
user.setModifiedUser(username);
user.setModifiedTime(new Date());
Integer integer = userMapper.updateInfoByUid(user);
if (integer!=1){
throw new UpdateException("更新数据时产生未知异常");
}
}
}
2.4 接口测试
@Test
public void getByUid(){
System.out.println(userService.getByUid(11));
}
@Test
public void changeInfo(){
User user = new User();
user.setPhone("1234");
user.setEmail("56565@qq.com");
user.setGender(0);
userService.changeInfo(11,"denlu",user);
}
3.个人资料-控制层
3.1 处理异常
3.2 设计请求
1.设置-打开页面就发送当前用户的查询。
/users/get_by_uid
GET
HttpSession session
JsonResult<User>
2.点击修改按钮发送用户的数据修改操作请求的设计。
/users/change_info
POST
User user,HttpSession session
JsonResult<Void>
3.3 处理请求
4. 个人资料-前端页面
1.在打开userdata.html页面自动发送ajax请求,查询到的数据填充到这个页面。
2.在检测到用户点击了修改按钮之后发送了一个ajax请求(change_info)。
上传头像
1.上传头像-持久层
1.1 SQL语句的规划
将对象文件保存在操作系统上,然后再把这个文件路径给记录下来,因为在记录路径的时候是非常的便捷和方便的。将来如果要打开这个文件,可以根据路径去找到这个文件。所以数据库里只要保存文件存储路径即可。将保存的静态资源(图片,文件,其他资源文件)放在某台电脑上,再把这台电脑作为一台单独的服务器使用。
对应是一个用户avatar的sql语句
update t_user set avatar=?,modified_user=?,modified_time=? where uid=?
1.2 设计接口和抽象方法
UserMapper接口中来定义这个给抽象方法用于修改用户头像
1.3 接口的映射
UserMapper.xml文件中编写映射的SQL语句。
2.上传头像-业务层
2.1 规划异常
1.用户数据不存在,找不到对应的用户的数据。
2.更新的时候,各种未知的异常产生。
2.2 设计接口和抽象方法
/**
* 根据uid修改头像
* @param uid 用户的id
* @param avatar 用户的头像路径
* @param username 用户名
*/
void changeAvatar(Integer uid,String avatar,String username);
2.3 实现抽象方法
@Override
public void changeAvatar(Integer uid, String avatar, String username) {
User user = userMapper.findByUid(uid);
if(user==null||user.getIsDelete().equals(1)){
throw new UsernameNotFoundException("用户数据不存在");
}
Integer integer = userMapper.updateAvatarByUid(uid, avatar, username, new Date());
if(integer!=1){
throw new UpdateException("更新用户头像产生未知的异常");
}
}
2.4 测试
3.上传头像-控制层
3.1 规划异常
父类:FileUploadException 泛指文件上传的异常
FileEmptyException 文件为空的异常
FileSizeException 文件大小超出限制
FileTypeException 文件类型异常
FileUploadIOException 文件读写的异常
FileStateException 文件状态异常
五个构造方法显式声明出来,再去继承相关的父类
3.2 异常处理
在基类BaseController类中进行编写和统一处理。
if (e instanceof FileEmptyException){
result.setState(6000);
result.setMessage("文件空异常");
}else if (e instanceof FileSizeException){
result.setState(6001);
result.setMessage("文件大小异常");
}else if (e instanceof FileTypeException){
result.setState(6002);
result.setMessage("文件类型异常");
}else if (e instanceof FileStateException){
result.setState(6003);
result.setMessage("文件状态异常");
}else if (e instanceof FileUploadException){
result.setState(6004);
result.setMessage("文件上传异常");
}
3.3 设计请求
/users/change_avatar
POST(GET请求提交数据2KB)
HttpSession session,MutipartFile file
JsonResult<String>
3.4 实现请求
/**
* MultipartFile接口是SpringMVC提供的一个接口,这个接口为我们包装了获取文件类型的数据(任何类型的file都可以接收),SpringBoot整合了SpringMVC,只需要在处理请求的方法参数列表上声明一个参数类型的MutipartFile的参数,然后SpringBoot自动将传递给服务的文件数据赋值给这个参数
* @param session
* @param file
* @return
*/
@RequestMapping("change_avatar")
public JsonResult<String> changeAvatar(HttpSession session, @RequestParam("file") MultipartFile file) {
if (file.isEmpty()){
throw new FileEmptyException("文件为空");
}
if (file.getSize()>AVATAR_MAX_SIZE){
throw new FileSizeException("文件超出限制");
}
if (!AVATAR_TYPE.contains(file.getContentType())){
throw new FileTypeException("文件类型不支持");
}
String parent=session.getServletContext().getRealPath("upload");
File dir = new File(parent);
if (!dir.exists()){
dir.mkdirs();//创建当前的目录
}
String originalFilename = file.getOriginalFilename();
int index = originalFilename.lastIndexOf(".");
String suffix=originalFilename.substring(index);//获取后缀(.XXX)
String filename = UUID.randomUUID().toString().toUpperCase()+suffix;//拼接随机生成的文件名和前面获取到的字符串
File dest = new File(dir, filename);//在存储路径下写入同名空文件,传回空文件
try {
file.transferTo(dest);//头像文件写入空文件中
} catch (FileStateException e) {
throw new FileStateException("文件状态异常");
} catch (IOException e) {
throw new FileUploadIOException("文件读写异常");
}
Integer uidFromSession = getUidFromSession(session);
String usernameFromSession = getUsernameFromSession(session);
String avatar="/upload/"+filename;
userService.changeAvatar(uidFromSession,avatar,usernameFromSession);
return new JsonResult<>(OK,avatar);
}
4.上传头像-前端页面
在upload页面中编写上传头像代码
说明:如果直接使用表单进行文件的上传,需要给表单显示的添加一个属性enctype=”multipart/form-data”声明出来,不会将目标文件的数据结构做修改再上传,不同于字符串
5.解决Bug
5.1 更改默认大小的限制
SpringMVC默认为1MB文件可以进行上传,需要手动修改SpringMVC默认上传文件的大小。
方式1:直接在配置文件中进行配置
spring.servlet.multipart.max-file-size=10MB
方式2:采用Java代码的形式来设置文件的上传大小的限制。主类中进行配置,可以定义一个方法,必须使用@Bean修,返回值为MutipartConfigElement。在类的前面添加一个@Configuration进行修饰
@Bean
MultipartConfigElement getMultipartConfigElement(){
MultipartConfigFactory multipartConfigFactory = new MultipartConfigFactory();
multipartConfigFactory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));
return multipartConfigFactory.createMultipartConfig();
}
5.2 显示头像
在页面中通过ajax请求来t提交文件,提交完成后,后端返回json串,从中解析出data中的数据,设置到img头像标签的src属性上
serialize()//将表单数据自动拼接成key=value的结构进行提交给服务器,一般提交是普通的控件类型中的数据(text/password/radio/checkbox)等等
FormdData类//将表单中数据保持原有结构进行数据的提交
--- new FormData($("#form")[0]);
ajax默认处理数据时按照字符串的形式进行处理,以及默认会采用字符串的形式进行提交数据。所以要关闭这两个默认的功能。
--- processData:false,
--- contentType:false,
5.3 登录后显示头像
更新头像成功后,将服务器返回的头像路径保存在客户端cookies对象中,然后每次检查用户打开上传头像页面,这个页面中通过ready()方法来自动检测去读取cookie中头像并设到src属性上。
1.设置cookie中的值
2.在upload页面中引入cookie.js文件
3.在upload.html页面中通过ready()自动读取cookie中的数据。
新增收货地址
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;
2 新增收货地址-创建实体类
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;
3 新增收货地址-持久层
3.1 各功能的开发顺序
当前收货地址功能模块:列表的展示,修改,删除,设置默认,新增收货地址。
开发顺序:新增-展示-设置默认-删除-修改
3.2 规划需要执行的SQL语句
1.新增-对应的是插入语句
insert into t_address(除了aid外字段列表) values(字段值列表)
2.一个用户的收货地址最多只能有20条数据对应。在插入用户数据前,先做查询操作。收货地址逻辑控制方面的一个异常。
select count(*) t_address where uid=?
3.3 接口和抽象方法
1.创建一个新的接口Address,在这个接口中来定义上面两个SQL语句抽象方法的定义。
/**
* 插入用户的收货地址数据
* @param address 收货地址数据
* @return 受影响的行数
*/
Integer insert(Address address);
/**
* 根据用户的uid统计用户记录的收货地址数量
* @param uid 用户的uid
* @return 当前用户的收货地址总数
*/
Integer countByUid(Integer uid);
3.4 配置SQL映射
resource下的AddressMapper.xml
<?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>
<result column="created_time" property="createdTime"></result>
<result column="modified_user" property="modifiedUser"></result>
<result column="modified_time" property="modifiedTime"></result>
</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, isDefault,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>
2.在test下的mapper文件夹下创建AddressMapperTests的测试类
4 新增收货地址-业务层
4.1 规划异常
如果用户是第一插入用户的收货地址,规则:当用户插入的地址是第一条,需要将当前地址作为默认收货地址,如果查询到统计总数为0,则将当前地址的is_default值设置为1。查询统计的结果为0不代表异常。查询结果大于20,这时需要抛出业务控制的异常AddressCountLimitException异常。自行创建这个异常。
4.2 接口和抽象方法
1.创建一个IAddressService接口,在接口中定义业务的抽象方法
4.3 实现抽象方法
5 新增收货地址-控制层
原创文章,作者:1402239773,如若转载,请注明出处:https://blog.ytso.com/275934.html