<file_sep>package com.smallrain.wechat.models.user.mapper;

import org.apache.ibatis.annotations.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.smallrain.wechat.models.user.entity.SysUser;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author wangying
 * @since 2019-10-30
 */
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {

  
}
<file_sep>package com.smallrain.wechat;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class SmallrainWechatApplication {

	public static void main(String[] args) {
		SpringApplication.run(SmallrainWechatApplication.class, args);
	}
}
<file_sep>package com.smallrain.wechat.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONObject;
import com.smallrain.wechat.common.model.Response;
import com.smallrain.wechat.models.dto.DtoUtils;
import com.smallrain.wechat.models.user.entity.SysUser;
import com.smallrain.wechat.utils.ShiroUtil;

import lombok.extern.slf4j.Slf4j;
import springfox.documentation.annotations.ApiIgnore;

/**
 * 通用页面控制类
 */
@Slf4j
@ApiIgnore
@Controller
public class CommonController {
	/**
	 * 登录成功后的跳转
	 * 
	 * @return
	 */
	@GetMapping("/index")
	public ModelAndView loginSuccess() {
		SysUser currentUser = ShiroUtil.getCurrentUser();
		if (null == currentUser) {
			return new ModelAndView("redirect:/login");
		}
		log.info("当前登陆用户：{}", currentUser.getAccount());
		// 后期加上根据用户角色跳转前台页面或者后台
		// roleService.getListByUserId(userId);
		ModelAndView mv = new ModelAndView("back/index");
		mv.addObject("user", currentUser);
		return mv;
	}

	/**
	 * 未授权提示页面
	 * 
	 * @return
	 */
	@GetMapping("/unauthorized")
	public ModelAndView unauthorized() {
		ModelAndView mv = new ModelAndView("error/unauthorized");
		return mv;
	}

	/**
   * 获取实体信息
   * 
   * @return
   */
  @ResponseBody
  @GetMapping("/model/info/{modelName}")
  public Response getModelInfo(@PathVariable String modelName) {
    JSONObject info = DtoUtils.getModelInfo(modelName);
    if(null==info) {
      return Response.fail(-1, "实体信息为空，请确认名称正确");
    }
    return Response.success(info);
  }
  
}
<file_sep>package com.samllrain.wechat;

import com.smallrain.wechat.utils.BaseUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SmallrainWechatApplicationTests {

  public static void main(String[] args) {
    log.info(BaseUtils.humpToLine("hsdbchHsdhs_sdcsh"));
  }

}
<file_sep>## smalrain wechat project back
<file_sep>var menuList =	[
	{
		id: '1',
		name: '菜单管理',
		title: '菜单管理页面',
		icon: 'el-icon-menu',
		childList: [
            {
                id: '1-1',
                name: '后台菜单管理',
                title: '菜单管理-后台菜单管理',
                icon: 'el-icon-s-tools',
                url: 'back/menu',
            }
        ]
	},
	{
		id: '2',
		name: '用户-角色管理',
		title: '用户-角色管理页面',
		icon: 'el-icon-s-custom',
		childList: [
            {
                id: '2-1',
                name: '用户列表',
                title: '用户管理-用户列表',
                icon: 'el-icon-user-solid',
                url: 'back/user',
            }
        ]
	}
]<file_sep>package com.smallrain.wechat.models.role.entity;

import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author wangying
 * @since 2019-10-30
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="SysRole对象", description="")
public class SysRole extends Model<SysRole> {

    private static final long serialVersionUID = 1L;

    private String id;

    @ApiModelProperty(value = "角色名称")
    private String name;

    @ApiModelProperty(value = "角色描述")
    private String description;

    private Integer deleteFlag;


    @Override
    protected Serializable pkVal() {
        return this.id;
    }

}
<file_sep>package com.smallrain.wechat.models.menu.entity;

import java.io.Serializable;

