项目随笔-ssm开发移动端小说评价网(仅个人学习)

前言:

博主开学进入大三下学期,目前备考计算机研究生,但是很坦诚地说,不一定能考上。因此,利用考研复习之余练习自己的后端开发技术也成了一种必要,(如果)考研失败坚决不二战,去外包当个crud boy也挺好。

简要说明ssm中各层的作用:

1.持久层

定义mapper接口,负责与数据库进行交互设计

2.业务层

定义service接口和service接口实现类,负责业务模块的逻辑应用设计

3.控制层

定义controller类,负责每个具体的业务模块流程控制

4.视图层

负责前台页面的展示

本项目使用的软件:

IntelliJ IDEA 、Navicat Premium 15
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第1张图片

注:

本项目主要记录后端内容,前端部分简要阐述或省略

开发步骤:

1.导入依赖包
<dependencies>
        <!--SpringMVC核心依赖包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>
        
        <!--Freemarker依赖包-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>
        
        <!--Freemarker支持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.3.3</version>
        </dependency>
        
        <!--json序列化包-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.0</version>
        </dependency>

        <!--Mybatis依赖包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        
        <!--Mybatis与Spring整合包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.14</version>
        </dependency>
        
        <!--单元测试包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        
		<!--javax.servlet包-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!--Mybatis-Plus包-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
        
        <!--Kaptcha验证码包-->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
        
        <!--加密/解密包-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>
        
        <!--Spring MVC文件上传底层包-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
        
        <!--Json Html解析包-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.12.1</version>
        </dependency>
