电商项目实战--用户相关

1. 项目分析

首先应该分析该项目中需要处理哪些种类的数据,在本项目中有:商品、商品类别、用户、收货地址、购物车、收藏、订单……

然后,确定以上这些数据的开发顺序,原则上应该先开发基础数据和简单的数据相关的功能,所以,以上数据的开发顺序应该是:用户 > 收货地址 > 商品类别 > 商品 > 收藏 > 购物车 > 订单。

接下来,分析第1种数据的相关功能,即“用户”数据的管理中,有哪些功能需要开发:注册,登录,修改密码,个人资料,上传头像。

分析完成后,再确定以上功能的开发顺序,通常开发顺序的基本原则是:增、查、删、改,所以,以上功能的开发顺序应该是:注册 > 登录 > 修改密码 > 个人资料 > 上传头像。

在具体的开发某个功能时,应该遵循的顺序是:创建数据表 > 创建实体类 > 持久层 > 业务层 > 控制器层 > 前端界面。

2. 用户-创建数据表

首先应该创建数据库:

CREATE DATABASE tedu_store;

然后使用该数据库:

USE tedu_store

再创建数据表:

CREATE TABLE t_user (
	uid INT AUTO_INCREMENT COMMENT '用户id',
	username VARCHAR(50) UNIQUE NOT NULL COMMENT '用户名',
	password CHAR(32) NOT NULL COMMENT '密码',
	salt CHAR(36) COMMENT '盐值',
	gender INT COMMENT '性别,0-女,1-男',
	phone VARCHAR(20) COMMENT '手机号码',
	email VARCHAR(50) COMMENT '电子邮箱',
	avatar VARCHAR(100) COMMENT '头像',
	is_delete INT COMMENT '是否删除,0-未删除,1-已删除',
	created_user VARCHAR(50) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(50) COMMENT '最后修改人',
	modified_time DATETIME COMMENT '最后修改时间',
	PRIMARY KEY (uid)
) DEFAULT CHARSET=utf8;

3. 用户-创建实体类

先下载共享的项目文件,解压得到项目文件夹,将其移动到Workspace中,通过Import中的Existing Maven Projects导入项目,然后,在此前的springboot项目中复制数据库连接的配置信息到新项目中,并修改需要连接到的数据库名称为tedu_store

然后,在src/test/java下的测试类,先执行原有的空的测试方法,以检验环境是否正确,然后,在该测试类测试获取数据库连接对象,以检验连接配置信息是否正确:

@Autowired
DataSource dataSource;

@Test
public void getConnection() throws SQLException {
	Connection conn = dataSource.getConnection();
	System.err.println(conn);
}

然后,先创建cn.tedu.store.entity.BaseEntity实体的父类,称之为实体类的“基类”,实现序列化接口,该类的作用就是用于被继承的,所以应该添加abstract修饰符,并且,该类也只需要被子类访问,子类都与它在同一个包中,所以,使用默认的访问权限即可:

abstract class BaseEntity implements Serializable {
	private String createdUser;
	private Date createdTime;
	private String modifiedUser;
	private Date modifiedTime;
}

cn.tedu.store.entity.User实体类,继承自以上BaseEntity类:

public class User extends BaseEntity {
	private Integer uid;
	private String username;
	private String password;
	private String salt;
	private Integer gender;
	private String phone;
	private String email;
	private String avatar;
	private Integer isDelete;

	// SET/GET/基于uid生成hashCode和equals/Serializable
}

4. 用户-注册-持久层

(a) 规划SQL语句

首先,应该分析该功能需要执行的SQL语句,当用户注册时,本质是向数据表中插入数据,则需要执行SQL语句大致是:

insert into t_user (
	除了uid以外的所有字段
) values (
	?,?,?,?,?...?
);

由于设计了“用户名唯一”的规则,在插入数据之前,还应该检查该用户名是否已经被占用,则可以“根据用户名查询数据,并判断是否查询到有效结果”作为判断依据,需要执行的SQL语句大致是:

select uid from t_user where username=?

(b) 接口与抽象方法

创建cn.tedu.store.mapper.UserMapper接口,并在接口中添加抽象方法:

Integer addnew(User user);

User findByUsername(String username);

注意:完成后,应该在启动类之前添加@MapperScan("cn.tedu.store.mapper")注解,以配置MyBatis中的持久层接口在哪个包中。

© 配置映射

src/main/resources下创建mappers文件夹,并在该文件夹中粘贴持久层映射的XML文件,确定文件名应该是UserMapper.xml,配置好根节点的namespace属性,并配置以上接口中2个抽象方法的映射:

	<mapper namespace="cn.tedu.store.mapper.UserMapper">
	
		<!-- 插入用户数据 -->
		<!-- Integer addnew(User user); -->
		<insert id="addnew"
			useGeneratedKeys="true"
			keyProperty="uid">
			INSERT INTO t_user (
				username, password,
				salt, gender,
				phone, email,
				avatar, is_delete,
				created_user, created_time,
				modified_user, modified_time
			) VALUES (
				#{username}, #{password},
				#{salt}, #{gender},
				#{phone}, #{email},
				#{avatar}, #{isDelete},
				#{createdUser}, #{createdTime},
				#{modifiedUser}, #{modifiedTime}
			)
		</insert>
		
		<!-- 根据用户名查询用户数据 -->
		<!-- User findByUsername(String username) -->
		<select id="findByUsername"
			resultType="cn.tedu.store.entity.User">
			SELECT 
				uid 
			FROM 
				t_user 
			WHERE 
				username=#{username}
		</select>
	
	</mapper>