import com.baomidou.mybatisplus.extension.activerecord.Model;

import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author wangying
 * @since 2019-10-30
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="SysMenu对象", description="")
public class SysMenu extends Model<SysMenu> {

    private static final long serialVersionUID = 1L;

    private String id;

    private String parentId;

    private String name;

    private String style;

    private String href;

    private String icon;

    private String text;

    private Integer type;

    private String permission;

    private Integer sort;

    private Integer status;


    @Override
    protected Serializable pkVal() {
        return this.id;
    }

}
<file_sep>package com.smallrain.wechat.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.smallrain.wechat.models.user.entity.SysUser;
import com.smallrain.wechat.utils.ShiroUtil;

import springfox.documentation.annotations.ApiIgnore;

/**
 * 管理后台相关控制类
 * 
 *
 */

@ApiIgnore
@Controller
@RequestMapping("/back")
public class BackManagerController {

	/**
	 * 后台管理 - 首页
	 * 
	 * @return
	 */
	@GetMapping("/main")
	public ModelAndView backMain() {
		SysUser currentUser = ShiroUtil.getCurrentUser();
		ModelAndView mv = new ModelAndView("back/main");
		mv.addObject("user", currentUser);
		return mv;
	}

	/**
	 * 后台管理 - 菜单管理
	 * 
	 * @return
	 */
	//@RequiresPermissions(value = "admin:menu")
	@GetMapping("/menu")
	public ModelAndView backMenu() {
		ModelAndView mv = new ModelAndView("back/menu");
		return mv;
	}
	
	/**
	 * 后台管理 - 菜单管理
	 * 
	 * @return
	 */
	//@RequiresPermissions(value = "admin:menu")
	@GetMapping("/user")
	public ModelAndView backUser() {
		ModelAndView mv = new ModelAndView("back/user");
		return mv;
	}

}
<file_sep>package com.smallrain.wechat.utils;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.yaml.snakeyaml.Yaml;

import com.alibaba.fastjson.JSONObject;
import com.smallrain.wechat.common.exception.SmallrainException;
import com.smallrain.wechat.models.menu.entity.SysMenu;
import com.smallrain.wechat.models.role.entity.SysRole;
import com.smallrain.wechat.models.user.entity.SysUser;

import lombok.extern.slf4j.Slf4j;

/**
 * 实体类检查工具类
 * @author wangying.dz3
 *
 */
@Slf4j
public class EntityCheckUtil {

  // 初始化实体类检查配置内存
  private static JSONObject checkConfig = new JSONObject();
  
//静态代码块，启动时加载本地配置文件
 static {
   // 获取配置文件输入流
   ClassPathResource resource = new ClassPathResource("check/beans-field-check.yml");
   try (InputStream inputStream = resource.getInputStream()){
     Yaml yaml = new Yaml();
     Object ticketsCheckConfig = yaml.load(inputStream);
     checkConfig = JSONObject.parseObject(JSONObject.toJSONString(ticketsCheckConfig));
     if (null != inputStream)
       inputStream.close();
   } catch (IOException e) {
     e.printStackTrace();
   }
 }
  
  /**
   * 检测用户对象某些必须属性
   * @param user
   * @return
   * @throws SmallrainException 
   */
  public static void userFieldCheck(SysUser user) throws SmallrainException {
    if(null == user) {
      throw new SmallrainException(601,"用户信息为空");
    }
    String check = BaseUtils.checkBeanNeededField(user, checkConfig.getJSONObject("USER"));
    if(StringUtils.isNotBlank(check)) {
      log.info("user check error:{}",check);
      throw new SmallrainException(600,check);
    }
//    if(StringUtils.isBlank(user.getAccount())) {
//      throw new SmallrainException(600,"用户信息缺少账号");
//    }
//    if(StringUtils.isBlank(user.getPassword())) {
//      throw new SmallrainException(600,"用户信息缺少密码");
//    }
//    return ;
  }
  
