Browse Source

add APP api project

Alex 4 years ago
parent
commit
41e1e676c4
29 changed files with 1670 additions and 2 deletions
  1. 1 0
      pom.xml
  2. 2 2
      ruoyi-admin/src/main/resources/application.yml
  3. 123 0
      ruoyi-app/pom.xml
  4. 15 0
      ruoyi-app/src/main/java/com/ruoyi/ApiApplication.java
  5. 16 0
      ruoyi-app/src/main/java/com/ruoyi/ApiServletInitializer.java
  6. 15 0
      ruoyi-app/src/main/java/com/ruoyi/app/annotation/PassToken.java
  7. 15 0
      ruoyi-app/src/main/java/com/ruoyi/app/annotation/UserLoginToken.java
  8. 20 0
      ruoyi-app/src/main/java/com/ruoyi/app/base/TokenService.java
  9. 23 0
      ruoyi-app/src/main/java/com/ruoyi/app/config/InterceptorConfig.java
  10. 118 0
      ruoyi-app/src/main/java/com/ruoyi/app/controller/AppLoginController.java
  11. 125 0
      ruoyi-app/src/main/java/com/ruoyi/app/controller/CommonController.java
  12. 133 0
      ruoyi-app/src/main/java/com/ruoyi/app/controller/TbAppUserController.java
  13. 120 0
      ruoyi-app/src/main/java/com/ruoyi/app/core/config/SwaggerConfig.java
  14. 85 0
      ruoyi-app/src/main/java/com/ruoyi/app/interceptor/AuthenticationInterceptor.java
  15. 24 0
      ruoyi-app/src/main/java/com/ruoyi/app/interceptor/GloablExceptionHandler.java
  16. 262 0
      ruoyi-app/src/main/java/com/ruoyi/app/util/AliSMSUtil.java
  17. 1 0
      ruoyi-app/src/main/resources/META-INF/spring-devtools.properties
  18. 77 0
      ruoyi-app/src/main/resources/application-dev.yml
  19. 77 0
      ruoyi-app/src/main/resources/application-prop.yml
  20. 127 0
      ruoyi-app/src/main/resources/application.yml
  21. 2 0
      ruoyi-app/src/main/resources/banner.txt
  22. 36 0
      ruoyi-app/src/main/resources/i18n/messages.properties
  23. 94 0
      ruoyi-app/src/main/resources/logback.xml
  24. 0 0
      ruoyi-app/src/main/resources/mybatis/mybatis-config.xml
  25. 87 0
      ruoyi-system/src/main/java/com/ruoyi/app/domain/TbAppUser.java
  26. 14 0
      ruoyi-system/src/main/java/com/ruoyi/app/mapper/TbAppUserMapper.java
  27. 14 0
      ruoyi-system/src/main/java/com/ruoyi/app/service/ITbAppUserService.java
  28. 18 0
      ruoyi-system/src/main/java/com/ruoyi/app/service/impl/TbAppUserServiceImpl.java
  29. 26 0
      ruoyi-system/src/main/resources/mapper/app/TbAppUserMapper.xml

+ 1 - 0
pom.xml

@@ -214,6 +214,7 @@
         <module>ruoyi-quartz</module>
         <module>ruoyi-generator</module>
         <module>ruoyi-common</module>
+        <module>ruoyi-app</module>
     </modules>
     <packaging>pom</packaging>
 

+ 2 - 2
ruoyi-admin/src/main/resources/application.yml

@@ -9,8 +9,8 @@ ruoyi:
   # 实例演示开关
   demoEnabled: true
   # 文件路径,使用jvm系统变量,兼容windows和linux;
-#  profile: ${user.dir}/ruoyi/uploadPath
-  profile: D:/ruoyi/uploadPath
+  profile: ${user.dir}/lineage/uploadPath
+#  profile: D:/ruoyi/uploadPath
   # 获取ip地址开关
   addressEnabled: false
   # 验证码类型 math 数组计算 char 字符验证

+ 123 - 0
ruoyi-app/pom.xml

@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>lineage</artifactId>
+        <groupId>com.ruoyi</groupId>
+        <version>3.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>ruoyi-app</artifactId>
+
+
+    <description>
+        app服务入口
+    </description>
+
+    <dependencies>
+
+        <!-- spring-boot-devtools -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <optional>true</optional> <!-- 表示依赖不会传递 -->
+        </dependency>
+
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.4.0</version>
+        </dependency>
+
+        <!-- swagger2-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+        </dependency>
+
+        <!--防止进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本-->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.5.21</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>1.5.21</version>
+        </dependency>
+
+        <!-- swagger2-UI-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+
+        <!-- Mysql驱动包 -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <!-- 核心模块-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-framework</artifactId>
+        </dependency>
+
+        <!-- 定时任务-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-quartz</artifactId>
+        </dependency>
+
+        <!-- 代码生成-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-generator</artifactId>
+        </dependency>
+        <!-- 阿里云短信SDK -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>4.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
+            <version>1.1.0</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.1.RELEASE</version>
+                <configuration>
+                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.1.0</version>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                    <warName>${project.artifactId}</warName>
+                </configuration>
+            </plugin>
+        </plugins>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+</project>

+ 15 - 0
ruoyi-app/src/main/java/com/ruoyi/ApiApplication.java

@@ -0,0 +1,15 @@
+package com.ruoyi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+
+import java.util.Date;
+
+@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
+public class ApiApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(ApiApplication.class, args);
+        System.out.println("===========[AppServer started successfully]==========\nTime:"+String.format("%tc%n",new Date()));
+    }
+}