注意:需要检查在application.properties中是否配置了XML映射文件的位置mybatis.mapper-locations=classpath:/mappers/*.xml

完成后,在src/test/java下创建cn.tedu.store.mapper.UserMapperTests单元测试类,并在该类中编写并执行持久层2个功能的测试方法:

	@RunWith(SpringRunner.class)
	@SpringBootTest
	public class UserMapperTests {
   
	
		@Autowired
		UserMapper mapper;
		
		@Test
		public void addnew() {
   
			User user = new User();
			user.setUsername("admin");
			user.setPassword("1234");
			System.err.println(user);
			Integer rows = mapper.addnew(user);
			System.err.println("rows=" + rows);
			System.err.println(user);
		}
		
		@Test
		public void findByUsername() {
   
			String username = "root888";
			User result = mapper.findByUsername(username);
			System.err.println(result);
		}
		
	}

5. 用户-注册-业务层

(a) 规划异常

首先,应该分析用户在执行此次操作时,可能有哪些失败的原因。

应该为所有的自定义异常创建一个公共的父类,以确定自定义异常的类别,所以,创建cn.tedu.store.service.ex.ServiceException,它应该继承自RuntimeException

在当前项目中,设置了“用户名唯一”的规则,如果用户尝试注册的用户名已经被占用,则应该抛出自定义的cn.tedu.store.service.ex.UsernameDuplicateException异常,继承自ServiceException

此次“注册”将执行INSERT语句,则可能存在某些不控的因素导致插入失败,所以,还应该为这种失败的可能设计对应的cn.tedu.store.service.ex.InsertException异常。

(b) 接口与抽象方法

创建cn.tedu.store.service.IUserService业务层接口,并在接口中添加抽象方法:

void reg(User user) throws UsernameDuplicateException, InsertException;

在设计抽象方法时,方法的返回值仅以操作成功为前提进行设计,如果需要考虑操作失败,则使用抛出异常的方式来解决。

© 实现抽象方法

创建cn.tedu.store.service.impl.UserServiceImpl业务层实现类,并实现以上接口,在类之前添加@Service注解,在类中添加@Autowired private UserMapper userMapper;持久层对象:

@Service
public class UserServiceImpl implements IUserService {

	@Autowired
	private UserMapper userMapper;

	public void reg(User user) {
	}

}

然后,重写抽象方法:

public void reg(User user) {
	// 根据参数user中的getUsername()获取尝试注册的用户名
	// 根据以上用户名查询用户数据
	// 判断查询结果是否不为null
	// 是:用户名已经被占用,抛出UsernameDuplicateException
	// 用户名未被占用,允许注册
	// TODO 向参数user中补全属性:盐值
	// TODO 取出参数user中的原始密码
	// TODO 将原始密码加密
	// TODO 向参数user中补全属性:加密后的密码
	// 向参数user中补全属性:isDelete-0
	// 向参数user中补全属性:4项日志
	// 执行注册
}

初步实现为:

	@Override
	public void reg(User user) throws UsernameDuplicateException, InsertException {
   
		// 根据参数user中的getUsername()获取尝试注册的用户名
		String username = user.getUsername();
		// 根据以上用户名查询用户数据
		User result = userMapper.findByUsername(username);
		// 判断查询结果是否不为null
		if (result != null) {
   
			// 是:用户名已经被占用,抛出UsernameDuplicateException
			throw new UsernameDuplicateException(
				"注册失败!尝试注册的用户名(" + username + ")已经被占用!");
		}
		
		// 用户名未被占用,允许注册
		// TODO 向参数user中补全属性:盐值
		// TODO 取出参数user中的原始密码
		// TODO 将原始密码加密
		// TODO 向参数user中补全属性:加密后的密码
		// 向参数user中补全属性:isDelete-0
		user.setIsDelete(0);
		// 向参数user中补全属性:4项日志
		Date now = new Date();
		user.setCreatedUser(username);
		user.setCreatedTime(now);
		user.setModifiedUser(username);
		user.setModifiedTime(now);
		// 执行注册
		Integer rows = userMapper.addnew(user);
		if (rows != 1) {
   
			throw new InsertException(
				"注册失败!插入用户数据时出现未知错误!请联系管理员!");
		}
	}

完成后,在src/test/java下创建cn.tedu.store.service.UserServiceTests单元测试类,并在该类中编写并执行以上功能的测试方法:

	@RunWith(SpringRunner.class)
	@SpringBootTest
	public class UserServiceTests {
   
	
		@Autowired
		IUserService service;
		
		@Test
		public void reg() {
   
			try {
   
				User user = new User();
				user.setUsername("Service");
				user.setPassword("1234");
				service.reg(user);
				System.err.println("OK");
			} catch (ServiceException e) {
   
				System.err.println(e.getClass().getName());
				System.err.println(e.getMessage());
			}
		}
		
	}

6. 用户-注册-控制器层

(a) 统一处理异常

创建cn.tedu.store.controller.BaseController控制类的基类,后续创建的每个控制器类都应该继承自这个基类,在基类中添加处理处理异常的方法,则每个子级的控制器类都相当于拥有这个方法:

	public abstract class BaseController {
   

		@ExceptionHandler(ServiceException.class)
		@ResponseBody
		public JsonResult<Void> handleException(Throwable e) {
   
			JsonResult<Void> jr = new JsonResult<Void>();
			jr.setMessage(e.getMessage());

			if (e instanceof UsernameDuplicateException) {
   
				jr.setState(2);
			} else if (e instanceof InsertException) {
   
				jr.setState(3);
			}

			return jr;
		}

	}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值