  /**
   * 检测角色对象某些必须属性
   * @param user
   * @return
   * @throws SmallrainException 
   */
  public static void roleFieldCheck(SysRole role) throws SmallrainException {
    if(null == role) {
      throw new SmallrainException(601,"角色信息为空");
    }
    String check = BaseUtils.checkBeanNeededField(role, checkConfig.getJSONObject("USER"));
    if(StringUtils.isNotBlank(check)) {
      throw new SmallrainException(600,check);
    }
//    if(StringUtils.isBlank(role.getName())) {
//      throw new SmallrainException(600,"角色信息名称");
//    }
    return ;
  }
  
  /**
   * 检测角色对象某些必须属性
   * @param user
   * @return
   * @throws SmallrainException 
   */
  public static void menuFieldCheck(SysMenu menu) throws SmallrainException {
    if(null == menu) {
      throw new SmallrainException(601,"角色信息为空");
    }
    String check = BaseUtils.checkBeanNeededField(menu, checkConfig.getJSONObject("USER"));
    if(StringUtils.isNotBlank(check)) {
      throw new SmallrainException(600,check);
    }
    return ;
  }
  
}
<file_sep>package com.smallrain.wechat.common;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;

import lombok.Data;

@Data
@SpringBootConfiguration
@PropertySource(value = {"classpath:auth/base-config.properties"})
@ConfigurationProperties(prefix = "base.config")
public class BaseProperties {
  
  private int tokenExpire;
  
}
<file_sep>package com.smallrain.wechat.common.shiro;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Base64Utils;

import com.smallrain.wechat.common.filter.LogoutFilter;
import com.smallrain.wechat.common.filter.RestfulFilter;
import com.smallrain.wechat.utils.AuthUtil;

import lombok.extern.slf4j.Slf4j; 

@Slf4j
@Configuration
public class ShiroConfig {

  @Resource
  private ShiroProperties shiroProperties;
  