</dependencies>
2.配置web.xml
	<!--配置DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext*.xml</param-value>
        </init-param>       
		<!--在Web应用启动时自动创建Spring Ioc容器,并初始化DispatcherServlet-->
        <load-on-startup>0</load-on-startup>  
    </servlet>
    
	<!--为DispatcherServlet映射url-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <!--解决Get、post请求的中文乱码-->
    <filter>
        <filter-name>characterFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterFilter</filter-name>
        <url-pattern>/*
    
3.配置applicationContext.xml
	<!--开启SpringMVC注解模式-->
    <context:component-scan base-package="com.xiaopeng"/>
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <!--解决响应的中文乱码-->
                        <value>text/html;charset=utf-8</value>
                        <!--json序列化输出配置-->
                        <value>application/json;charset=utf-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <mvc:default-servlet-handler/>
    
    <!--配置Freemarker模板引擎-->
    <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/WEB-INF/ftl"/>
        <property name="freemarkerSettings">
            <props>
                <prop key="defaultEncoding">utf-8</prop>
            </props>
        </property>
    </bean>
     
    <!-- 配置视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="contentType" value="text/html;charset=utf-8"/>
        <property name="suffix" value=".ftl"/>
    </bean>
    
    <!--Mybatis与Spring的整合配置-->
    <!--配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/xiaopeng_reader?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="initialSize" value="10"/>
        <property name="maxActive" value="20"/>
    </bean>
    
    <!--创建sessionFactory对象-->
    <!--bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"-->
    <bean id="sessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:mappers/*.xml"/>
        
    <!--Mybatis配置文件地址-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    
    <!--配置Mapper扫描器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.xiaopeng.reader.mapper"/>
    </bean>
    
    <!--声明式事务配置-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <!--配置Kaptcha验证码-->
    <bean id="kaptchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg>
                    <props>
                        <!--验证码图片不生成边框-->
                        <prop key="kaptcha.border">no</prop>
                        <!--验证码图片宽度150像素-->
                        <prop key="kaptcha.image.width">100</prop>
                        <!--验证码图片字体颜色为浅蓝色-->
                        <prop key="kaptcha.textproducer.font.color">blue</prop>
                        <!--每个字符最大占用50像素-->
                        <prop key="kaptcha.textproducer.font.size">50</prop>
                        <!--验证码包含4个字符-->
                        <prop key="kaptcha.textproducer.char.length">4</prop>
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>
    
    <!--开启spring task任务的注解模式-->
    <task:annotation-driven/>
    
	<!--文件上传配置-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>
3.配置mybatis-config.xml和logback.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration 
         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
    <plugins>
        <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"></plugin>
    </plugins>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{
     HH:mm:ss}%-5level[%thread]%logger{
     30} - %msg%n</pattern>
            <charset>UTF=8</charset>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="console"/>
    </root>
</configuration>
4.设计数据表

项目随笔-ssm开发移动端小说评价网(仅个人学习)_第2张图片
在这里插入图片描述
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第3张图片
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第4张图片
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第5张图片
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第6张图片
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第7张图片

5-(1)创建Category实体类
@TableName("category")
public class Category {
     
    @TableId(type= IdType.AUTO)
    private Long categoryId;
    private String categoryName;

    public Long getCategoryId() {
     
        return categoryId;
    }

    public void setCategoryId(Long categoryId) {
     
        this.categoryId = categoryId;
    }

    public String getCategoryName() {
     
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
     
        this.categoryName = categoryName;
    }
}
5-(2)创建CategoryMapper接口

继承BaseMapper接口,拥有基类增删改查方法

public interface CategoryMapper extends BaseMapper<Category> {
     }
5-(3)创建category.xml

关联CategoryMapper接口,从而对数据库进行操作

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaopeng.reader.mapper.CategoryMapper"></mapper>
5-(4)创建CategoryService接口

自定义抽象方法selectAll,由子类实现其抽象方法

public interface CategoryService {
     
    public List<Category> selectAll();
}
5-(5)创建CategoryServiceImpl接口实现类

实现CategoryService接口的抽象方法:
通过categoryMapper接口的selectList方法得到category表
自定义selectAll方法返回category表

@Service("categoryService")
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public class CategoryServiceImpl implements CategoryService {
     
    @Resource
    private CategoryMapper categoryMapper;
    public List<Category> selectAll(){
     
        List<Category> list=categoryMapper.selectList(new QueryWrapper<Category>());
        return list;
    }
}
5-(6)创建bookController类并编写showIndex方法

调用CategoryService接口向上转型的selectAll方法得到category表
自定义showIndex方法返回category表数据

@Controller
public class BookController {
     
    @Resource
    private CategoryService categoryService;
	@GetMapping("/")
    public ModelAndView showIndex(){
     
        ModelAndView mav=new ModelAndView("/index");
        List<Category> categoryList=categoryService.selectAll();
        mav.addObject("categoryList",categoryList);
        return mav;
    }
}
5-(7)将数据填充到视图(主要代码如下)
<div class="col-8 mt-2">
                <span data-category="-1" style="cursor: pointer" class="highlight  font-weight-bold category">全部</span>
                |
                <#list categoryList as category>
                <a style="cursor: pointer" data-category="${category.categoryId}" class="text-black-50 font-weight-bold category">${
     category.categoryName}</a>
                <#if category_has_next>|</#if>
                </#list>
        </div>

实现功能:
显示动态数据:武侠,玄幻,都市,二次元
显示效果:
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第8张图片

6-(1)创建Book实体类
6-(2)创建BookMapper接口
6-(3)创建book.xml
6-(4)创建BookService接口

自定义抽象方法paging,由子类实现其抽象方法

public interface BookService {
     
    public IPage<Book> paging(Long categoryId,String order,Integer page,Integer rows);
}
6-(5)创建BookServiceImpl接口实现类

实现BookService接口的抽象方法:
通过BookMapper接口的selectPage方法和条件构造器筛选得到分页对象
自定义paging方法返回分页对象

@Service("BookService")
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public class BookServiceImpl implements BookService {
     
    @Resource
    private BookMapper bookMapper;
    public IPage<Book> paging(Long categoryId,String order,Integer page, Integer rows){
     
        Page<Book> p=new Page<Book>(page,rows);
        QueryWrapper<Book> queryWrapper=new QueryWrapper<Book>();
        if(categoryId!=null&&categoryId!=-1){
     
            queryWrapper.eq("category_id",categoryId);
        }
        if(order!=null){
     
            if(order.equals("quantity")){
     
                queryWrapper.orderByDesc("evaluation_quantity");
            }else if(order.equals("score")){
     
                queryWrapper.orderByDesc("evaluation_score");
            }
        }
        IPage<Book> pageObject=bookMapper.selectPage(p,queryWrapper);
        return pageObject;
    }
6-(6)在bookController类中编写selectBook方法

调用BookService接口向上转型的paging方法得到分页对象
自定义selectBook方法返回分页对象的json格式数据

	@Resource
    private BookService bookService;
	@GetMapping("/books")
    @ResponseBody
    public IPage<Book> selectBook(Long categoryId,String order,Integer p){
     
        if(p==null)p=1;
        IPage<Book> pageObject=bookService.paging(categoryId,order,p,3);
        return pageObject;
    }
6-(7)将服务器端的json格式数据动态填充到视图(主要代码如下)
<script>
        $.fn.raty.defaults.path="../../resources/raty/lib/images";
        function loadMore(isReset){
     
            if(isReset==true){
     
                $("#bookList").html("");
                $("#nextPage").val(1);
            }
            var nextPage=$("#nextPage").val();
            var categoryId=$("#categoryId").val();
            var order=$("#order").val();
            $.ajax({
     
                url:"/books",
                data:{
     p:nextPage,"categoryId":categoryId,"order":order},
                type:"get",
                dataType:"json",
                success:function (json) {
     
                    console.info(json);
                    var list=json.records;
                    for(var i=0;i<list.length;i++){
     
                        var bookRecords=json.records[i];
                        var html=template("template",bookRecords);
                        console.info(html);
                        $("#bookList").append(html);
                    }
                    $(".stars").raty({
     readOnly:true});

                    if(json.current<json.pages){
     
                        $("#nextPage").val(parseInt(json.current)+1);
                        $("#btnMore").show();
                        $("#divNoMore").hide();
                    }else{
     
                        $("#btnMore").hide();
                        $("#divNoMore").show();
                    }
                }
            })
        }
        $(function () {
     
            loadMore(true);
        })
        $(function () {
     
            $("#btnMore").click(function () {
     
                loadMore();
            })
            $(".category").click(function () {
     
                $(".category").removeClass("highlight");
                $(".category").addClass("text-black-50");
                $(this).addClass("highlight")
                var categoryId=$(this).data("category");
                $("#categoryId").val(categoryId);
                loadMore(true);
            })
            $(".order").click(function () {
     
                $(".order").removeClass("highlight");
                $(".order").addClass("text-black-50");
                $(this).addClass("highlight")
                var order=$(this).data("order");
                $("#order").val(order);
                loadMore(true);
            })
        })
  </script>

实现功能:
1.客户端访问时默认显示首页,每页记录数为3条
2.点击加载更多时:p+=1,局部更新页面并显示下一页的3条小说信息
3.小说信息由Book表的数据动态填充,星型组件随着评分的变化动态更新
4.武侠、玄幻、都市、二次元和按热度、按评分用于筛选和排序小说.前者按照categoryId,后者按照evaluationScore和evaluationQuantity进行筛选和排序,比如点击二次元再点击按评分,会显示对应小说类型的图书并按照评分标准进行降序排列
显示效果:
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第9张图片

6-(8)编写bookService接口

自定义抽象方法selectById,由子类实现其抽象方法

public Book selectById(Long bookId);
6-(9)编写bookServiceImpl接口实现类

实现BookService接口的抽象方法:
通过BookMapper接口的selectById方法得到对应bookId的book表记录
自定义selectById方法返回对应bookId的book表记录

public Book selectById(Long bookId){
     
        Book book=bookMapper.selectById(bookId);
        return book;
    }
6-(10)编写bookController中的showDetail()方法

调用BookService的selectById方法得到对应bookId的Book表记录
自定义showDetail方法返回对应bookId的book表数据

	@GetMapping("/book/{id}")
    public ModelAndView showDetail(@PathVariable("id") Long id){
     
        Book book=bookService.selectById(id);
        ModelAndView mav=new ModelAndView("/detail");
        mav.addObject("book",book);
        return mav;
    }
6-(11)渲染视图(此处代码省略)

实现功能:
接收对应bookId的Book表数据并填充到视图中
显示效果:
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第10张图片

7-(1)创建Evaluation实体类
7-(2)创建EvaluationMapper接口
7-(3)创建evaluation.xml
7-(4)创建EvaluationService接口

自定义抽象方法selectById,由子类实现其抽象方法

public List<Evaluation> selectById(Long bookId);
7-(5)创建Member实体类
7-(6)创建MemberMapper接口
7-(7)创建member.xml
7-(8)Evaluation实体类中新增book,member属性以及get,set方法
	@TableField(exist = false)
    private Book book;
    public Book getBook() {
     
        return book;
    }
    public void setBook(Book book) {
     
        this.book = book;
    }

    @TableField(exist = false)
    private Member member;
    public Member getMember() {
     
        return member;
    }

    public void setMember(Member member) {
     
        this.member = member;
    }
7-(9)创建EvaluationServiceImpl接口实现类

实现EvaluationService接口的抽象方法:
通过EvaluationMapper接口的selectById方法得到对应bookId的有效evaluation表
自定义selectById方法返回有效evaluation表

	@Resource
    private EvaluationMapper evaluationMapper;
    @Resource
    private MemberMapper memberMapper;
    @Resource
    private BookMapper bookMapper;
    @Service("EvaluationService")
	@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
   public List<Evaluation> selectById(Long bookId){
     
        Book book=bookMapper.selectById(bookId);
        QueryWrapper<Evaluation> queryWrapper=new QueryWrapper<Evaluation>();
        queryWrapper.eq("book_id",bookId);
        queryWrapper.eq("state","enable");
        queryWrapper.orderByDesc("create_time");
        List<Evaluation> evaluationList=evaluationMapper.selectList(queryWrapper);
        for(Evaluation eva:evaluationList){
     
            Member member=memberMapper.selectById(eva.getMemberId());
            eva.setMember(member);
            eva.setBook(book);
        }
        return  evaluationList;
    }
}
7-(10)修改bookController中的showDetail方法

调用BookService的selectById方法得到对应bookId的book表记录
调用EvaluationService的selectById方法得到对应bookId的evaluation表
自定义showDetail方法返回对应bookId的book表记录和evaluation表

	@Resource
    private EvaluationService evaluationService;
	@GetMapping("/book/{id}")
    public ModelAndView showDetail(@PathVariable("id") Long id){
     
        Book book=bookService.selectById(id);
        List<Evaluation> evaluationList=evaluationService.selectById(id);
        ModelAndView mav=new ModelAndView("/detail");
        mav.addObject("book",book);
        mav.addObject("evaluationList",evaluationList);
        return mav;
    }
7-(11)渲染视图(此处代码省略)

实现功能:
接收对应bookId的有效evaluation表数据并填充到视图中
显示效果:
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第11张图片

8-(1)创建kaptchaController类并编写createVerifyCode方法

自定义createVerifyCode方法返回验证码图片

@Controller
public class KaptchaController {
     
    @Resource
    private Producer kaptchaProducer;
    @GetMapping("/verify_code")
    public void createVerifyCode(HttpServletRequest request, HttpServletResponse response){
     
        response.setDateHeader("Expires",0);
        response.setHeader("Cache-Control","no-store,no-cache,must-revalidate");
        response.setHeader("Cache-Control","post-check=0,pre-check=0");
        response.setHeader("Pragma","no-cache");
        response.setContentType("image/png");
        String verifyCode=kaptchaProducer.createText();
        request.getSession().setAttribute("kaptchaVerifyCode",verifyCode);
        BufferedImage image=kaptchaProducer.createImage(verifyCode);
        ServletOutputStream out=null;
        try {
     
            out=response.getOutputStream();
            ImageIO.write(image,"png",out);
        } catch (IOException e) {
     
            e.printStackTrace();
        }finally {
     
            try {
     
                out.flush();
                out.close();
            } catch (IOException e) {
     
                e.printStackTrace();
            }
        }
    }
}
8-(2)业务逻辑异常类中编写BussinessException方法
public class BussinessException  extends RuntimeException{
     
    private String code;
    private String msg;
    public BussinessException(String code,String msg){
     
        super(code+":"+msg);
        this.code=code;
        this.msg=msg;
    }

    public String getCode() {
     
        return code;
    }

    public void setCode(String code) {
     
        this.code = code;
    }

    public String getMsg() {
     
        return msg;
    }

    public void setMsg(String msg) {
     
        this.msg = msg;
    }
}

8-(3)编写MD5Utils加密类

算法:
将密码字符串拆成一个个字符,加密后产生一个个新的字符,再将一个个新的字符组装起来
自定义md5Digest方法返回加密后的字符串

public class MD5Utils {
     
    public static String md5Digest(String source,Integer salt){
     
        char[] c=source.toCharArray();
        for(int i=0;i<c.length;i++){
     
            c[i]=(char)(c[i]+salt);
        }
        String target=new String(c);
        String md5=DigestUtils.md5Hex(target);
        return md5;
    }
}
9-(1)创建MemberReadState实体类
9-(2)创建MemberReadStateMapper接口
9-(3)创建member_read_state.xml
9-(4)创建MemberService接口
public interface MemberService {
     
    /**
     * 用户注册,创建新用户
     * @param username 用户名
     * @param password 密码
     * @param nickname 昵称
     * @return 新用户对象
     */
    public Member createMember(String username,String password,String nickname);

    /**
     * 验证登录
     * @param username 用户名
     * @param password 密码
     * @return 
     */
    public Member checkLogin(String username,String password);

    /**
     *
     * @param memberId 用户编号
     * @param bookId 图书编号
     * @return 阅读状态对象
     */
    public MemberReadState selectMemberReadState(Long memberId,Long bookId);

    /**
     *更新阅读状态
     * @param memberId 用户编号
     * @param bookId 图书编号
     * @param readState 阅读状态
     * @return 阅读状态对象
     */
    public MemberReadState updateMemberReadState(Long memberId,Long bookId,Integer readState);

    /**
     *发布新评论
     * @param memberId 会员编号
     * @param bookId 图书编号
     * @param score 评分
     * @param content 评价内容
     * @return
     */
    public Evaluation evaluate(Long memberId,Long bookId,Integer score,String content);

    /**
     * 评论点赞功能
     * @param evaluationId 评论编号
     * @return评论对象
     */
    public Evaluation enjoy(Long evaluationId);
}
9-(5)创建MemberServiceImpl接口实现
@Service("MemberService")
@Transactional
public class MemberServiceImpl implements MemberService {
     
