shop项目从零构建(二)

145 阅读1分钟

shiro

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.6.0</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.6.0</version>
</dependency>
package com.github.shop.config;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> pattern = new HashMap<>();
        pattern.put("/api/v1/code", "anon");
        pattern.put("/api/v1/login", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(pattern);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        securityManager.setCacheManager(new MemoryConstrainedCacheManager());
        securityManager.setSessionManager(new DefaultWebSecurityManager());
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }
}

AuthController.java

package com.github.shop.controller;


import com.github.shop.service.AuthService;
import com.github.shop.service.TelVerificationService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("/api/v1")
public class AuthController {
    private final AuthService authService;
    private final TelVerificationService telVerificationService;

    public AuthController(AuthService authService, TelVerificationService telVerificationService) {
        this.authService = authService;
        this.telVerificationService = telVerificationService;
    }

    /**
     * @api {post} /code 请求验证码
     * @apiName GetCode
     * @apiGroup 登录与授权
     *
     * @apiHeader {String} Accept application/json
     * @apiHeader {String} Content-Type application/json
     *
     * @apiParam {String} tel 手机号码
     * @apiParamExample {json} Request-Example:
     *          {
     *              "tel":"13912345678"
     *          }
     *
     * @apiSuccessExample Success-Response:
     *      HTTP/1.1 200 OK
     * @apiErrorExample Error-Response:
     *      HTTP/1.1 400 Bad Request
     *      {
     *          "message":"Bad Request"
     *      }
     */
    /**
     * @param telAndCode 电话和验证码
     * @param response HTTP response
     */
    @PostMapping("/code")
    public void code(@RequestBody TelAndCode telAndCode, HttpServletResponse response){
        if (telVerificationService.verifyTelParameter(telAndCode)) {
            authService.sendVerificationCode(telAndCode.getTel());
        } else {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
        }
    }
    @PostMapping("/login")
    public void login(){

    }

    public static class TelAndCode {
        private String tel;
        private String code;

        public TelAndCode(String tel, String code) {
            this.tel = tel;
            this.code = code;
        }

        public String getTel() {
            return tel;
        }

        public void setTel(String tel) {
            this.tel = tel;
        }

        public String getCode() {
            return code;
        }

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

文档是使用的apiDoc