  @Bean
  public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
      ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
      shiroFilterFactoryBean.setSecurityManager(securityManager);  // 设置 securityManager
      shiroFilterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl()); // 登录的 url
      //shiroFilterFactoryBean.setSuccessUrl(shiroProperties.getSuccessUrl());  // 登录成功后跳转的 url
      shiroFilterFactoryBean.setUnauthorizedUrl(shiroProperties.getUnauthorizedUrl());  // 未授权 url
      LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();  
      // 设置免认证 url
      String[] anonUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(shiroProperties.getAnonUrl(), ",");
      for (String url : anonUrls) {
          filterChainDefinitionMap.put(url, "anon");
      }
      // 配置退出过滤器，其中具体的退出代码 Shiro已经替我们实现了
      filterChainDefinitionMap.put(shiroProperties.getLogoutUrl(), "logout");
      // 除上以外所有 url都必须认证通过才可以访问，未通过认证自动访问 LoginUrl
      filterChainDefinitionMap.put("/**", "authc");
      shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
      //配置自定义退出过滤器 
      LogoutFilter logoutFilter = new LogoutFilter();
	    logoutFilter.setRedirectUrl("/login");
	    shiroFilterFactoryBean.getFilters().put("logout", logoutFilter);
	    //配置restFul 接口过滤器
	    RestfulFilter restfulFilter = new RestfulFilter();
	    shiroFilterFactoryBean.getFilters().put("authc", restfulFilter);
	    
      log.info("Shiro拦截器工厂类注入成功");
      return shiroFilterFactoryBean;
  }

  @Bean
  @Autowired
  public SecurityManager securityManager(SmallrainRealm smallrainRealm) {
      DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
      // 配置 SecurityManager，并注入 shiroRealm
      smallrainRealm.setCredentialsMatcher(hashedCredentialsMatcher()); // 原来在这里
      securityManager.setRealm(smallrainRealm);
      // 配置 shiro session管理器
      securityManager.setSessionManager(sessionManager());
      // 配置 rememberMeCookie
      securityManager.setRememberMeManager(rememberMeManager());
      return securityManager;
  }
  
  @Bean
  public HashedCredentialsMatcher hashedCredentialsMatcher() {
      HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
      hashedCredentialsMatcher.setHashAlgorithmName(AuthUtil.ALGORITHM_NAME); // 散列算法
      hashedCredentialsMatcher.setHashIterations(AuthUtil.HASH_ITERATIONS); // 散列次数
      return hashedCredentialsMatcher;
  }

  /**
   * rememberMe cookie 效果是重开浏览器后无需重新登录
   *
   * @return SimpleCookie
   */
  private SimpleCookie rememberMeCookie() {
      // 设置 cookie 名称，对应 login.html 页面的 <input type="checkbox" name="rememberMe"/>
      SimpleCookie cookie = new SimpleCookie("rememberMe");
      // 设置 cookie 的过期时间，单位为秒，这里为一天
      cookie.setMaxAge(shiroProperties.getCookieTimeout());
      return cookie;
  }

  /**
   * cookie管理对象
   *
   * @return CookieRememberMeManager
   */
  private CookieRememberMeManager rememberMeManager() {
      CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
      cookieRememberMeManager.setCookie(rememberMeCookie());
      // rememberMe cookie 加密的密钥
      String encryptKey = "febs_shiro_key";
      byte[] encryptKeyBytes = encryptKey.getBytes(StandardCharsets.UTF_8);
      String rememberKey = Base64Utils.encodeToString(Arrays.copyOf(encryptKeyBytes, 16));
      cookieRememberMeManager.setCipherKey(Base64.decode(rememberKey));
      return cookieRememberMeManager;
  }

  @Bean
  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
      AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
      authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
      return authorizationAttributeSourceAdvisor;
  }

  /**
   * session 管理对象
   *
   * @return DefaultWebSessionManager
   */
  @Bean
  public DefaultWebSessionManager sessionManager() {
      DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
      Collection<SessionListener> listeners = new ArrayList<>();
      listeners.add(new ShiroSessionListener());
      // 设置 session超时时间
      sessionManager.setGlobalSessionTimeout(shiroProperties.getSessionTimeout() * 1000L);
      sessionManager.setSessionListeners(listeners);
     // sessionManager.setSessionDAO(redisSessionDAO());
      sessionManager.setSessionIdUrlRewritingEnabled(false);
      return sessionManager;
  }
  
}
<file_sep>package com.smallrain.wechat.generator;

import java.util.ArrayList;
import java.util.List;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

/**
 * mybatis 代码生成器
 * 
 * @author wangying.dz3
 *
 */
public class MybatisCodeGenerator {
  
  private static final String MODEL_NAME = "";
  
  private static final String TABLES_NAME = "";   // ，分割

  public static void main(String[] args) {

    // 代码生成器
    AutoGenerator autoGenerator = new AutoGenerator();
    // set freemarker engine
    autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());

    String projectPath = System.getProperty("user.dir");
    
    // 全局配置
    GlobalConfig globalConfig = new GlobalConfig();
    globalConfig.setOutputDir(projectPath + "/src/main/java");   //代码生成目录
    globalConfig.setAuthor("wangying");
    globalConfig.setOpen(false);
    globalConfig.setSwagger2(true); // 实体属性 Swagger2 注解
    globalConfig.setFileOverride(true);
    globalConfig.setActiveRecord(true);// 不需要ActiveRecord特性的请改为false
    globalConfig.setEnableCache(false);// XML 二级缓存
    globalConfig.setBaseResultMap(true);// XML ResultMap
    globalConfig.setBaseColumnList(true);// XML columList
    globalConfig.setServiceName("%sService");
    // .setKotlin(true) 是否生成 kotlin 代码
    autoGenerator.setGlobalConfig(globalConfig);