    @Resource
    private MemberMapper memberMapper;
    @Resource
    private MemberReadStateMapper memberReadStateMapper;
    @Resource
    EvaluationMapper evaluationMapper;
   
    public Member createMember(String username, String password, String nickname) {
     
        QueryWrapper<Member> queryWrapper=new QueryWrapper<Member>();
        queryWrapper.eq("username",username);
        List<Member> members=memberMapper.selectList(queryWrapper);
        if(members.size()>0){
     
            throw new BussinessException("Mistake01","用户名已存在");
        }
        Member member=new Member();
        member.setUsername(username);
        member.setNickname(nickname);
        int salt=new Random().nextInt(1000)+1000;
        String md5=MD5Utils.md5Digest(password,salt);
        member.setPassword(md5);
        member.setSalt(salt);
        member.setCreateTime(new Date());
        memberMapper.insert(member);
        return member;
    }
    
    public Member checkLogin(String username,String password){
     
        QueryWrapper<Member>queryWrapper=new QueryWrapper<Member>();
        queryWrapper.eq("username",username);
        Member member=memberMapper.selectOne(queryWrapper);
        if(member==null){
     
            throw new BussinessException("Mistake02","用户不存在");
        }
        String md5=MD5Utils.md5Digest(password,member.getSalt());
        if(!md5.equals(member.getPassword())){
     
            throw new BussinessException("Mistake03","密码不正确");
        }
        return member;
    }
    
