基于SpringBoot的商品评价模块后端接口实现
1、技术简介:
SpringBoot+MongoDB+Redis+docker
2、功能实现:
能够为商品打分,要求每天同一个ip只能评价一次
3、技术介绍:
1)SpringBoot的自动配置原理:
它的自动配置原理依赖于它的@SpringBootApplication注解,服务启动的时候,会根据META-INF/spring.factories下的所有的class去自动进行bean的生成以及注入。我们可以根据exclude来实现指定的class不被加载
2)MongoDB的简单使用:
对于它的使用,其实应该要知道,为什么使用MongoDB,这里我只是考虑了它的存储格式很自由,
MongoDB使用的时候,需要为使用的那个db创建一个user,设置名称密码,设置角色
db.createUser({user:"name",pwd:"password",roles:[{role:"readWrite",db:"库名称"}]})
3)docker部署MongoDB:
1、拉取mongo默认是最新版本
docker pull daocloud.io/library/mongo:latest
2、启动
docker run -d -p 27017:27017 -v /opt/mongoDB/:/data/db daocloud.io/library/mongo:latest
3、查看此时运行的容器
docker ps
4、进入容器
docker exec -it id名称 /bin/bash
5、输入mongo指令
mongo
6、查看所有的库
show dbs
4)Redis的作用:
这里使用到Redis只是为了存储一个ip以及对应的上次访问时间而已,但是redis是可以进行很多操作的:
缓存
分布式锁等
4、工具类介绍
1)判断上一次的日期是否属于当天
public class TimeUtil {
/**
* 判断是否重复评价
* @param time
* @return
*/
public static boolean isRepeated(String time){
//第一次评价的时候,从redis中查出的可能为空,此时直接添加即可所以返回true
if(time==null){
return true;
}
//redis中存储的是日期的字符串格式,我们需要转换一下
long l = getMill(time);
//判断当前的日期是否是指定日期的后一天,对应着当前的ip只能评价一次
boolean ok = isOk(l);
return ok;
}
/**
* 判断是否可以评价,也就是小于今天就可以评价
* @param time
* @return
*/
static boolean isOk(long time){
//当前时间毫秒数
long current=System.currentTimeMillis();
//今天零点零分零秒的毫秒数
long zeroTime=current/(1000*3600*24)*(1000*3600*24)- TimeZone.getDefault().getRawOffset();
//计算零点与上一次评价的时间的差值
long ll=time-zeroTime;
if(ll<0){
//此时可以继续评价
return true;
}
return false;
}
/**
* 解析日期,转换为对应的毫秒值
* @param time
* @return
*/
static long getMill(String time){
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date parse = sdf.parse(time);
return parse.getTime();
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
}
2)获取当前请求的ip值
public class IpUtil {
private static final String UNKNOWN = "unknown";
private static final String LOCALHOST = "127.0.0.1";
private static final String SEPARATOR = ",";
public static String getIpAddress(HttpServletRequest request) {
String ipAddress;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (LOCALHOST.equals(ipAddress)) {
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
// "***.***.***.***".length()
if (ipAddress != null && ipAddress.length() > 15) {
if (ipAddress.indexOf(SEPARATOR) > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress = "";
}
return ipAddress;
}
}
3)主要业务逻辑
@Service
@Transactional(rollbackFor = Exception.class)
public class EvaluationServiceImpl implements EvaluationService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 当我们操作redis存储的只是字符串的时候,使用StringRedisTemplate比使用RedisTemplate更好
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public Evaluation insert(Evaluation e,HttpServletRequest request) {
//首先应该判断是否是当天重复执行
//根据ip获取其上一次的访问时间
String ipAddress = IpUtil.getIpAddress(request);
//根据此时的ip从redis中取出
String s = stringRedisTemplate.opsForValue().get(ipAddress);
boolean flag = TimeUtil.isRepeated(s);
if(!flag){
throw new MyException(Result.ADD_REPEAT);
}
//查询出对应的商品规格
//假设此时拿到了一个Sku对象,那么一般来说,它的格式是a:大小;b:颜色这种字符串形式的,我们只是需要"大小 颜色"这种形式
//先转换成Map,然后遍历拼接
Map map=new HashMap(15);
map.put("颜色","红色");
map.put("尺寸","5.6");
StringBuffer sb=new StringBuffer();
for(Object key:map.keySet()){
sb.append(map.get(key)+" ");
}
String skuSpec = sb.substring(0, sb.length() - 1);
//设置规格
e.setSkuSpec(skuSpec);
//设置是否匿名
Integer nameStatus = e.getNameStatus();
if(nameStatus==1){
String username = e.getUsername();
int length = username.length();
if(length==1){
username+="***";
e.setUsername(username);
}else{
String s1=username.substring(0,1);
String s2=username.substring(length-1,length);
username=s1+"***"+s2;
e.setUsername(username);
}
}
//设置用户头像,用户如果为上传头像,那么就会给它设置默认的头像
//根据用户名查询出用户信息,获取头像的路径,那么假设这里已经获取到了
//设置评价时间
long time = System.currentTimeMillis();
String sTime=getTime(time);
e.setTime(sTime);
Evaluation insert = mongoTemplate.insert(e);
//存储时间到redis记录此时的时间
stringRedisTemplate.opsForValue().set(ipAddress,sTime,60*60*24,TimeUnit.SECONDS);
return insert;
}
private String getTime(long time){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm");
String time1 = sdf.format(time);
return time1;
}
}
4)SpringBoot整合redis之StringRedisTemplate的常用方法介绍
//我们这里主要使用到
StringRedisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);
StringRedisTemplate.opsForValue().get(key);
5)公共异常处理