1.开发工具以及相关环境的配置
1.首先关于IDE,前端小程序端采用的是微信官方的微信开发者工具,后端使用的是idea(idea是真的智能,再也不想回去eclipse了呢),关于前端的一些代码,主要是参照微信官方的API进行开发的,整体的文件结构也和js,css,html也很相似。
2.关于测试环境的配置,因为小程序的调试需要认证的https域名才能进行调试,在pc端调试需要开启debug模式,所以相关功能测试,我是使用的内网穿透工具,这样就可以实现在手机端的测试访问
3.为了开发方便,在后台搭建的为聚合工程项目结构如下
- api模块为直接给前端调用的所有接口
- common模块用来存放一些公共的工具类,以及项目所需要的全部依赖
- mapper用来存放jap的dao接口
- pojo用来存放对应数据库的实体类以及对应前端数据需要返回的一些特定的Vo包装类
- service用来存放项目的服务层接口和实现类
4.编写项目的配置文件,为了方便开发,项目采用yml文件的格式
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/videos?useUnicode=true&characterEncoding=utf8
driver-class-name: com.mysql.jdbc.Driver
http:
multipart:
max-file-size: 100Mb
max-request-size: 100Mb
jpa:
show-sql: true
注:useUnicode=true,这个在配置的时候一定要加上,就因为之前开发时候没有写这个配置,导致中文数据存到数据库里面全都是问好,而且查询语句还无法查询到,当时我找了大半天才解决,切记一定要加上!!!
2.编写注册接口
1.在api中创建一个专门提供注册和登录接口的controller
2.然后在公共模块中创建一个用来返回给前端结果的工具类,密码加密md5工具类,redis工具类
import lombok.Data;
/**
* @Description: 自定义响应数据结构
* 这个类是提供给门户,ios,安卓,微信商城用的
* 门户接受此类数据后需要使用本类的方法转换成对于的数据类型格式(类,或者list)
* 其他自行处理
* 200:表示成功
* 500:表示错误,错误信息在msg字段中
* 501:bean验证错误,不管多少个错误都以map形式返回
* 502:拦截器拦截到用户token出错
* 555:异常抛出信息
*/
@Data
public class LexJSONResult {
// 响应业务状态
private Integer status;
// 响应消息
private String msg;
// 响应中的数据
private Object data;
private String ok;
public static LexJSONResult build(Integer status, String msg, Object data) {
return new LexJSONResult(status, msg, data);
}
public static LexJSONResult ok(Object data) {
return new LexJSONResult(data);
}
public static LexJSONResult ok() {
return new LexJSONResult(null);
}
public static LexJSONResult errorMsg(String msg) {
return new LexJSONResult(500, msg, null);
}
public static LexJSONResult errorMap(Object data) {
return new LexJSONResult(501, "error", data);
}
public static LexJSONResult errorTokenMsg(String msg) {
return new LexJSONResult(502, msg, null);
}
public static LexJSONResult errorException(String msg) {
return new LexJSONResult(555, msg, null);
}
public LexJSONResult() {
}
public LexJSONResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public LexJSONResult(Object data) {
this.status = 200;
this.msg = "OK";
this.data = data;
}
}
public class MD5Utils {
/**
* @Description: 对字符串进行md5加密
*/
public static String getMD5Str(String strValue) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
String newstr = Base64.encodeBase64String(md5.digest(strValue.getBytes()));
return newstr;
}
public static void main(String[] args) {
try {
String md5 = getMD5Str("imooc");
System.out.println(md5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Component
public class RedisOperator {
// @Autowired
// private RedisTemplate<String, Object> redisTemplate;
@Autowired
private StringRedisTemplate redisTemplate;
// Key(键),简单的key-value操作
/**
* 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。
*
* @param key
* @return
*/
public long ttl(String key) {
return redisTemplate.getExpire(key);
}
/**
* 实现命令:expire 设置过期时间,单位秒
*
* @param key
* @return
*/
public void expire(String key, long timeout) {
redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 实现命令:INCR key,增加key一次
*
* @param key
* @return
*/
public long incr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 实现命令:KEYS pattern,查找所有符合给定模式 pattern的 key
*/
public Set<String> keys(String pattern) {
return redisTemplate.keys(pattern);
}
/**
* 实现命令:DEL key,删除一个key
*
* @param key
*/
public void del(String key) {
redisTemplate.delete(key);
}
// String(字符串)
/**
* 实现命令:SET key value,设置一个key-value(将字符串值 value关联到 key)
*
* @param key
* @param value
*/
public void set(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 实现命令:SET key value EX seconds,设置key-value和超时时间(秒)
*
* @param key
* @param value
* @param timeout
* (以秒为单位)
*/
public void set(String key, String value, long timeout) {
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}
/**
* 实现命令:GET key,返回 key所关联的字符串值。
*
* @param key
* @return value
*/
public String get(String key) {
return (String)redisTemplate.opsForValue().get(key);
}
// Hash(哈希表)
/**
* 实现命令:HSET key field value,将哈希表 key中的域 field的值设为 value
*
* @param key
* @param field
* @param value
*/
public void hset(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}
/**
* 实现命令:HGET key field,返回哈希表 key中给定域 field的值
*
* @param key
* @param field
* @return
*/
public String hget(String key, String field) {
return (String) redisTemplate.opsForHash().get(key, field);
}
/**
* 实现命令:HDEL key field [field ...],删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
*
* @param key
* @param fields
*/
public void hdel(String key, Object... fields) {
redisTemplate.opsForHash().delete(key, fields);
}
/**
* 实现命令:HGETALL key,返回哈希表 key中,所有的域和值。
*
* @param key
* @return
*/
public Map<Object, Object> hgetall(String key) {
return redisTemplate.opsForHash().entries(key);
}
// List(列表)
/**
* 实现命令:LPUSH key value,将一个值 value插入到列表 key的表头
*
* @param key
* @param value
* @return 执行 LPUSH命令后,列表的长度。
*/
public long lpush(String key, String value) {
return redisTemplate.opsForList().leftPush(key, value);
}
/**
* 实现命令:LPOP key,移除并返回列表 key的头元素。
*
* @param key
* @return 列表key的头元素。
*/
public String lpop(String key) {
return (String)redisTemplate.opsForList().leftPop(key);
}
/**
* 实现命令:RPUSH key value,将一个值 value插入到列表 key的表尾(最右边)。
*
* @param key
* @param value
* @return 执行 LPUSH命令后,列表的长度。
*/
public long rpush(String key, String value) {
return redisTemplate.opsForList().rightPush(key, value);
}
}
3.注册的需求分析
- 首先我需要前端传来一个User对象
- 对传来的对象进行非空判断,如果对象的用户名或者密码为空的话就返回一个带有错误信息的结果类,并传入,用户信息不能空
- 然后要判断该用户是否已经被注册过了,如果已经存在返回给前端用户已被注册信息
- 需要对密码进行加密,使用封装好的md5工具类,对前端传过来的密码进行加密,并存入数据库
- 使用UUID生成一个该用户的一个唯一的token,把USER:+用户的id作为redis的key,该token作为value存入redis中,并设置过期时间为30分钟
4.代码实现
dao层代码
public interface UsersDao extends JpaRepository<Users,String> {
}
service层代码
/**
* 根据传入的用户的用户名在数据库中进行查找
* 如果该用户存在就将该用户返回,如果不存在则返回的就为null
* @param users 用户的pojo类
* @return
*/
@Override
public Users findUserIsExist(Users users) {
Users u =new Users();
u.setUsername(users.getUsername());
Example<Users> example = Example.of(u);
return usersDao.findOne(example);
}
/**
* 将用户信息存入数据库
* @param users
* @return
*/
@Override
@Transactional
public Users save(Users users) {
users.setId(IdUtils.getId());
return usersDao.save(users);
}
controller层代码
@Autowired
private UserService userService;
@Autowired
private RedisOperator redisOperator;
public final String USERSESSIONID="user-session-id";
@ApiOperation(value = "用户注册", notes = "用户注册的接口")
@PostMapping("/regist")
public LexJSONResult regist( @RequestBody Users users) throws Exception {
//1.判断用户名和密码是否为空
if (StringUtils.isEmpty(users.getUsername())){
return LexJSONResult.errorMsg("用户名不能为空");
}
if (StringUtils.isEmpty(users.getPassword())){
return LexJSONResult.errorMsg("密码不能为空");
}
//2.查询数据库看该用户名是否已经被注册
Users userIsExist = userService.findUserIsExist(users);
//如果为空说明没有注册过
if (!StringUtils.isEmpty(userIsExist)){
return LexJSONResult.errorMsg("该用户名已经被注册");
}
//3.初始化用户信息,并存入数据库
String md5Str = MD5Utils.getMD5Str(users.getPassword());
users.setPassword(md5Str);
users.setNickname(users.getUsername());
users.setFansCounts(0);
users.setFollowCounts(0);
users.setReceiveLikeCounts(0);
Users save = userService.save(users);
save.setPassword("");
String token=UUID.randomUUID().toString();
redisOperator.set(USERSESSIONID+":"+save.getId(),token,60*30);
UsersVo usersVo =new UsersVo();
BeanUtils.copyProperties(save,usersVo);
//将生成的token信息返回给前端,做一些相关的验证
usersVo.setToken(token);
return LexJSONResult.ok();
}
3.编写用户登录的接口
1.登录需求分析
- 接受到前端传来的user对象,首先对该对象进行判断,如果该对象的用户名或者密码为空的话,返回用户名密码不能为空
- 需要判断该用户是否存在,查询数据库,如果不存在返回该用户不存在
- 先获取到用户密码,然后通过md5加密生成的加密字符串存入该对象,通过用户名查询数据库,校验密码是否一致,如果不一致返回密码错误
2.代码实现
service层的相关代码
/**
* 通过用户名校验用户密码是否正确,如正确则将查询到的对象返回
* 若果错误则返回null
* @param users
* @return
*/
@Override
public Users checkPassword(Users users) {
//根据用户名查询用户密码是否正确
Users u=new Users();
u.setUsername(users.getUsername());
Example<Users> example =Example.of(u);
Users one = usersDao.findOne(example);
//如果密码相同就将该对象返回,密码正确
if (users.getPassword().equals(one.getPassword())){
return one;
}
//如果返回null,则说明密码错误
return null;
/**
* 将用户信息存入数据库
* @param users
* @return
*/
@Override
@Transactional
public Users save(Users users) {
users.setId(IdUtils.getId());
return usersDao.save(users);
}
controller层代码实现
@ApiOperation(value = "用户登录", notes = "用户登录的接口")
@PostMapping("/login")
public LexJSONResult login(@RequestBody Users users) throws Exception {
//判断用户名和密码不能为空
if (StringUtils.isEmpty(users.getUsername())){
return LexJSONResult.errorMsg("用户名不能为空");
}
if (StringUtils.isEmpty(users.getPassword())){
return LexJSONResult.errorMsg("密码不能为空");
}
//查询该用户是否存在
Users userIsExist = userService.findUserIsExist(users);
if (StringUtils.isEmpty(userIsExist)){
return LexJSONResult.errorMsg("对不起,该用户不存在");
}
//检验密码和用户名是否相符
users.setPassword(MD5Utils.getMD5Str(users.getPassword()));
Users u = userService.checkPassword(users);
if (StringUtils.isEmpty(u)){
return LexJSONResult.errorMsg("对不起,你输入的密码不正确");
}
u.setPassword("");
UsersVo usersVo =new UsersVo();
BeanUtils.copyProperties(u,usersVo);
usersVo.setToken(token);
return LexJSONResult.ok(usersVo);
}
userVo对象,该类在原有的user类中新添加了一个token字段,返回给前端,用来做后面的一些相关的权限验证
@Data
public class UsersVo {
@ApiModelProperty(hidden = true)
private String id;
/**
* 用户登陆后生成的令牌
*/
private String token;
/**
* 用户名
*/
@ApiModelProperty(value = "用户名",name = "username",example = "123",required = true)
private String username;
/**
* 密码
*/
@ApiModelProperty(value = "用户密码",name = "password",example = "123",required = true)
@JsonIgnore
private String password;
/**
* 我的头像,如果没有默认给一张
*/
@ApiModelProperty(hidden = true)
private String faceImage;
/**
* 昵称
*/
@ApiModelProperty(hidden = true)
private String nickname;
/**
* 我的粉丝数量
*/
@ApiModelProperty(hidden = true)
private Integer fansCounts;
/**
* 我关注的人总数
*/
@ApiModelProperty(hidden = true)
private Integer followCounts;
/**
* 我接受到的赞美/收藏 的数量
*/
@ApiModelProperty(hidden = true)
private Integer receiveLikeCounts;
}
4.编写用户注销接口
1.需求分析
用户注销接口相对来说比较简单,用户如果调用该接口只需要将redis中用户对应的数据删除即可
2.代码实现
@ApiOperation(value = "用户注销", notes = "用户注销的接口")
@ApiImplicitParam(name = "id",value = "用户id",required = true,dataType = "String" ,paramType = "query")
@PostMapping("/logout")
public LexJSONResult logout(String id){
//将redis中的数据删除
redisOperator.del(USERSESSIONID+":"+id);
return LexJSONResult.ok();
}