    public MemberReadState selectMemberReadState(Long memberId,Long bookId){
     
        QueryWrapper<MemberReadState> queryWrapper=new QueryWrapper<MemberReadState>();
        queryWrapper.eq("book_id",bookId);
        queryWrapper.eq("member_id",memberId);
        MemberReadState memberReadState=memberReadStateMapper.selectOne(queryWrapper);
        return memberReadState;
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
    public MemberReadState updateMemberReadState(Long memberId, Long bookId, Integer readState) {
     
        QueryWrapper<MemberReadState> queryWrapper=new QueryWrapper<MemberReadState>();
        queryWrapper.eq("book_id",bookId);
        queryWrapper.eq("member_id",memberId);
        MemberReadState memberReadState=memberReadStateMapper.selectOne(queryWrapper);
        if(memberReadStateMapper==null){
     
            memberReadState= new MemberReadState();
            memberReadState.setMemberId(memberId);
            memberReadState.setBookId(bookId);
            memberReadState.setReadState(readState);
            memberReadState.setCreateTime(new Date());
            memberReadStateMapper.insert(memberReadState);
        }else{
     
            memberReadState.setReadState(readState);
            memberReadStateMapper.updateById(memberReadState);
        }
        return memberReadState;
    }

    public Evaluation evaluate(Long memberId, Long bookId, Integer score, String content) {
     
        Evaluation evaluation=new Evaluation();
        evaluation.setMemberId(memberId);
        evaluation.setBookId(bookId);
        evaluation.setScore(score);
        evaluation.setContent(content);
        evaluation.setCreateTime(new Date());
        evaluation.setState("enable");
        evaluation.setEnjoy(0);
        evaluationMapper.insert(evaluation);
        return evaluation;
    }

    public Evaluation enjoy(Long evaluationId) {
     
        Evaluation eva=evaluationMapper.selectById(evaluationId);
        eva.setEnjoy(eva.getEnjoy()+1);
        evaluationMapper.updateById(eva);
        return eva;
    }
}
9-(5)创建MemberController类
@Controller
public class MemberController {
     
