springboot项目目前设计的技术点

maven依赖

  • gson格式化json使用
  • freemarker模板引擎渲染
  • common-langs工具包使用
  • druid数据库连接池使用
  • mybatis-pagehelper使用
  • mybatis-generator逆向pojo,mapper,dao功能使用
  • mybatis-plugin mapper和xml文件对应插件使用\
  • mybatis使用(早期使用jpa,后改成mybatis注解,最后改为使用mybatis-xml配置)
  • redis缓存使用(这块暂时未涉及实际功能,已尝试过测试demo)

项目配置方面

  • 使用正测试环境分离


    springboot项目目前设计的技术点_第1张图片
  • log日志文件化



    这块解决了我在线上遇到的很多bug,还是非常的有用的

  • mybatis逆向pojo,dao,mapper层


  • 线上脚本自动发布

项目模块(从dao层往前翻)

数据库这块(这些都是我踩过的坑)

springboot项目目前设计的技术点_第2张图片

文章部分这里使用text格式存储,这里要注意一点,text在逆向时会生成blob格式,这不是我们想要的varchar格式,我们得指定转成varchar格式


        

时间这块用datetime,创建有默认时间,更新随时间更新更新

CREATE TABLE `t_article` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(200) DEFAULT NULL COMMENT '标题',
  `titlePic` varchar(100) DEFAULT NULL COMMENT '标题图片',
  `content` text COMMENT '主题内容',
  `author_id` int(11) DEFAULT NULL COMMENT '用户id',
  `type` varchar(16) DEFAULT NULL COMMENT '类型',
  `status` int(1) DEFAULT NULL COMMENT '状态:0-未发布,1-已发布,2-已撤销',
  `category_id` int(11) DEFAULT NULL COMMENT '类型id',
  `category_name` varchar(20) DEFAULT NULL COMMENT '类型名称',
  `allow_comment` tinyint(1) DEFAULT '0' COMMENT '允许评论:0-允许,1-不允许',
  `like_count` int(11) DEFAULT '0' COMMENT '点赞数',
  `collect_count` int(11) DEFAULT '0' COMMENT '收藏数',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

评论

评论这款我加了个ip字段,因为评论不需要登录,为了防止有人狂刷接口,这里我用ip禁止,每个ip不能重复发表评论

CREATE TABLE `t_comment` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `address_ip` varchar(30) NOT NULL COMMENT '评论ip,防刷接口',
  `article_id` int(11) NOT NULL COMMENT '文章id',
  `parent_id` int(11) DEFAULT NULL COMMENT '父评论',
  `content` varchar(50) NOT NULL COMMENT '评论内容',
  `person_id` int(11) DEFAULT NULL COMMENT '评论人id',
  `status` int(2) DEFAULT '0' COMMENT '评论状态:0-正常,1-删除',
  `like_count` int(11) DEFAULT '0' COMMENT '点赞数',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `name` varchar(50) DEFAULT NULL COMMENT '昵称',
  `icon` varchar(200) DEFAULT 'https://upload.jianshu.io/users/upload_avatars/7290998/f64f5ef0-def0-4b26-beb3-b9d88f060ba0.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240' COMMENT '头像',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

t_log这块,之前我想把错误数据打到数据库内,后来懒得做了,直接本地化存储.........其实要做也是可以的,我做了errorlog处理,这个后面再谈

数据库总结:
第一次设计数据库,真的很没有经验,我基本上是边写边改字段,然后边重新生成mapper,xml,pojo,这里我希望给没有那么多经验的人一个警示:设计一个表的时候,一定要考虑好状态这个点,这个表对应的实体有什么状态呢,比如上架,下架,审核未审核,删除未删除,这些都要有状态!

DAO层:

  • mapper里面注意返回插入主键,以保证返回插入主键

  • 多参数取值要注意用@Param键,取值用#{}
User login(@Param("username") String username,@Param("password") String md5Password);

DAO层总结:
这块儿没什么好说,sql多写写即可

Service层:

  • 不要在for循环里面做select操作,这种是很纯的做法,比如一组文章记录中for循环去查对应的分类id对应的分类名,这种幂次操作很傻
  • 注意库存概念,这是我之前做商城项目时遇到的,要注意下单前,库存余量,撤销订单时要注意库存余量等
  • 在执行sql把可能遇到的error都给处理掉,不要什么都不想就去做sql操作,对sql的操作都进行try-catch,然后抛出我们的CrmException等,然后做统一返回,并且得把这个error落地到日志去处理,这里我写的比较粗糙,大致意思就是这样