    // 数据库配置
    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setDbType(DbType.MYSQL);
    dataSourceConfig.setUrl("jdbc:mysql://172.16.17.32:3306/template?useUnicode=true&useSSL=false&characterEncoding=utf8");
    dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
    dataSourceConfig.setUsername("template");
    dataSourceConfig.setPassword("<PASSWORD>");
    autoGenerator.setDataSource(dataSourceConfig);

    // 包配置
    PackageConfig packageConfig = new PackageConfig();
    packageConfig.setModuleName(MODEL_NAME.toLowerCase());
    packageConfig.setParent("com.smallrain.wechat.models");
    autoGenerator.setPackageInfo(packageConfig);

    // 自定义配置
    InjectionConfig injectionConfig = new InjectionConfig() {
      @Override
      public void initMap() {
        // to do nothing
      }
    };

    // 如果模板引擎是 freemarker
    String templatePath = "/templates/mapper.xml.ftl";

    // 自定义输出配置
    List<FileOutConfig> focList = new ArrayList<>();
    // 自定义配置会被优先输出
    focList.add(new FileOutConfig(templatePath) {
      @Override
      public String outputFile(TableInfo tableInfo) {
        // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
        return projectPath + "/src/main/resources/mapper/" + packageConfig.getModuleName() + "/" + tableInfo.getEntityName()
            + "Mapper" + StringPool.DOT_XML;
      }
    });
    injectionConfig.setFileOutConfigList(focList);
    autoGenerator.setCfg(injectionConfig);
    
   // 配置模板
    TemplateConfig templateConfig = new TemplateConfig();
    // 配置自定义输出模板
    //指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
//    templateConfig.setEntity("templates/entity2.java");
     templateConfig.setService("/source/service.java");
                                 //  /templates/source/controller.java.ftl
     templateConfig.setController("/source/controller.java");

    templateConfig.setXml(null);
    autoGenerator.setTemplate(templateConfig);

    
   // 策略配置
    StrategyConfig strategy = new StrategyConfig();
    //strategy.setCapitalMode(true);// 全局大写命名 ORACLE 注意
    //strategy.setTablePrefix(new String[] { "tlog_", "tsys_" });// 此处可以修改为您的表前缀
    strategy.setNaming(NamingStrategy.underline_to_camel);
    strategy.setColumnNaming(NamingStrategy.underline_to_camel);
    //strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
    strategy.setEntityLombokModel(true);
    strategy.setRestControllerStyle(true);
    strategy.setControllerMappingHyphenStyle(true);
    // 公共父类
    //strategy.setSuperControllerClass("com.smallrain.wechat.common.base.BaseController.java");
    //strategy.setSuperServiceClass("com.smallrain.wechat.common.base.BaseService.java");
    // 写于父类中的公共字段
    //strategy.setSuperEntityColumns("id");
    strategy.setInclude(TABLES_NAME.split(","));
    strategy.setTablePrefix(packageConfig.getModuleName() + "_");
    autoGenerator.setStrategy(strategy);
    autoGenerator.execute();
    
  }

}
<file_sep>package com.smallrain.wechat.models.user.entity;

import java.io.Serializable;
import java.util.Date;