+ 16 - 0
ruoyi-app/src/main/java/com/ruoyi/ApiServletInitializer.java

@@ -0,0 +1,16 @@
+package com.ruoyi;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+/**
+ * web容器中进行部署
+ * 
+ * @author ruoyi
+ */
+public class ApiServletInitializer extends SpringBootServletInitializer {
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+        return application.sources(ApiApplication.class);
+    }
+}

+ 15 - 0
ruoyi-app/src/main/java/com/ruoyi/app/annotation/PassToken.java

@@ -0,0 +1,15 @@
+package com.ruoyi.app.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Alex
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PassToken {
+    boolean required() default true;
+}

+ 15 - 0
ruoyi-app/src/main/java/com/ruoyi/app/annotation/UserLoginToken.java

@@ -0,0 +1,15 @@
+package com.ruoyi.app.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Alex
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UserLoginToken {
+    boolean required() default true;
+}

+ 20 - 0
ruoyi-app/src/main/java/com/ruoyi/app/base/TokenService.java

@@ -0,0 +1,20 @@
+package com.ruoyi.app.base;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.ruoyi.app.domain.TbAppUser;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * @author Alex
+ */
+@Service("TokenService")
+public class TokenService {
+    public String getToken(TbAppUser user) {
+        String token="";
+        token= JWT.create().withAudience(user.getId().toString())// 将 user id 保存到 token 里面
+                .sign(Algorithm.HMAC256(user.getMobile()));// 以 手机号 作为 token 的密钥
+        return token;
+    }
+}

+ 23 - 0
ruoyi-app/src/main/java/com/ruoyi/app/config/InterceptorConfig.java

@@ -0,0 +1,23 @@
+package com.ruoyi.app.config;
+
+import com.ruoyi.app.interceptor.AuthenticationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author Alex
+ */
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(authenticationInterceptor())
+                .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
+    }
+    @Bean
+    public AuthenticationInterceptor authenticationInterceptor() {
+        return new AuthenticationInterceptor();
+    }
+}

+ 118 - 0
ruoyi-app/src/main/java/com/ruoyi/app/controller/AppLoginController.java

@@ -0,0 +1,118 @@
+package com.ruoyi.app.controller;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ruoyi.app.annotation.UserLoginToken;
+import com.ruoyi.app.base.TokenService;
+import com.ruoyi.app.domain.TbAppUser;
+import com.ruoyi.app.service.ITbAppUserService;
+import com.ruoyi.app.util.AliSMSUtil;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.StringUtils;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * 会员登录
+ *
+ * @author Alex
+ * @date 2020-09-24
+ */
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@RestController
+@RequestMapping("/app" )
+public class AppLoginController {
+
+    @Autowired
+    TokenService tokenService;
+    @Autowired
+    private ITbAppUserService userService;
+
+    /**
+     * 登录
+     */
+    @ApiOperation(value = "APP登录", notes = "APP登录")
+    @PostMapping("/login")
+    public AjaxResult login(String mobile, String captcha){
+        if (StringUtils.isBlank(mobile)) {
+            return AjaxResult.error("手机号不能为空");
+        }
+        if (StringUtils.isBlank(captcha)){
+            return AjaxResult.error("验证码不能为空");
+        }
+        //TODO 短信验证
+        //手机短信验证
+        boolean ckCaptcha = AliSMSUtil.getInstance().validateSmsCode(mobile, captcha);
+        if (!ckCaptcha) {
+            return AjaxResult.error("验证码错误");
+        }
+
+        JSONObject jsonObject = new JSONObject();
+        TbAppUser userForBase = userService.getOne(new QueryWrapper<TbAppUser>()
+            .eq("mobile",mobile)
+        );
+        if(userForBase==null){
+            return AjaxResult.error("用户不存在");
+        }
+        String token = tokenService.getToken(userForBase);
+        jsonObject.put("token", token);
+        jsonObject.put("user", userForBase);
+        return AjaxResult.success(jsonObject);
+    }
+
+
+    /**
+     * 发送短信验证码
+     * @param type 1、用户注册;2、登录确认;3、身份验证;4、登录异常;5、修改密码;6、信息变更;
+     * @param mobile 手机号码
+     * @return
+     */
+    @ApiOperation(value = "发送短信验证码", notes = "发送短信验证码")
+    @GetMapping("/captchaSend")
+    public AjaxResult sendCaptcha(Integer type, String mobile){
+        Map<String,String> map = AliSMSUtil.getInstance().sendSmsCode(type, mobile);
+        if(map.get("code").equals("1")) {
+            return AjaxResult.success("发送成功");
+        }
+        return AjaxResult.error(map.get("msg"));
+    }
+
+    /**
+     * 校验验证码
+     * @param mobile
+     * @param captcha
+     * @return
+     */
+    @ApiOperation(value = "校验短信验证码", notes = "校验短信验证码")
+    @GetMapping("/captchaValidate")
+    public AjaxResult validateCaptcha(String mobile, String captcha){
+        if (StringUtils.isBlank(mobile)) {
+            return AjaxResult.error("手机号不能为空");
+        }
+        if (StringUtils.isBlank(captcha)){
+            return AjaxResult.error("验证码不能为空");
+        }
+        //手机短信验证
+        boolean ckCaptcha = AliSMSUtil.getInstance().validateSmsCode(mobile, captcha);
+        if (!ckCaptcha) {
+            return AjaxResult.error("验证码错误");
+        }
+        return AjaxResult.success("验证成功");
+    }
+
+
+
+
+
+
+    @UserLoginToken
+    @GetMapping("/getMessage")
+    public String getMessage(){
+        return "你已通过验证";
+    }
+}