    @Resource
    private MemberService memberService;

    @GetMapping("/register.html")
    public ModelAndView showRegister(){
     
        return new ModelAndView("/register");
    }

    @GetMapping("/login.html")
    public ModelAndView showLogin(){
     
        return new ModelAndView("/login");
    }

    @PostMapping("/register")
    @ResponseBody
    public Map register(String vc, String username, String password, String nickname, HttpServletRequest request){
     
        String verifyCode=(String)request.getSession().getAttribute("kaptchaVerifyCode");
        Map result=new HashMap();
        if(vc==null || verifyCode==null ||!vc.equalsIgnoreCase(verifyCode)){
     
            result.put("code","vcMistake");
            result.put("msg","验证码错误");
        }else{
     
            try {
     
                memberService.createMember(username,password,nickname);
                result.put("code","vcCorrect");
                result.put("msg","注册成功");
            }catch (BussinessException bec){
     
                bec.printStackTrace();
                result.put("code",bec.getCode());
                result.put("msg",bec.getMsg());
        }
        }
        return result;
    }
    @PostMapping("/check_login")
    @ResponseBody
    public Map checkLogin(String username, String password, String vc, HttpSession session){
     
        String verifyCode=(String)session.getAttribute("kaptchaVerifyCode");
        Map result=new HashMap();
        if(vc==null || verifyCode==null ||!vc.equalsIgnoreCase(verifyCode)){
     
            result.put("code","vcMistake");
            result.put("msg","验证码错误");
        }else {
     
            try {
     
               Member member= memberService.checkLogin(username,password);
               session.setAttribute("loginMember",member);
               result.put("code","loginCorrect");
               result.put("msg","登录成功");
            }catch (BussinessException bec){
     
                bec.printStackTrace();
                result.put("code",bec.getCode());
                result.put("msg",bec.getMsg());
            }
        }
        return result;
    }
    @PostMapping("/update_read_state")
    @ResponseBody
    public Map updateReadState(Long memberId, Long bookId, Integer readState){
     
        Map result=new HashMap();
        try {
     
            memberService.updateMemberReadState(memberId,bookId,readState);
            result.put("code","updateCorrect");
            result.put("msg","更新成功");
        }catch (BussinessException bec){
     
            bec.printStackTrace();
            result.put("code",bec.getCode());
            result.put("msg",bec.getMsg());
        }
        return result;
    }
    @PostMapping("/evaluate")
    @ResponseBody
    public Map evaluate(Long memberId, Long bookId, Integer score, String content){
     
        Map result=new HashMap();
        try {
     
            Evaluation eva=memberService.evaluate(memberId,bookId,score,content);
            result.put("code","submitCorrect");
            result.put("msg","更新成功");
            result.put("evaluation",eva);
        }catch (BussinessException bec){
     
            bec.printStackTrace();
            result.put("code",bec.getCode());
            result.put("msg",bec.getMsg());
        }
        return result;
    }
    @PostMapping("/enjoy")
    @ResponseBody
    public Map enjoy(Long evaluationId){
     
        Map result=new HashMap();
        try {
     
            Evaluation evaluation=memberService.enjoy(evaluationId);
            result.put("code","enjoyCorrect");
            result.put("msg","点赞成功");
            result.put("evaluation",evaluation);
        }catch (BussinessException bec){
     
            bec.printStackTrace();
            result.put("code",bec.getCode());
            result.put("msg",bec.getMsg());
        }
        return result;
    }
}
9-(6)视图渲染(代码省略)

实现功能:
1.未登录状态下点击评价之类的按钮都会提示去登录
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第12张图片
2.注册功能
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第13张图片
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第14张图片
3.登录功能项目随笔-ssm开发移动端小说评价网(仅个人学习)_第15张图片项目随笔-ssm开发移动端小说评价网(仅个人学习)_第16张图片
4.用户阅读状态变更和点赞数量变更
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第17张图片
5.用户评论
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第18张图片
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第19张图片

10-(1)bookService中新增updateEvaluation方法
public interface BookMapper extends BaseMapper<Book> {
     