 /**
     * @api {post} /code 请求验证码
     * @apiName GetCode
     * @apiGroup 登录与授权
     *
     * @apiHeader {String} Accept application/json
     * @apiHeader {String} Content-Type application/json
     *
     * @apiParam {String} tel 手机号码
     * @apiParamExample {json} Request-Example:
     *          {
     *              "tel":"13912345678"
     *          }
     *
     * @apiSuccessExample Success-Response:
     *      HTTP/1.1 200 OK
     * @apiErrorExample Error-Response:
     *      HTTP/1.1 400 Bad Request
     *      {
     *          "message":"Bad Request"
     *      }
     */
    /**
     * @param telAndCode 电话和验证码
     * @param response HTTP response
     */

主要设置:

npm install apidoc -g

mac一般会因为权限报错 加一个sudo就可以了

下载后添加apidoc.json

{
  "name": "商城项目API文档",
  "version": "0.1.0",
  "description": "A basic apiDoc example",
  "url": "http://localhost:8080/api/v1"
}

然后使用命令

apidoc -i myapp/ -o apidoc/ -t mytemplate/

示范命令:

apidoc -i /Users/zuojiabin/dev/shop -o /Users/zuojiabin/dev/shop/apidoc/

然后就可以创建好看的api文档了

AuthService.java

package com.github.shop.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AuthService {
    private final UserService userService;
    private final VerificationCodeCheckService verificationCodeCheckService;
    private final SmsCodeService smsCodeService;

    @Autowired
    public AuthService(UserService userService,
                       VerificationCodeCheckService verificationCodeCheckService,
                       SmsCodeService smsCodeService) {
        this.userService = userService;
        this.verificationCodeCheckService = verificationCodeCheckService;
        this.smsCodeService = smsCodeService;
    }

    public void sendVerificationCode(String tel) {
        userService.createUserIfNotExists(tel);
        String correctCode = smsCodeService.sendSmsCode(tel);
        verificationCodeCheckService.addCode(tel,correctCode);
    }
}

UserService.java

package com.github.shop.service;

import com.github.shop.dao.UserDao;
import com.github.shop.generate.User;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class UserService {
    private final UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public User createUserIfNotExists(String tel) {
        User user = new User();
        user.setTel(tel);
        user.setCreatedAt(new Date());
        user.setUpdatedAt(new Date());
        try {
            userDao.insertUser(user);
        } catch (PersistenceException e) {
            return userDao.getUserByTel(tel);
        }
        return user;
    }
}

SmsCodeService.java

package com.github.shop.service;

import org.springframework.stereotype.Service;

@Service
public interface SmsCodeService {
    /**
     * 向一个指定手机号码发送验证码,返回正确验证码
     * @param tel 目标手机号
     * @return 正确的验证码
     */
    String sendSmsCode(String tel);
}

MockSmsCodeService.java

package com.github.shop.service;

import org.springframework.stereotype.Service;

@Service
public class MockSmsCodeService implements SmsCodeService{
    @Override
    public String sendSmsCode(String tel) {
        return "0000";
    }
}

VerificationCodeCheckService.java

package com.github.shop.service;

import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class VerificationCodeCheckService {
    private Map<String, String> telNumberToCorrectCode = new ConcurrentHashMap<>();

    public void addCode(String tel, String correctCode) {
        telNumberToCorrectCode.put(tel, correctCode);
    }

}

UserDao.java

package com.github.shop.service;

import com.github.shop.dao.UserDao;
import com.github.shop.generate.User;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class UserService {
    private final UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public User createUserIfNotExists(String tel) {
        User user = new User();
        user.setTel(tel);
        user.setCreatedAt(new Date());
        user.setUpdatedAt(new Date());
        try {
            userDao.insertUser(user);
        } catch (PersistenceException e) {
            return userDao.getUserByTel(tel);
        }
        return user;
    }
}

TelVerificationService.java

package com.github.shop.service;

import com.github.shop.controller.AuthController;
import org.springframework.stereotype.Service;

import java.util.regex.Pattern;

@Service
public class TelVerificationService {
    private static Pattern TEL_PATTERN = Pattern.compile("1\\d{10}");

    /**
     * 验证电话的合法性 必须是以1开头的11位数字的中国大陆电话
     * @param param 输入的参数
     * @return true->电话合法 false->电话不合法
     */
    public boolean verifyTelParameter(AuthController.TelAndCode param) {
        if (param == null) {
            return false;
        } else if (param.getTel() == null) {
            return false;
        } else {
            return TEL_PATTERN.matcher(param.getTel()).find();
        }
    }
}

github.com/keda725/sho…

TelVerificationServiceTest.java

package com.github.shop.service;

import com.github.shop.controller.AuthController;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;


public class TelVerificationServiceTest {
    public static AuthController.TelAndCode VALID_PARAMETER = new AuthController.TelAndCode("12345678901", null);
    public static AuthController.TelAndCode EMPTY_TEL = new AuthController.TelAndCode(null, null);

    @Test
    public void returnTrueIfValid() {
        Assertions.assertTrue(new TelVerificationService().verifyTelParameter(VALID_PARAMETER));
    }


    @Test
    public void returnFalseIfNoTel() {
        Assertions.assertFalse(new TelVerificationService().verifyTelParameter(EMPTY_TEL));
        Assertions.assertFalse(new TelVerificationService().verifyTelParameter(null));
    }
}

CodeIntegrationTest.java

package com.github.shop;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.shop.service.TelVerificationServiceTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = ShopApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application.yml")
public class CodeIntegrationTest {
    @Autowired
    Environment environment;

    private ObjectMapper objectMapper = new ObjectMapper();