import com.baomidou.mybatisplus.extension.activerecord.Model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author wangying
 * @since 2019-10-30
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="SysUser对象", description="")
public class SysUser extends Model<SysUser> {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "用户 id")
    private String id;

    @ApiModelProperty(value = "用户名")
    private String name;

    @ApiModelProperty(value = "用户账号")
    private String account;

    @ApiModelProperty(value = "用户密码")
    private String password;

    private String salt;

    @ApiModelProperty(value = "用户性别： 0- 男，1- 女")
    private Integer sex;

    @ApiModelProperty(value = "用户手机号")
    private String phone;

    @ApiModelProperty(value = "用户邮箱   ")
    private String email;

    @ApiModelProperty(value = "用户身份证号")
    private String identity;

    @ApiModelProperty(value = "头像")
    private String headImage = "";

    @ApiModelProperty(value = "用户角色ID")
    private String role;

    @ApiModelProperty(value = "注册时间")
    private Date registerTime;

    @ApiModelProperty(value = "绑定 ip ")
    private String bandIp;

    @ApiModelProperty(value = "用户真实姓名")
    private String realName;

    @ApiModelProperty(value = "签名")
    private String signature;

    @ApiModelProperty(value = "删除标记")
    private Integer status;
    
    public String getCredentialsSalt() {
        // TODO Auto-generated method stub
        return "user-credentials-salt-"+salt;
      }


    @Override
    protected Serializable pkVal() {
        return this.id;
    }

}
<file_sep>package com.smallrain.wechat.models.user.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.smallrain.wechat.common.exception.SmallrainException;
import com.smallrain.wechat.common.model.QueryParam;
import com.smallrain.wechat.models.user.entity.SysUser;

/**
 *
 * @author wangying
 * @since 2019-10-30
 */
public interface SysUserService {
  
  public IPage<SysUser> getList(QueryParam<SysUser> param) throws SmallrainException;
  
  public SysUser getOne(String id) throws SmallrainException;
  
  public SysUser add(SysUser entity) throws SmallrainException;
  
  public SysUser update(SysUser entity) throws SmallrainException;
  
  public boolean delete(String... ids) throws SmallrainException;

  public SysUser getUserByUserName(String useName) throws SmallrainException;
}
<file_sep>package com.smallrain.wechat.common.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.smallrain.wechat.common.manager.datasource.SelectDataType;

/**
 * 用于标记需要编辑的实体属性
 * @author wangying.dz3
 *
 */