    /**
     * 更新图书评分和评价数量
     */
    public void updateEvaluation();
}
10-(2)book.xml中新增数据库操作
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaopeng.reader.mapper.BookMapper">
    <update id="updateEvaluation">
        update book b set evaluation_score=(
        select ifnull(avg(score),0) from evaluation where book_id=b.book_id
        and state='enable'),evaluation_quantity=(
        select ifnull(count(*),0) from evaluation where book_id=b.book_id
        and state='enable')
    </update>
</mapper>
10-(3)编写CaculateTask类
@Component
public class CaculateTask {
     
    @Resource
    private BookService bookService;
    @Scheduled(cron="0 * * * * ?")
    public void updateEvaluation(){
     
        bookService.updateEvaluation();
    }
}
10-(1~3)

可实现每隔1分钟(xx:xx:00时刻)调用一次book.xml中的sql语句,更新评分和评价数量

11-项目目录

项目随笔-ssm开发移动端小说评价网(仅个人学习)_第20张图片

项目随笔-ssm开发移动端小说评价网(仅个人学习)_第21张图片
项目随笔-ssm开发移动端小说评价网(仅个人学习)_第22张图片

关于图书后台管理系统增删改查的具体代码,等有空时再慢慢记录

项目随笔-ssm开发移动端小说评价网(仅个人学习)_第23张图片

小伙伴们要时刻努力自学,提高编程技术哦~!

你可能感兴趣的:(ssm,java)