//这里还需要当前用户的信息
        User user = CrmUtils.getLoginUser(request);
        if (null == user) {
            throw new CrmAuthException();
        }
        try {
            //取值
            Article article = new Article();
            BeanUtils.copyProperties(articleForm, article);
            article.setContent(articleForm.getText());
            article.setAuthorId(user.getId());

            int result = articleMapper.insertSelective(article);
            if (result > 0) {
                return article;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CrmException(RespEnum.article_insert_error);
        }
} catch (Exception e) {
       e.printStackTrace();
       throw new CrmException(450, e.getMessage());
}
  • 避免横向越权,对数据的操作一定要验证当前用户是不是这个文章的用户等,这个是基础,不要随便来个接口请求就能把文章给删了!!!!!

service总结:
这块真的很重要,很多很多的逻辑都是这块儿处理的,希望大家做的时候多思考

controller层

  • 对api处理和对url处理分层,可以做层继承什么的(这些都是基础,相信大家都这么做的)


    springboot项目目前设计的技术点_第3张图片
  • 可以用@Reqparam去取参,也可以用抽出一层form层取参,我这里为了好调用BeanUtils.copyProperties(articleForm, article);这个方法多了层form层处理,lombok不错,希望没用过得人都去用用
import lombok.Data;

import javax.validation.constraints.NotNull;

@Data
public class CommentForm {

    @NotNull(message = "文章id不能为空")
    private Integer articleId;

    @NotNull(message = "昵称不能为空")
    private String name;

    @NotNull(message = "评论内容不能为空")
    private String content;

    private Integer parentId;

    private Integer personId;

    private String addressIp;

}
  • 路径匹配简单用法
@RequestMapping("/article/{id}")
    public String article(@PathVariable("id") Integer id, Map map) {

controller总结
注意结构清晰即可

exception处理

@Data
public class CrmException extends Exception{

    private Integer code;

    public CrmException(RespEnum respEnum) {
        super(respEnum.getMsg());
        this.code = respEnum.getCode();
    }

    public CrmException(Integer code, String msg) {
        super(msg);
        this.code = code;
    }
}
@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(CrmException.class)
    @ResponseBody
    public Resp handleException(CrmException exception) {
        return Resp.error(exception.getCode(), exception.getMessage());
    }

    @ExceptionHandler(CrmAuthException.class)
    public String hanleAuthException(CrmAuthException exception) {
        return "/admin/login";
    }

}

spring已经为我们做好了一切,只需稍微配置即可

拦截器处理

@Slf4j
@Component
public class AdminInterceptor implements HandlerInterceptor {

    private  String baseImg = "/upload";


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String uri = request.getRequestURI();
        // 这里传过去basePath的一些参数
        request.getSession().setAttribute("base", "");
        request.getSession().setAttribute("baseImg", baseImg);

//        log.info(String.valueOf(request.getSession().getMaxInactiveInterval()));
        log.info("UserAgent: {}", request.getHeader("user-agent"));
        log.info("用户访问地址: {}, 来路地址: {}", uri, IPKit.getIpAddrByRequest(request));
        //请求拦截处理
        User user = CrmUtils.getLoginUser(request);
        if (uri.startsWith("/admin") && !uri.startsWith("/admin/login") && null == user
                && !uri.startsWith("/admin/css") && !uri.startsWith("/admin/images")
                && !uri.startsWith("/admin/js")) {
            response.sendRedirect(request.getContextPath() + "/admin/login");
            return false;
        }

        //先在这里存一下user
        request.setAttribute("user", user);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    }
}

@Component
public class WebMVCConfig implements WebMvcConfigurer {

@Autowired
private AdminInterceptor adminInterceptor;


@Value("${web.upload}")
private String rootPath;

/**
 * 不需要登录拦截的url:登录注册
 */
final String[] notLoginInterceptPaths = {"/upload/**","/admin/login","/admin/regist"};

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(adminInterceptor).addPathPatterns("/**").excludePathPatterns("/upload/**","/admin/login","/admin/regist","/error");

}


@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //这里是用来处理upload文件的文件地址访问,因为上传的图片等在项目外
    registry.addResourceHandler("/upload/**").addResourceLocations("file:"+rootPath + "/");
}

}

项目目录:

springboot项目目前设计的技术点_第4张图片

其实目前我的boot项目弊端已经体现出来了,controller,service,dao层太过厚重了,一个功能可能要多很多个文件,项目也越来越厚,但是没办法,目前我这个是单模块web项目,后续我会再起一个项目(或者直接在这个项目上修改),直接越过maven模块(因为这块已经做过),向spring cloud前进!

这篇博客我后面也会同步到我搭建的博客网站,欢迎大家比较,指正!(本博客暂未开通emoji表情,希望大家文章内别带emoji表情,不然是创建不成功的)
博客链接

你可能感兴趣的:(springboot项目目前设计的技术点)