//ElementType.FIELD：注解放在属性上
@Target({ ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)// 注解在运行时有效
public @interface ModelEditField {
  
  /**
   * 属性名称
   * @return
   */
  String name() default "实体属性";
  
  /**
   * 是否在添加时需要
   * @return
   */
  boolean add() default true;
  
  /**
   * 是否在修改时需要
   * @return
   */
  boolean edit() default true;
  
  /**
   * 是否在列表显示
   * @return
   */
  boolean show() default false;
  
  /**
   * 列表显示时的宽度
	 * @return
	 */
  int width() default 100;
  
  /**
   * 输入格式
   * @return
   */
  InputType type() default InputType.TEXT;
  
  /**
   * 校验规则 ，形如：type:string;message:必须为字符串,tigger:blur 参考:async-validator
   * @return
   */
  String [] validators() default {};
  
  /**
   * 选择框所需的选择项来源
   * @return
   */
  SelectDataType source() default SelectDataType.DEFAULT;
  
}
<file_sep>package com.smallrain.wechat.models.miniapp.config;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Data;

/**
 * 微信小程序先相关配置
 * 
 * @author wangying.dz3
 *
 */
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WechatMiniAppProperties {

  private List<Config> configs;

  @Data
  public static class Config {
    /**
     * 设置微信小程序的appid
     */
    private String appid;

    /**
     * 设置微信小程序的Secret
     */
    private String secret;

    /**
     * 设置微信小程序消息服务器配置的token
     */
    private String token;

    /**
     * 设置微信小程序消息服务器配置的EncodingAESKey
     */
    private String aesKey;

    /**
     * 消息格式，XML或者JSON
     */
    private String msgDataFormat;
  }

}
<file_sep>package com.smallrain.wechat.models.user.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smallrain.wechat.common.exception.SmallrainException;
import com.smallrain.wechat.common.model.QueryParam;
import com.smallrain.wechat.models.user.entity.SysUser;
import com.smallrain.wechat.models.user.mapper.SysUserMapper;
import com.smallrain.wechat.models.user.service.SysUserService;
import com.smallrain.wechat.utils.AuthUtil;
import com.smallrain.wechat.utils.BaseUtils;
import com.smallrain.wechat.utils.EntityCheckUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author wangying
 * @since 2019-10-30
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

  
	@Override
	public IPage<SysUser> getList(QueryParam<SysUser> param) throws SmallrainException {
		// TODO Auto-generated method stub
	  return this.page(param.getPage(), param.getQueryWrapper());
	}

	@Override
	public SysUser getOne(String id) throws SmallrainException {
		// TODO Auto-generated method stub
		return this.getById(id);
	}

	@Override
	public SysUser add(SysUser entity) throws SmallrainException {
		// TODO Auto-generated method stub
		log.info("添加用户：{}", entity);
		if (StringUtils.isNotBlank(entity.getId())) {
			update(entity);
		}
		entity.setId(BaseUtils.createUuid("user"));
		entity.setRegisterTime(new Date());
		entity.setStatus(0);
		EntityCheckUtil.userFieldCheck(entity);
		// 密码加密
		AuthUtil.encryptPassword(entity, true);
		this.save(entity);
		return entity;
	}

	@Override
	public SysUser update(SysUser entity) throws SmallrainException {
		// TODO Auto-generated method stub
		log.info("更新用户：{}", entity);
		if (StringUtils.isBlank(entity.getId()) || !entity.getId().startsWith("user")) {
			entity.setId(null);
			add(entity);
		}
		SysUser oldUser = getOne(entity.getId());
		if (null == oldUser) {
			log.info("不存在 id 为：{} 的用户", entity.getId());
			add(entity);
		}
		BaseUtils.copyNotNullProperties(entity, oldUser, "id", "registerTime", "bandIp");
		EntityCheckUtil.userFieldCheck(entity);
		log.info("更新用户。。");
		this.updateById(entity);
		return entity;
	}

	@Override
	public boolean delete(String... ids) throws SmallrainException {
		// TODO Auto-generated method stub
		log.info("根据Id：{} 删除用户。", String.join(",", ids));
		if (null == ids || ids.length == 0) {
			throw new SmallrainException(601, "删除用户失败，传入的用户id 为空!");
		}
		boolean result = true;
		if (ids.length == 1) {
			result = this.removeById(ids[0]);
		} else {
			List<String> idsList = new ArrayList<String>();
			for (String id : ids) {
				idsList.add(id);
			}
			result = this.removeByIds(idsList);
		}
		return result;
	}

	@Override
	public SysUser getUserByUserName(String useName) throws SmallrainException {
		// TODO Auto-generated method stub
		log.info("根据用户名：{} 获取用户信息", useName);
		if (StringUtils.isBlank(useName)) {
			throw new SmallrainException(601, "获取用户失败，用户名为空");
		}
		QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("account", useName);
		return this.getOne(queryWrapper);
	}

}
<file_sep>package com.smallrain.wechat.models.dto;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.smallrain.wechat.common.annotations.ModelEditField;
import com.smallrain.wechat.common.manager.datasource.SelectDataSourceManager;
import com.smallrain.wechat.common.manager.datasource.SelectDataType;
import com.smallrain.wechat.utils.BaseUtils;
import com.smallrain.wechat.utils.SpringUtil;

/**
 * 数据转对象工具类，用于生成实体类信息
 * @author wangying.dz3
 *
 */
public class DtoUtils {
  
  private static Map<Class<?>,String> INIT_MODELS = new HashMap<>();
  
  private static JSONObject MODELS = new JSONObject();
  
  static {
    INIT_MODELS.put(UserDto.class, "用户");
    INIT_MODELS.put(MenuDto.class, "菜单");
    
    //遍历处理
    Set<Class<?>> keys = INIT_MODELS.keySet();
    for(Class<?> key:keys) {
      if(null==key) continue;
      JSONObject obj = new JSONObject();
      String modelName = INIT_MODELS.get(key);
      obj.put("modelName", modelName);
      obj.put("beanInfo", resolveEntity(key));
      MODELS.put(key.getSimpleName().toUpperCase(), obj);
    }
  }