+ 125 - 0
ruoyi-app/src/main/java/com/ruoyi/app/controller/CommonController.java

@@ -0,0 +1,125 @@
+package com.ruoyi.app.controller;
+
+import com.ruoyi.common.config.RuoYiConfig;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.file.FileUploadUtils;
+import com.ruoyi.common.utils.file.FileUtils;
+import com.ruoyi.framework.config.ServerConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 通用请求处理
+ * 
+ * @author ruoyi
+ */
+@RestController
+public class CommonController {
+    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
+
+    @Autowired
+    private ServerConfig serverConfig;
+
+    /**
+     * 通用下载请求
+     * 
+     * @param fileName 文件名称
+     * @param delete 是否删除
+     */
+    @GetMapping("common/download")
+    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
+    {
+        try
+        {
+            if (!FileUtils.isValidFilename(fileName))
+            {
+                throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
+            }
+            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
+            String filePath = RuoYiConfig.getDownloadPath() + fileName;
+
+            response.setCharacterEncoding("utf-8");
+            response.setContentType("multipart/form-data");
+            response.setHeader("Content-Disposition",
+                    "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, realFileName));
+            FileUtils.writeBytes(filePath, response.getOutputStream());
+            if (delete)
+            {
+                FileUtils.deleteFile(filePath);
+            }
+        }
+        catch (Exception e)
+        {
+            log.error("下载文件失败", e);
+        }
+    }
+
+    /**
+     * 通用上传请求
+     */
+    @PostMapping("/common/upload")
+    public AjaxResult uploadFile(MultipartFile file) throws Exception {
+        try
+        {
+            // 上传文件路径
+            String filePath = RuoYiConfig.getUploadPath();
+            // 上传并返回新文件名称
+            String fileName = FileUploadUtils.upload(filePath, file);
+            String url = serverConfig.getUrl() + fileName;
+            AjaxResult ajax = AjaxResult.success();
+            ajax.put("fileName", fileName);
+            ajax.put("url", url);
+            return ajax;
+        } catch (Exception e) {
+            return AjaxResult.error(e.getMessage());
+        }
+    }
+    /**
+     * 图片上传请求
+     */
+    @PostMapping("/common/uploadImg")
+    public AjaxResult uploadFile(MultipartHttpServletRequest multiReq) throws Exception {
+        try {
+            // 上传文件路径
+            String filePath = RuoYiConfig.getUploadPath();
+            // 上传并返回新文件名称
+            String fileName = FileUploadUtils.upload(filePath, multiReq.getFile("img"));
+            String url = serverConfig.getUrl() + fileName;
+            AjaxResult ajax = AjaxResult.success();
+            ajax.put("fileName", fileName);
+            ajax.put("url", url);
+            return ajax;
+        } catch (Exception e) {
+            return AjaxResult.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 本地资源通用下载
+     */
+    @GetMapping("/common/download/resource")
+    public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception {
+        // 本地资源路径
+        String localPath = RuoYiConfig.getProfile();
+        // 数据库资源地址
+        String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX);
+        // 下载名称
+        String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
+        response.setCharacterEncoding("utf-8");
+        response.setContentType("multipart/form-data");
+        response.setHeader("Content-Disposition",
+                "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName));
+        FileUtils.writeBytes(downloadPath, response.getOutputStream());
+    }
+}

+ 133 - 0
ruoyi-app/src/main/java/com/ruoyi/app/controller/TbAppUserController.java

@@ -0,0 +1,133 @@
+package com.ruoyi.app.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+
+import java.util.List;
+import java.util.Arrays;
+
+import com.ruoyi.app.domain.TbAppUser;
+import com.ruoyi.app.service.ITbAppUserService;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 会员Controller
+ * 
+ * @author Alex
+ * @date 2020-09-24
+ */
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@RestController
+@RequestMapping("/app/user" )
+public class TbAppUserController extends BaseController {
+
+    private final ITbAppUserService iTbAppUserService;
+
+    /**
+     * 查询会员列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(TbAppUser tbAppUser)
+    {
+        startPage();
+        LambdaQueryWrapper<TbAppUser> lqw = new LambdaQueryWrapper<TbAppUser>();
+        if (tbAppUser.getFamilyId() != null){
+            lqw.eq(TbAppUser::getFamilyId ,tbAppUser.getFamilyId());
+        }
+        if (tbAppUser.getTemplateId() != null){
+            lqw.eq(TbAppUser::getTemplateId ,tbAppUser.getTemplateId());
+        }
+        if (StringUtils.isNotBlank(tbAppUser.getRole())){
+            lqw.eq(TbAppUser::getRole ,tbAppUser.getRole());
+        }
+        if (StringUtils.isNotBlank(tbAppUser.getNickName())){
+            lqw.like(TbAppUser::getNickName ,tbAppUser.getNickName());
+        }
+        if (StringUtils.isNotBlank(tbAppUser.getAvatar())){
+            lqw.eq(TbAppUser::getAvatar ,tbAppUser.getAvatar());
+        }
+        if (StringUtils.isNotBlank(tbAppUser.getQrcode())){
+            lqw.eq(TbAppUser::getQrcode ,tbAppUser.getQrcode());
+        }
+        if (tbAppUser.getMobile() != null){
+            lqw.eq(TbAppUser::getMobile ,tbAppUser.getMobile());
+        }
+        if (StringUtils.isNotBlank(tbAppUser.getOpenid())){
+            lqw.eq(TbAppUser::getOpenid ,tbAppUser.getOpenid());
+        }
+        if (StringUtils.isNotBlank(tbAppUser.getVerCode())){
+            lqw.eq(TbAppUser::getVerCode ,tbAppUser.getVerCode());
+        }
+        List<TbAppUser> list = iTbAppUserService.list(lqw);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:export')" )
+    @Log(title = "会员" , businessType = BusinessType.EXPORT)
+    @GetMapping("/export" )
+    public AjaxResult export(TbAppUser tbAppUser) {
+        LambdaQueryWrapper<TbAppUser> lqw = new LambdaQueryWrapper<TbAppUser>(tbAppUser);
+        List<TbAppUser> list = iTbAppUserService.list(lqw);
+        ExcelUtil<TbAppUser> util = new ExcelUtil<TbAppUser>(TbAppUser. class);
+        return util.exportExcel(list, "user" );
+    }
+
+    /**
+     * 获取会员详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:query')" )
+    @GetMapping(value = "/{id}" )
+    public AjaxResult getInfo(@PathVariable("id" ) Long id) {
+        return AjaxResult.success(iTbAppUserService.getById(id));
+    }
+
+    /**
+     * 新增会员
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:add')" )
+    @Log(title = "会员" , businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody TbAppUser tbAppUser) {
+        return toAjax(iTbAppUserService.save(tbAppUser) ? 1 : 0);
+    }
+
+    /**
+     * 修改会员
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:edit')" )
+    @Log(title = "会员" , businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody TbAppUser tbAppUser) {
+        return toAjax(iTbAppUserService.updateById(tbAppUser) ? 1 : 0);
+    }
+
+    /**
+     * 删除会员
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:remove')" )
+    @Log(title = "会员" , businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}" )
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(iTbAppUserService.removeByIds(Arrays.asList(ids)) ? 1 : 0);
+    }
+}

+ 120 - 0
ruoyi-app/src/main/java/com/ruoyi/app/core/config/SwaggerConfig.java

@@ -0,0 +1,120 @@
+package com.ruoyi.app.core.config;
+
+import com.ruoyi.common.config.RuoYiConfig;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Swagger2的接口配置
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig
+{
+    /** 系统基础配置 */
+    @Autowired
+    private RuoYiConfig ruoyiConfig;
+
+    /** 是否开启swagger */
+    @Value("${swagger.enabled}")
+    private boolean enabled;
+
+    /** 设置请求的统一前缀 */
+    @Value("${swagger.pathMapping}")
+    private String pathMapping;
+
+    /**
+     * 创建API
+     */
+    @Bean
+    public Docket createRestApi()
+    {
+        return new Docket(DocumentationType.SWAGGER_2)
+                // 是否启用Swagger
+                .enable(enabled)
+                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
+                .apiInfo(apiInfo())
+                // 设置哪些接口暴露给Swagger展示
+                .select()
+                // 扫描所有有注解的api,用这种方式更灵活
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                // 扫描指定包中的swagger注解
+                // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
+                // 扫描所有 .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build()
+                /* 设置安全模式,swagger可以设置访问token */
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts())
+                .pathMapping(pathMapping);
+    }
+
+    /**
+     * 安全模式,这里指定token通过Authorization头请求头传递
+     */
+    private List<ApiKey> securitySchemes()
+    {
+        List<ApiKey> apiKeyList = new ArrayList<ApiKey>();
+        apiKeyList.add(new ApiKey("token", "token", "header"));
+        return apiKeyList;
+    }
+
+    /**
+     * 安全上下文
+     */
+    private List<SecurityContext> securityContexts()
+    {
+        List<SecurityContext> securityContexts = new ArrayList<>();
+        securityContexts.add(
+                SecurityContext.builder()
+                        .securityReferences(defaultAuth())
+                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
+                        .build());
+        return securityContexts;
+    }
+
+    /**
+     * 默认的安全上引用
+     */
+    private List<SecurityReference> defaultAuth()
+    {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        List<SecurityReference> securityReferences = new ArrayList<>();
+        securityReferences.add(new SecurityReference("token", authorizationScopes));
+        return securityReferences;
+    }
+
+    /**
+     * 添加摘要信息
+     */
+    private ApiInfo apiInfo()
+    {
+        // 用ApiInfoBuilder进行定制
+        return new ApiInfoBuilder()
+                // 设置标题
+                .title("标题:传承云App_接口文档")
+                // 描述
+                .description("")
+                // 作者信息
+                .contact(new Contact(ruoyiConfig.getName(), null, null))
+                // 版本
+                .version("版本号:" + ruoyiConfig.getVersion())
+                .build();
+    }
+}

+ 85 - 0
ruoyi-app/src/main/java/com/ruoyi/app/interceptor/AuthenticationInterceptor.java

@@ -0,0 +1,85 @@
+package com.ruoyi.app.interceptor;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.ruoyi.app.annotation.PassToken;
+import com.ruoyi.app.annotation.UserLoginToken;
+import com.ruoyi.app.domain.TbAppUser;
+import com.ruoyi.app.service.ITbAppUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+
+
+/**
+ * token验证拦截
+ * @author Alex
+ */
+public class AuthenticationInterceptor implements HandlerInterceptor {
+    @Autowired
+    ITbAppUserService userService;
+    @Override
+    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
+        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
+        // 如果不是映射到方法直接通过
+        if(!(object instanceof HandlerMethod)){
+            return true;
+        }
+        HandlerMethod handlerMethod=(HandlerMethod)object;
+        Method method=handlerMethod.getMethod();
+        //检查是否有passtoken注释,有则跳过认证
+        if (method.isAnnotationPresent(PassToken.class)) {
+            PassToken passToken = method.getAnnotation(PassToken.class);
+            if (passToken.required()) {
+                return true;
+            }
+        }
+        //检查有没有需要用户权限的注解
+        if (method.isAnnotationPresent(UserLoginToken.class)) {
+            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
+            if (userLoginToken.required()) {
+                // 执行认证
+                if (token == null) {
+                    throw new RuntimeException("无token,请重新登录");
+                }
+                // 获取 token 中的 user id
+                String userId;
+                try {
+                    userId = JWT.decode(token).getAudience().get(0);
+                } catch (JWTDecodeException j) {
+                    throw new RuntimeException("401");
+                }
+                TbAppUser user = userService.getById(userId);
+                if (user == null) {
+                    throw new RuntimeException("用户不存在,请重新登录");
+                }
+                // 验证 token
+                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getMobile())).build();
+                try {
+                    jwtVerifier.verify(token);
+                } catch (JWTVerificationException e) {
+                    throw new RuntimeException("401");
+                }
+                return true;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
+
+    }
+    @Override
+    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
+
+    }
+}

