- 外星人商城项目介绍
- 项目背景
- 项目功能
- 项目技术
- 项目模块
- 项目要求
- 外星人商城项目开发流程
- 第一节 基础构建
- 第一步,连接数据库,单元测试
- 第二步,访问静态资源,单元测试
- 第二节 用户注册
- 1.创建用户的实体类
- 2.注册-持久层
- 3.UserMapper的单元测试
- 4.编写映射
- 5.配置properties文件
- 6.主程序
- 7.注册-业务层
- 异常处理
- 业务处理
- 单元测试-UserService
- 8.注册-控制层
- 响应结果类
- 请求的设计
- 控制层优化设计
- 9.注册-前端页面
- Ajax
- 10.总结
- 为什么实现Serializable接口?
- instanceof
- 第三节 用户登录
- 第四节 用户管理
- 第五节 热销商品
- 第六节 购物车
- 第七节 订单
- 第八节 商品秒杀
- 外星人商城项目总结
外星人公司(狗头)委托我开发一个一个专门的外星人商城(模仿京东、天猫),出售外星人电子产品以及周边,实现了以下功能。
本项目已经搭载了服务器,网址给定:
链接: 外星人官方网站.
- 登录
- 注册
- 用户管理
- 热销商品
- 购物车
- 订单
- 商品秒杀
- 项目框架:springboot
- 数据库框架:mybaits
- 前端技术:JS、JQuery、Ajax
持久层:依据业务要求规划相关的SQL语句,以及进行配置
业务层:核心功能控制、业务操作以及异常处理
控制层:接受请求,处理响应
前端开发:JS、JQuery、Ajax
单元测试:junit
- JDK8
- maven3.6.1
- 数据库mysql5.1
- idea
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseEntity implements Serializable {
private String createdUser;
private Date createdTime;
private String modifiedUser;
private Date modifiedTime;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends BaseEntity implements Serializable {
private Integer uid;
private String username;
private String password;
private String salt;
private String phone;
private String email;
private Integer gender;
private String avatar;
private Integer isDelete;
}
2.注册-持久层
通过Mybatis来操作数据库。
规划功能需要的SQL语句
用户的注册就是插入操作
insert into user () values ()
同时,在创建前需要查询用户是否存在,如果不存在,才能执行操作;
创建一个Mapper包,作为用户持久层的接口
public interface UserMapper {
Integer insert(User user);
User findByUsername(String username);
Integer updatePasswordByUid(
@Param("uid") Integer uid,
@Param("password") String password,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime);
User findByUid(Integer uid);
Integer updateInfoByUid(User user);
Integer updateAvatarByUid(
@Param("uid") Integer uid,
@Param("avatar") String avatar,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime);
}
3.UserMapper的单元测试
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void getUserMapper() {
User user = new User();
user.setUsername("12");
user.setPassword("123456");
Integer rows = userMapper.insert(user);
System.out.println(rows);
User user1 = (User) userMapper.findByUid(rows);
System.out.println(user1);
}
}
4.编写映射
-
定义xml文件,与对应的接口关联。所有的映射文件需要放在resources文件下
-
创建一个mapper包存放xml文件
5.配置properties文件INSERT INTO compution.t_user (username, password, salt, phone, email, gender, avatar, is_delete, created_user, created_time, modified_user, modified_time) VALUES (#{username}, #{password}, #{salt}, #{phone}, #{email}, #{gender}, #{avatar}, #{isDelete}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
需要将xml文件的位置告知给spring,即配置给properties
mybatis.mapper-locations=classpath:mapper
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);
}
}
注册的过程中,用户名可能被占用
public class UsernameDuplicateException extends ServiceException {
public UsernameDuplicateException() {
super();
}
public UsernameDuplicateException(String message) {
super(message);
}
public UsernameDuplicateException(String message, Throwable cause) {
super(message, cause);
}
public UsernameDuplicateException(Throwable cause) {
super(cause);
}
protected UsernameDuplicateException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
业务处理
public interface IUserService {
void reg(User user);
User login(String username, String password);
public void changePassword(Integer uid, String username, String oldPassword, String newPassword);
User getByUid(Integer uid);
void changeInfo(Integer uid, String username, User user);
void changeAvatar(Integer uid, String username, String avatar);
}
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public void reg(User user) {
// 根据参数user对象获取注册的用户名
String username = user.getUsername();
// 调用持久层的User findByUsername(String username)方法,根据用户名查询用户数据
User result = userMapper.findByUsername(username);
// 判断查询结果是否不为null
if (result != null) {
// 是:表示用户名已被占用,则抛出UsernameDuplicateException异常
throw new UsernameDuplicateException("尝试注册的用户名[" + username + "]已经被占用");
}
// 创建当前时间对象
Date now = new Date();
// 补全数据:加密后的密码
String salt = UUID.randomUUID().toString().toUpperCase();
String md5Password = getMd5Password(user.getPassword(), salt);
user.setPassword(md5Password);
// 补全数据:盐值
user.setSalt(salt);
// 补全数据:isDelete(0)
user.setIsDelete(0);
// 补全数据:4项日志属性
user.setCreatedUser(username);
user.setCreatedTime(now);
user.setModifiedUser(username);
user.setModifiedTime(now);
// 表示用户名没有被占用,则允许注册
// 调用持久层Integer insert(User user)方法,执行注册并获取返回值(受影响的行数)
Integer rows = userMapper.insert(user);
// 判断受影响的行数是否不为1
if (rows != 1) {
// 是:插入数据时出现某种错误,则抛出InsertException异常
throw new InsertException("添加用户数据出现未知错误,请联系系统管理员");
}
}
}
单元测试-UserService
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserMapper userMapper;
@Autowired
private UserServiceImpl userService;
@Test
void contextLoads() {
User user = new User();
user.setUsername("TestService");
user.setPassword("123456");
userService.reg(user);
System.out.println(userMapper.findByUsername("TestService"));
}
}
结果如下:
User(uid=4, username=TestService, password=25557681058F70D960A1CC4D7DC969B0, salt=AD9581A7-9E86-464D-9F40-401FC371333F, phone=null, email=null, gender=null, avatar=null, isDelete=0)8.注册-控制层 响应结果类
public class JsonResult请求的设计implements Serializable { private Integer state; private String message; private E data; }
依据当前的功能进行请求的设计
- 请求的路径:/users/reg
- 请求的参数:User
- 请求的类型:post
- 响应的结果:JsonResult
@RestController
@RequestMapping("users")
public class UserController extends BaseController {
@Autowired
private IUserService userService;
@RequestMapping("reg")
public JsonResult reg(User user) {
// 创建返回值
JsonResult result = new JsonResult();
try {
// 调用业务对象执行注册
userService.reg(user);
// 响应成功
result.setState(200);
} catch (UsernameDuplicateException e) {
// 用户名被占用
result.setState(4000);
result.setMessage("用户名已经被占用");
} catch (InsertException e) {
// 插入数据异常
result.setState(5000);
result.setMessage("注册失败,请联系系统管理员");
}
return new JsonResult(OK);
}
}
控制层优化设计
在控制层抽离一个父类,在这个父类中统一的去处理关于异常的操作
@RequestMapping("reg")
public JsonResult reg(User user) {
// 调用业务对象执行注册
userService.reg(user);
// 返回
return new JsonResult(OK);
}
public class BaseController {
public static final int OK = 200;
protected final Integer getUidFromSession(HttpSession session) {
return Integer.valueOf(session.getAttribute("uid").toString());
}
protected final String getUsernameFromSession(HttpSession session) {
return session.getAttribute("username").toString();
}
@ExceptionHandler({ServiceException.class, FileUploadException.class})
public JsonResult handleException(Throwable e) {
JsonResult result = new JsonResult(e);
if (e instanceof UsernameDuplicateException) {
result.setState(4000);
} else if (e instanceof UserNotFoundException) {
result.setState(4001);
} else if (e instanceof PasswordNotMatchException) {
result.setState(4002);
} else if (e instanceof AddressCountLimitException) {
result.setState(4003);
} else if (e instanceof AddressNotFoundException) {
result.setState(4004);
} else if (e instanceof AccessDeniedException) {
result.setState(4005);
} else if (e instanceof ProductNotFoundException) {
result.setState(4006);
} else if (e instanceof CartNotFoundException) {
result.setState(4007);
} else if (e instanceof InsertException) {
result.setState(5000);
} else if (e instanceof UpdateException) {
result.setState(5001);
} else if (e instanceof DeleteException) {
result.setState(5002);
} 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;
}
}
9.注册-前端页面
Ajax
- 在reg页面中编写发送请求的方法,点击事件来完成。
- jquery中封装了一个函数,称之为.ajax函数,可以异步加载相关的请求。
核心是依靠jsp提供的一个对象xmlhttpResponse - ajax使用方式,需要一个方法体作为方法的参数来使用
$.ajax({
url:"",
type:"",
data:"",
dataType:"",
success: function() {},
error: function() {}
});
- js代码可以独立声明在一个js的文件中
10.总结 为什么实现Serializable接口?
- Serializable是http://java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。
- Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。
- 实现了Serializable接口的类可以被ObjectOutputStream转换为字节流,同时也可以通过ObjectInputStream再将其解析为对象。
- 例如,我们可以将序列化对象写入文件后,再次从文件中读取它并反序列化成对象,也就是说,可以使用表示对象及其数据的类型信息和字节在内存中重新创建对象。
而这一点对于面向对象的编程语言来说是非常重要的,因为无论什么编程语言,其底层涉及IO操作的部分还是由操作系统其帮其完成的,而底层IO操作都是以字节流的方式进行的,所以写操作都涉及将编程语言数据类型转换为字节流,而读操作则又涉及将字节流转化为编程语言类型的特定数据类型。
而Java作为一门面向对象的编程语言,对象作为其主要数据的类型载体,为了完成对象数据的读写操作,也就需要一种方式来让JVM知道在进行IO操作时如何将对象数据转换为字节流,以及如何将字节流数据转换为特定的对象,而Serializable接口就承担了这样一个角色。
instanceof-
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
-
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
@Test
public void main() {
Object testObject = new ArrayList();
displayObjectClass(testObject);
}
public void displayObjectClass(Object o) {
if (o instanceof Vector)
System.out.println("对象是 java.util.Vector 类的实例");
else if (o instanceof ArrayList)
System.out.println("对象是 java.util.ArrayList 类的实例");
else
System.out.println("对象是 " + o.getClass() + " 类的实例");
}
第三节 用户登录
第四节 用户管理
第五节 热销商品
第六节 购物车
第七节 订单
第八节 商品秒杀
外星人商城项目总结