    @Test
    public void returnHttpBadRequestWhenParameterIsCorrect() throws JsonProcessingException {
        int responseCode = HttpRequest.post(getUrl("/api/v1/code"))
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .accept(MediaType.APPLICATION_JSON_VALUE)
                .send(objectMapper.writeValueAsString(TelVerificationServiceTest.EMPTY_TEL))
                .code();

        Assertions.assertEquals(HTTP_BAD_REQUEST, responseCode);
    }


    private String getUrl(String apiName) {
        // 获取集成测试的端口号
        return "http://localhost:" + environment.getProperty("local.server.port") + apiName;
    }
}
mvn verify

配置checkstyle 添加依赖

			<plugin>
				<artifactId>maven-checkstyle-plugin</artifactId>
				<version>3.1.0</version>
				<configuration>
					<configLocation>${basedir}/checkstyle.xml</configLocation>
					<includeTestSourceDirectory>true</includeTestSourceDirectory>
					<enableRulesSummary>false</enableRulesSummary>
					<excludes>**/generate/**</excludes>
				</configuration>
				<executions>
					<execution>
						<id>compile</id>
						<phase>compile</phase>
						<goals>
							<goal>check</goal>
						</goals>
					</execution>
				</executions>
				<dependencies>
					<dependency>
						<groupId>com.puppycrawl.tools</groupId>
						<artifactId>checkstyle</artifactId>
						<version>8.29</version>
					</dependency>
				</dependencies>
			</plugin>

在根目录下创建checkstyle.xml

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
<module name="Checker">
    <property name="localeLanguage" value="en"/>
    <module name="NewlineAtEndOfFile">
        <property name="lineSeparator" value="lf"/>
    </module>
    <module name="FileTabCharacter">
        <property name="fileExtensions" value="java,xml"/>
    </module>
    <module name="RegexpSingleline">
        <!-- \s matches whitespace character, $ matches end of line. -->
        <property name="format" value="\s+$"/>
        <property name="message" value="Line has trailing spaces."/>
    </module>
    <module name="TreeWalker">
        <module name="IllegalImport"/>
        <module name="RedundantImport"/>
        <module name="UnusedImports"/>
        <module name="NeedBraces"/>
        <module name="JavadocMethod">
            <property name="scope" value="public"/>
        </module>
        <module name="ModifierOrder"/>
        <module name="RedundantModifier"/>
        <module name="UpperEll"/>
        <module name="LeftCurly"/>
        <module name="NeedBraces"/>
        <module name="RightCurly"/>
        <module name="GenericWhitespace"/>
        <module name="WhitespaceAfter"/>
        <module name="NoWhitespaceBefore"/>
    </module>
</module>
mvn checkstyle:check

改掉checkstyle的问题 看到build success也是很爽的。至少是遵守一定的代码规范的约定啦。

github.com/keda725/sho…

spotbugs jacoco

			<plugin>
                <groupId>com.github.spotbugs</groupId>
                <artifactId>spotbugs-maven-plugin</artifactId>
                <version>3.1.12</version>
                <configuration>
                    <excludeFilterFile>ignore.xml</excludeFilterFile>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.github.spotbugs</groupId>
                        <artifactId>spotbugs</artifactId>
                        <version>4.0.0-beta3</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>spotbugs</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.5</version>
                <executions>
                    <execution>
                        <id>default-prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>default-report</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>default-check</id>
                        <goals>
                            <goal>check</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <rule>
                                    <element>BUNDLE</element>
                                    <limits>
                                        <limit>
                                            <counter>COMPLEXITY</counter>
                                            <value>COVEREDRATIO</value>
                                            <minimum>0.40</minimum>
                                        </limit>
                                    </limits>
                                </rule>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
    <Match>
        <Class name="~.*\.generate\..*"/>
    </Match>
</FindBugsFilter>

过滤掉spotbugs排查mybatis-generator生成的代码

因为都绑定在maven verify上 所以运行mvn verify 就可以运行jacoco checkstyle spotbugs这三个插件

target/site/jacoco/index.html在浏览器中打开这个html就可以看到jacoco的测试率报告。 就知道后面还需要做什么了