+ 24 - 0
ruoyi-app/src/main/java/com/ruoyi/app/interceptor/GloablExceptionHandler.java

@@ -0,0 +1,24 @@
+package com.ruoyi.app.interceptor;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.domain.AjaxResult;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * @author Alex
+ */
+@ControllerAdvice
+public class GloablExceptionHandler {
+    @ResponseBody
+    @ExceptionHandler(Exception.class)
+    public AjaxResult handleException(Exception e) {
+        String msg = e.getMessage();
+        if (msg == null || msg.equals("")) {
+            msg = "服务器出错";
+            return AjaxResult.error(msg);
+        }
+        return AjaxResult.success(msg);
+    }
+}

+ 262 - 0
ruoyi-app/src/main/java/com/ruoyi/app/util/AliSMSUtil.java

@@ -0,0 +1,262 @@
+package com.ruoyi.app.util;
+
+
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+/**
+ * 阿里发送短信验证码工具类
+ * @author Alex
+ */
+public class AliSMSUtil {
+
+    private static final Logger logger = LoggerFactory.getLogger(AliSMSUtil.class);
+
+    private static Map<String,String> CodeMap=new HashMap<>();
+    private static Map<String,Long> CodeMapTime=new HashMap<>();
+
+    private static final long EXPIRATIONTIME=1000*60*5;//5分钟
+    private static final int START=0;//设置执行开始时间
+    private static final int INTERVAL=1000*60*5;//设置间隔执行时间 单位/毫秒  设置为一分钟
+
+
+    //产品名称:云通信短信API产品,开发者无需替换
+    static final String product = "Dysmsapi";
+    //产品域名,开发者无需替换
+    static final String domain = "dysmsapi.aliyuncs.com";
+
+    // TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
+    static final String accessKeyId = "LTAIuSBxXqyBNSCG";
+    static final String accessKeySecret = "K0QrsoGHQQFNya71vG9tmbjfOBnnFf";
+
+    private static AliSMSUtil INSTANCE;
+
+    private static String SIGN_NAME = "传承云";
+
+    //单例
+    public static AliSMSUtil getInstance(){
+        if (INSTANCE == null) {
+            synchronized (AliSMSUtil.class) {
+                if (INSTANCE == null) {
+                    INSTANCE = new AliSMSUtil();
+                }
+            }
+        }
+        return INSTANCE;
+    }
+
+    /**
+     * 发送验证码 type类型 1、用户注册;2、登录确认;3、身份验证;4、登录异常;5、修改密码;6、信息变更;phone:电话号码
+     * @param type 1、用户注册;2、登录确认;3、身份验证;4、登录异常;5、修改密码;6、信息变更;0、通用验证;
+     * @param phone 电话号码
+     * @return
+     */
+    public Map<String,String> sendSmsCode(Integer type,String phone){
+        Map<String,String> map = new HashMap<>();
+
+        String templateCode = "";
+        switch (type){
+            case 0:
+                templateCode="SMS_140736533";
+                break;
+            case 1://用户注册
+                templateCode="SMS_136825040";
+                break;
+            case 2://登录确认
+                templateCode="SMS_136825042";
+                break;
+            case 3://身份验证
+                templateCode="SMS_136825043";
+                break;
+            case 4://登录异常
+                templateCode="SMS_136825041";
+                break;
+            case 5://修改密码
+                templateCode="SMS_136825039";
+                break;
+            case 6://信息变更
+                templateCode="SMS_136825038";
+                break;
+            default:
+                templateCode="";
+                break;
+        }
+        if (templateCode.equals("")){
+            map.put("code","-1");
+            map.put("msg","验证类型不存在!");
+            return map;
+        }
+        String code = GetCode(phone);
+        String params = "{\"code\":\""+code+"\"}";
+        map = sendSms(phone,params,SIGN_NAME,templateCode);
+        return map;
+
+    }
+
+    /**
+     * 验证验证码 phone:电话号码 code:验证码
+     * @param phone
+     * @param code
+     * @return 成功返回true,
+     */
+    public Boolean validateSmsCode(String phone,String code){
+        boolean istrue=false;
+        //没有记录
+        if (CodeMap.size()<=0){
+            return false;
+        }
+        //找不到手机的记录
+        if(CodeMap.get(phone)==null){
+            return false;
+        }
+        //时间超时了
+        if((CodeMapTime.get(phone).longValue()+EXPIRATIONTIME) < new Date().getTime()){
+            CodeMap.remove(phone);
+            CodeMapTime.remove(phone);
+            return false;
+        }
+        String mapCode = CodeMap.get(phone);
+        if(mapCode.equals(code)){
+            CodeMap.remove(phone);
+            CodeMapTime.remove(phone);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 会员充值
+     * @param phone 电话号码
+     * @param store 商家名称
+     * @param money 充值金额
+     * @param newMoney 充值后会员余额
+     * @return
+     */
+    public Map<String,String> sendSmsHycz(String phone,String store,String money,String newMoney){
+        Map<String,String> map = new HashMap<>();
+        String templateCode = "SMS_143706044";
+        String params = "{\"store\":\""+store+"\",\"money\":\""+money+"\",\"newMoney\":\""+newMoney+"\"}";
+        map = sendSms(phone,params,SIGN_NAME,templateCode);
+        return map;
+    }
+
+
+
+    private Map<String,String> sendSms(String phone,String params,String signName,String templateCode){
+        Map<String,String> map = new HashMap<>();
+        //可自助调整超时时间
+        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
+        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
+
+        //初始化acsClient,暂不支持region化
+        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
+        DefaultProfile.addEndpoint("cn-hangzhou", product, domain);
+        IAcsClient acsClient = new DefaultAcsClient(profile);
+
+        //组装请求对象-具体描述见控制台-文档部分内容
+        SendSmsRequest request = new SendSmsRequest();
+        //必填:待发送手机号
+        request.setPhoneNumbers(phone);
+        //必填:短信签名-可在短信控制台中找到
+        request.setSignName(signName);
+        //必填:短信模板-可在短信控制台中找到
+        request.setTemplateCode(templateCode);
+        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
+        request.setTemplateParam(params);
+
+        //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
+        //request.setSmsUpExtendCode("90997");
+
+        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
+//        request.setOutId("yourOutId");
+
+        //hint 此处可能会抛出异常,注意catch
+        try {
+            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
+            if(sendSmsResponse!=null){
+               if(sendSmsResponse.getCode().toUpperCase().equals("OK")){
+                   map.put("code","200");
+                   map.put("msg","发送成功!");
+               }else{
+                   map.put("code","500");
+                   map.put("msg",sendSmsResponse.getMessage());
+               }
+            }else{
+                map.put("code","500");
+                map.put("msg","接口调用异常!");
+            }
+        } catch (ClientException e) {
+            e.printStackTrace();
+            map.put("code","500");
+            map.put("msg",e.getErrMsg());
+        }
+        return map;
+    }
+    /**
+     * 获取验证码
+     * @param phone
+     * @return
+     */
+    private  String GetCode(String phone){
+        String key = GetNum();
+        CodeMap.put(phone,key);
+        CodeMapTime.put(phone,new Date().getTime());
+        return key;
+    }
+
+    private  String GetNum(){
+        int number = 899999;
+        Random random = new Random();
+        int num =random.nextInt(number)+100000;
+        return num+"";
+    }
+
+    public static void main(String args[]) throws Exception {
+//       Map<String,String> map = getInstance().sendSmsCode(1,"18172065995");
+//       System.out.println(map);
+//        long tt = new Long("1000000000000");
+//            long _new = new Date().getTime()-tt;
+//            System.out.println(_new^1000001);
+//        getInstance().sendSmsHycz("18778060750","测试商家","100","1000");
+    }
+
+    static{
+        Timer tt=new Timer();//定时类
+        tt.schedule(new TimerTask(){//创建一个定时任务
+            @Override
+            public void run() {
+                try {
+                    if (CodeMapTime.size() > 0) {
+                        long nd = new Date().getTime();//获取系统时间
+                        Iterator<Map.Entry<String, Long>> entries = CodeMapTime.entrySet().iterator();
+                        while (entries.hasNext()) {
+                            Map.Entry<String, Object> entry = (Map.Entry) entries.next();
+                            String key = entry.getKey(); //获取key
+                            long value = (Long) entry.getValue(); //获取value
+                            long rt = nd - value;//获取当前时间跟存入时间的差值
+                            if (rt > EXPIRATIONTIME) {//判断时间是否已经过期  如果过期则清除key 否则不做处理
+                               CodeMap.remove(key);
+                                entries.remove();
+                            }
+                        }
+
+                    }
+
+                }catch (Exception ex){
+                    ex.printStackTrace();
+                }
+
+            }
+        }, START,INTERVAL);//从0秒开始,每隔10秒执行一次
+    }
+
+}

+ 1 - 0
ruoyi-app/src/main/resources/META-INF/spring-devtools.properties

@@ -0,0 +1 @@
+restart.include.json=/com.alibaba.fastjson.*.jar

+ 77 - 0
ruoyi-app/src/main/resources/application-dev.yml

@@ -0,0 +1,77 @@
+# 数据源配置
+spring:
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: com.mysql.cj.jdbc.Driver
+        druid:
+            # 主库数据源
+            master:
+                url: jdbc:mysql://114.215.150.32:3306/lineage?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
+                username: root
+                password: Wumao@520
+            # 从库数据源
+            slave:
+                # 从数据源开关/默认关闭
+                enabled: false
+                url: 
+                username: 
+                password: 
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1 FROM DUAL
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter: 
+                enabled: true
+            statViewServlet:
+                enabled: true
+                # 设置白名单,不填则允许所有访问
+                allow:
+                url-pattern: /druid/*
+                # 控制台管理用户名和密码
+                login-username: 
+                login-password: 
+            filter:
+                stat:
+                    enabled: true
+                    # 慢SQL记录
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true
+    # redis 配置
+    redis:
+        # 地址
+        host: 114.215.150.32
+        # 端口,默认为6379
+        port: 6379
+        # 密码
+        password: wm123456
+        # 连接超时时间
+        timeout: 10s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms

+ 77 - 0
ruoyi-app/src/main/resources/application-prop.yml

@@ -0,0 +1,77 @@
+# 数据源配置
+spring:
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: com.mysql.cj.jdbc.Driver
+        druid:
+            # 主库数据源
+            master:
+                url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
+                username: root
+                password: root
+            # 从库数据源
+            slave:
+                # 从数据源开关/默认关闭
+                enabled: false
+                url: 
+                username: 
+                password: 
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1 FROM DUAL
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter: 
+                enabled: true
+            statViewServlet:
+                enabled: true
+                # 设置白名单,不填则允许所有访问
+                allow:
+                url-pattern: /druid/*
+                # 控制台管理用户名和密码
+                login-username: 
+                login-password: 
+            filter:
+                stat:
+                    enabled: true
+                    # 慢SQL记录
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true
+    # redis 配置
+    redis:
+        # 地址
+        host: 192.168.0.222
+        # 端口,默认为6379
+        port: 6379
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 10s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms

+ 127 - 0
ruoyi-app/src/main/resources/application.yml

@@ -0,0 +1,127 @@
+# 项目相关配置
+ruoyi:
+  # 名称
+  name: Lineage
+  # 版本
+  version: 1.0.0
+  # 版权年份
+  copyrightYear: 2020
+  # 实例演示开关
+  demoEnabled: true
+  # 文件路径,使用jvm系统变量,兼容windows和linux;
+#    profile: ${user.dir}/lineage/uploadPath
+  profile: D:/lineage/uploadPath
+  # 获取ip地址开关
+  addressEnabled: false
+  # 验证码类型 math 数组计算 char 字符验证
+  captchaType: math
+
+# 开发环境配置
+server:
+  # 服务器的HTTP端口,默认为8080
+  port: 8686
+  servlet:
+    # 应用的访问路径
+    context-path: /
+  undertow:
+    # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
+    io-threads: 8
+    # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
+    worker-threads: 256
+    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
+    # 每块buffer的空间大小,越小的空间被利用越充分
+    buffer-size: 512
+    # 是否分配的直接内存
+    direct-buffers: true
+
+# 日志配置
+logging:
+  level:
+    com.ruoyi: debug
+    org.springframework: warn
+
+# Spring配置
+spring:
+  # 资源信息
+  messages:
+    # 国际化资源文件路径
+    basename: i18n/messages
+  profiles: 
+    active: dev
+  # 文件上传
+  servlet:
+     multipart:
+       # 单个文件大小
+       max-file-size:  10MB
+       # 设置总上传的文件大小
+       max-request-size:  20MB
+  # 服务模块
+  devtools:
+    restart:
+      # 热部署开关
+      enabled: true
+
+# token配置
+token:
+    # 令牌自定义标识
+    header: token
+    # 令牌密钥
+    secret: abcdefghijklmnopqrstuvwxyz
+    # 令牌有效期(默认10080分钟=7天)
+    expireTime: 10080
+  
+# MyBatis配置
+mybatis-plus:
+  mapper-locations: classpath*:mapper/**/*Mapper.xml
+  #实体扫描,多个package用逗号或者分号分隔
+  typeAliasesPackage: com.ruoyi.**.domain
+  configuration:
+    map-underscore-to-camel-case: true
+    cache-enabled: true
+  global-config:
+    banner: false
+    #刷新mapper 调试神器
+    refresh: true
+    db-config:
+      #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+      id-type: auto
+      #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+      field-strategy: not_empty
+      #驼峰下划线转换
+      db-column-underline: true
+      #数据库大写下划线转换
+      #capital-mode: true
+      #序列接口实现类配置
+      #key-generator: com.baomidou.springboot.xxx
+      #逻辑删除配置
+      logic-delete-value: 1
+      logic-not-delete-value: 0
+      #数据库类型
+      db-type: mysql
+    #自定义SQL注入器
+    #sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
+    #自定义填充策略接口实现
+    #meta-object-handler: com.baomidou.springboot.xxx
+
+# PageHelper分页插件
+pagehelper: 
+  helperDialect: mysql
+  reasonable: true
+  supportMethodsArguments: true
+  params: count=countSql 
+
+# Swagger配置
+swagger:
+  # 是否开启swagger
+  enabled: true
+  # 请求前缀
+  pathMapping: /api
+
+# 防止XSS攻击
+xss: 
+  # 过滤开关
+  enabled: true
+  # 排除链接,富文本(多个用逗号分隔)
+  excludes: /system/notice/*,/system/templatePage/*,/system/personalPage/*
+  # 匹配链接
+  urlPatterns: /system/*,/monitor/*,/tool/*

+ 2 - 0
ruoyi-app/src/main/resources/banner.txt

@@ -0,0 +1,2 @@
+Application Version: ${ruoyi.version}
+Spring Boot Version: ${spring-boot.version}

+ 36 - 0
ruoyi-app/src/main/resources/i18n/messages.properties

@@ -0,0 +1,36 @@
+#错误消息
+not.null=* 必须填写
+user.jcaptcha.error=验证码错误
+user.jcaptcha.expire=验证码已失效
+user.not.exists=用户不存在/密码错误
+user.password.not.match=用户不存在/密码错误
+user.password.retry.limit.count=密码输入错误{0}次
+user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
+user.password.delete=对不起,您的账号已被删除
+user.blocked=用户已封禁,请联系管理员
+role.blocked=角色已封禁,请联系管理员
+user.logout.success=退出成功
+
+length.not.valid=长度必须在{min}到{max}个字符之间
+
+user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
+user.password.not.valid=* 5-50个字符
+ 
+user.email.not.valid=邮箱格式错误
+user.mobile.phone.number.not.valid=手机号格式错误
+user.login.success=登录成功
+user.notfound=请重新登录
+user.forcelogout=管理员强制退出,请重新登录
+user.unknown.error=未知错误,请重新登录
+
+##文件上传消息
+upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
+upload.filename.exceed.length=上传的文件名最长{0}个字符
+
+##权限
+no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
+no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
+no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
+no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
+no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
+no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]

+ 94 - 0
ruoyi-app/src/main/resources/logback.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <property name="log.path" value="./logs"/>
+    <property name="console.log.pattern"
+              value="%blue(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
+    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${console.log.pattern}</pattern>
+            <charset>utf-8</charset>
+		</encoder>
+	</appender>
+	
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+	
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+	
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+	
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.ruoyi" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+	
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+	
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration> 

+ 0 - 0
ruoyi-app/src/main/resources/mybatis/mybatis-config.xml


+ 87 - 0
ruoyi-system/src/main/java/com/ruoyi/app/domain/TbAppUser.java

@@ -0,0 +1,87 @@
+package com.ruoyi.app.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+import com.ruoyi.common.annotation.Excel;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.io.Serializable;
+import java.util.Date;
+import java.math.BigDecimal;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 会员对象 tb_app_user
+ * @author Alex
+ * @date 2020-09-24
+ */
+@Data
+@ToString
+@EqualsAndHashCode
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName("tb_app_user")
+public class TbAppUser implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id")
+    private Long id;
+
+    /** 家族表id */
+    @Excel(name = "家族表id")
+    private Long familyId;
+
+    /** 页面模板id */
+    @Excel(name = "页面模板id")
+    private Long templateId;
+
+    /** 角色:USER未加入家族的会员,GROUP已加入家族的会员,ADMIN家族管理员 */
+    @Excel(name = "角色:USER未加入家族的会员,GROUP已加入家族的会员,ADMIN家族管理员")
+    private String role;
+
+    /** 昵称 */
+    @Excel(name = "昵称")
+    private String nickName;
+
+    /** 头像url */
+    @Excel(name = "头像url")
+    private String avatar;
+
+    /** 二维码code */
+    @Excel(name = "二维码code")
+    private String qrcode;
+
+    /** 手机号码 */
+    @Excel(name = "手机号码")
+    private String mobile;
+
+    /** openid */
+    @Excel(name = "openid")
+    private String openid;
+
+    /** $column.columnComment */
+    @Excel(name = "openid")
+    private String verCode;
+
+    /** 创建者 */
+    private String createBy;
+
+    /** 创建时间 */
+    private Date createTime;
+
+    /** 更新者 */
+    private String updateBy;
+
+    /** 更新时间 */
+    private Date updateTime;
+
+    /** 备注 */
+    @Excel(name = "备注")
+    private String remark;
+}

+ 14 - 0
ruoyi-system/src/main/java/com/ruoyi/app/mapper/TbAppUserMapper.java

@@ -0,0 +1,14 @@
+package com.ruoyi.app.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.app.domain.TbAppUser;
+
+/**
+ * 会员Mapper接口
+ *
+ * @author Alex
+ * @date 2020-09-24
+ */
+public interface TbAppUserMapper extends BaseMapper<TbAppUser> {
+
+}

+ 14 - 0
ruoyi-system/src/main/java/com/ruoyi/app/service/ITbAppUserService.java

@@ -0,0 +1,14 @@
+package com.ruoyi.app.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.app.domain.TbAppUser;
+
+/**
+ * 会员Service接口
+ *
+ * @author Alex
+ * @date 2020-09-24
+ */
+public interface ITbAppUserService extends IService<TbAppUser> {
+
+}

+ 18 - 0
ruoyi-system/src/main/java/com/ruoyi/app/service/impl/TbAppUserServiceImpl.java

@@ -0,0 +1,18 @@
+package com.ruoyi.app.service.impl;
+
+import com.ruoyi.app.domain.TbAppUser;
+import com.ruoyi.app.mapper.TbAppUserMapper;
+import com.ruoyi.app.service.ITbAppUserService;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+/**
+ * 会员Service业务层处理
+ *
+ * @author Alex
+ * @date 2020-09-24
+ */
+@Service
+public class TbAppUserServiceImpl extends ServiceImpl<TbAppUserMapper, TbAppUser> implements ITbAppUserService {
+
+}

+ 26 - 0
ruoyi-system/src/main/resources/mapper/app/TbAppUserMapper.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.app.mapper.TbAppUserMapper">
+    
+    <resultMap type="TbAppUser" id="TbAppUserResult">
+        <result property="id"    column="id"    />
+        <result property="familyId"    column="family_id"    />
+        <result property="templateId"    column="template_id"    />
+        <result property="role"    column="role"    />
+        <result property="nickName"    column="nick_name"    />
+        <result property="avatar"    column="avatar"    />
+        <result property="qrcode"    column="qrcode"    />
+        <result property="mobile"    column="mobile"    />
+        <result property="openid"    column="openid"    />
+        <result property="verCode"    column="ver_code"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+
+</mapper>