  public static JSONObject getModelInfo(String modelName) {
    if(StringUtils.isBlank(modelName)) return null;
    return MODELS.getJSONObject((modelName+"DTO").toUpperCase());
  }
  
  
  private static JSONObject resolveEntity(Class<?> clazz) {
    JSONObject result = new JSONObject();
    if (null == clazz)
      return result;
    List<Field> fieldsList = new ArrayList<>();
    Field[] fields = clazz.getDeclaredFields();
    fieldsList.addAll(Arrays.asList(fields));
    Class<?> superClazz = clazz.getSuperclass();
    if (superClazz != null) {
      Field[] superFields = superClazz.getDeclaredFields();
      fieldsList.addAll(Arrays.asList(superFields));
    }
    List<String> names = new ArrayList<>();
    for (Field field : fieldsList) {
      // 设置访问对象权限，保证对私有属性的访问
      field.setAccessible(true);
      ModelEditField mef = field.getAnnotation(ModelEditField.class);
      if (null == mef)
        continue; // 没有该注解，跳过
      String name = field.getName();
      names.add(field.getName());
      JSONObject mefJson = JSONObject.parseObject(JSON.toJSONString(mef));
      //处理校验规则
      if(StringUtils.isNoneBlank(mef.validators())) {
        JSONArray validatorsJson = new JSONArray();
        String [] validators = mef.validators();
        for(String validator:validators) {
          JSONObject json = new JSONObject();
          if(StringUtils.isBlank(validator)) continue;
          String [] validatorItems = validator.split(",");
          if(null!=validatorItems) {
            for(String validatorItem:validatorItems) {
              if(!validatorItem.contains(":")) continue;
              String [] items = validatorItem.split(":");
              if(null==items || items.length == 0) continue;
              if(items.length == 1) {
                json.put(validatorItem.split(":")[0], "");
              }else {
                json.put(validatorItem.split(":")[0], BaseUtils.transBoolean(validatorItem.split(":")[1])?true:validatorItem.split(":")[1]);
              }
            }
          }
          validatorsJson.add(json);
        }
        mefJson.put("validators", validatorsJson);
      }
      //处理选择数据
      if(SelectDataType.DEFAULT != mef.source()) {
        SelectDataSourceManager selectDataSourceManager = SpringUtil.getBean(SelectDataSourceManager.class);
        mefJson.put("dataList", selectDataSourceManager.getData(mef.source()));
      }
      result.put(name, mefJson);
    }
    result.put("FIELD_ITEM_LIST", names);
    return result;
  }

  
}
<file_sep>package com.smallrain.wechat.common.manager.datasource;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Service;

/**
 * 表单选择项数据源管理
 * @author wangying.dz3
 *
 */
@Service
public class SelectDataSourceManager {
  
  public List<Map<String,String>> getData(SelectDataType dataType){
    if(null == dataType) {
      return new ArrayList<>();
    }
    switch(dataType) {
      case SEX:  //性别
        return getSexDataSource();
      case DEFAULT: 
        return new ArrayList<>();
      default:
        return new ArrayList<>();
    }
  }
  
  @SuppressWarnings("serial")
  private List<Map<String,String>> getSexDataSource() {
    List<Map<String,String>> result = new ArrayList<>();
    result.add(new HashMap<String,String>() {{
      put("key","0");
      put("value","男");
    }});
    result.add(new HashMap<String,String>() {{
      put("key","1");
      put("value","女");
    }});
    return result;
  }

}
<file_sep>package com.smallrain.wechat.common.model;

import lombok.Data;

@Data
public class LoginData {
  
  private String account;
  private String password;
  private boolean rememberMe;

}
