@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Roo {

    private JetGlobalContext    context  = null;
    private Map<String, String> settings = new HashMap<>();

    public boolean existSetting(String key) {
        return settings.containsKey(key);
    }

    /**
     * 设置模板引擎上下文
     *
     * @param type
     * @param key
     * @param value
     * @return
     */
    public Roo context(Class<?> type, String key, Object value) {
        context.set(type, key, value);
        return this;
    }

    public Roo globalContext(JetGlobalContext context) {
        this.context = context;
        return this;
    }

    public String getSiteUrl() {
        return Roo.me().getSetting("site_url");
    }

    public String getSetting(String key) {
        return settings.get(key);
    }

    public String getOrDefault(String key, String defaultValue) {
        return settings.getOrDefault(key, defaultValue);
    }

    /**
     * 刷新节点
     *
     * @return
     */
    public Roo refreshNodes() {
        List<Node> parent = new Node().where("pid", 0).and("state", 1).findAll();
        List<NodeDto> nodes = parent.stream()
                .map(this::buildNodeDto)
                .collect(Collectors.toList());
        context.set(List.class, "nodes", nodes);
        return this;
    }

    private NodeDto buildNodeDto(Node p) {
        NodeDto nodeDto = new NodeDto();
        nodeDto.setTitle(p.getTitle());
        nodeDto.setSlug(p.getSlug());
        List<Node> children = new Node().where("pid", p.getId()).and("state", 1).findAll();
        nodeDto.setChildren(children);
        return nodeDto;
    }

    /**
     * 刷新配置
     *
     * @return
     */
    public Roo refreshSettings() {
        List<Setting> settings = new Setting().findAll();
        Map<String, String> map = settings.stream()
                .collect(Collectors.toMap(x -> x.getSkey(), y -> y.getSvalue()));
        this.settings = map;
        context.set("settings", map);
        return this;
    }

    public static Roo me() {
        return RooCacheEnum.SINGLE.instance;
    }

    public enum RooCacheEnum {
        SINGLE;
        Roo instance;

        RooCacheEnum() {
            instance = new Roo();
        }
    }

} public interface RooConst {

    String RELATE_DBNAME         = "db/roo_relate.db";
    String DBKEY_FOLLOW          = "follow";
    String DBKEY_FOLLOWING       = "following";
    String DBKEY_USER_FAVORITES  = "user:favorites";
    String DBKEY_TOPIC_LIKES     = "topic:likes";
    String DBKEY_TOPIC_VIEWS     = "topic:views";
    String DBKEY_TOPIC_FAVORITES = "topic:favorites";
    String DBKEY_TOPIC_GAINS     = "topic:gains";

    String SETTING_KEY_USERS    = "site_users";
    String SETTING_KEY_TOPICS   = "site_topics";
    String SETTING_KEY_COMMENTS = "site_comments";

    String LOGIN_SESSION_KEY = "login_user";
    String LOGIN_COOKIE_KEY  = "ROO_U_ID";

    // 名人名言类别
    int TIP_QUOTES    = 1;
    // 社区提示
    int TIP_COMMUNITY = 2;

    // 发帖频率
    long FREQUENCY_PUBLISH_TOPIC = 60;
    // 回复频率
    long FREQUENCY_COMMENT       = 30;
} public enum UserRole {

    GUEST(0, "游客"),
    MEMBER(1, "注册会员-所有注册用户自动属于该角色"),
    VIP(2, "VIP会员-没有特殊权限，只是一个身份象征"),
    MODERATOR(3, "版主-可以管理若干个话题下的帖子"),
    SUPER_MODERATOR(4, "超级版主-可以管理所有话题下的帖子和所有会员"),
    ADMIN(5, "管理员-享有论坛的最高权限，可以管理整个论坛，设置整个论坛的参数");

    @Getter
    private Integer id;
    @Getter
    private String  description;

    UserRole(Integer id, String description) {
        this.id = id;
        this.description = description;
    }

    public String role(){
        return this.toString().toLowerCase();
    }

} @Data
@Table(value = "roo_actived")
public class Actived extends ActiveRecord {

    private Long    id;
    private Long    uid;
    private String  username;
    private String  email;
    private String  code;
    /**
     * 激活状态
     * 0未激活 1已激活
     */
    private Integer state;
    /**
     * 创建时间
     */
    private Date    created;
    /**
     * 过期时间
     */
    private Date    expired;

} @Data
@Table(value = "roo_profile", pk = "uid")
public class Profile extends ActiveRecord {

    /**
     * 用户id
     */
    private Long    uid;
    /**
     * 用户名
     */
    private String  username;
    /**
     * 发布的帖子数
     */
    private Integer topics;
    /**
     * 评论的帖子数
     */
    private Integer comments;
    /**
     * 收藏数
     */
    private Integer favorites;
    /**
     * 粉丝数
     */
    private Integer followers;
    /**
     * 所在位置
     */
    private String  location;
    /**
     * 个人主页
     */
    private String  website;
    /**
     * github账号
     */
    private String  github;
    /**
     * 微博账号
     */
    private String  weibo;
    /**
     * 个性签名
     */
    private String  signature;

} @Table(value = "roo_setting", pk = "skey")
@Data
public class Setting extends ActiveRecord{

    /**
     * 配置键
     */
    private String  skey;
    /**
     * 配置值
     */
    private String  svalue;
    /**
     * 0禁用 1正常
     */
    private Integer state;

} @Table(value = "roo_user", pk = "uid")
@Data
public class User extends ActiveRecord {

    /**
     * 用户id
     */
    private Long    uid;
    /**
     *用户名
     */
    private String  username;
    /**
     * 用户密码
     */
    private String  password;
    /**
     * 用户邮箱
     */
    private String  email;
    /**
     *
     */
    private String  avatar;
    /**
     * 角色
     */
    private String  role;
    /**
     * 注册时间
     */
    private Date    created;
    /**
     * 最后一次操作时间
     */
    private Date    updated;
    /**
     * 最后一次登录时间
     */
    private Date    logined;
    /**
     * 用户状态 0:未激活 1:正常 2:停用 3:注销
     */
    private Integer state;

} @Data
public class SigninParam {

    @NotEmpty(message = "请输入用户名")
    private String username;

    @NotEmpty(message = "请输入密码")
    private String password;

    private String remeberMe;

} @Data
public class SignupParam {

    @NotEmpty(message = "请输入用户名")
    private String username;

    @NotEmpty(message = "请输入邮箱")
    @Email(message = "输入正确的邮箱格式")
    private String email;

    @NotEmpty(message = "请输入密码")
    private String password;

    @NotEmpty(message = "请输入确认密码")
    private String repassword;

//    @NotEmpty(message = "请输入验证码")
    private String vcode;

    private String link;

} @Slf4j
public class EmailUtils {

    private static final JetEngine       engine   = JetEngine.create();
    private static final ExecutorService executor = Executors.newFixedThreadPool(3);

    public static void init() {
        try {
            Properties props = new Properties();
            props.put("mail.smtp.annotation", "true");
            props.put("mail.smtp.ssl.enable", Roo.me().getOrDefault("mail.smtp.ssl.enable", "false"));
            props.put("mail.transport.protocol", "smtp");
            props.put("mail.smtp.timeout", Roo.me().getOrDefault("mail.smtp.timeout", "10000"));
            props.put("mail.smtp.host", Roo.me().getSetting("mail.smtp.host"));
            props.put("mail.smtp.port", Roo.me().getOrDefault("mail.smtp.host", "25"));

            OhMyEmail.config(props,
                    Roo.me().getSetting("mail.username"),
                    Roo.me().getSetting("mail.password"));
        } catch (Exception e) {
            throw new RuntimeException("Email init error.");
        }
    }

    /**
     * 异步发送注册邮件
     *
     * @param signupParam
     */
    public static void sendRegister(SignupParam signupParam) {
        executor.submit(() -> {
            JetTemplate         template = engine.getTemplate("/templates/emails/register.html");
            Map<String, Object> context  = new HashMap<>();
            context.put("username", signupParam.getUsername());
            context.put("appname", Roo.me().getOrDefault("mail.form", "Roo 社区"));
            context.put("link", signupParam.getLink());
            context.put("copyright", "Roo 社区 © 2017");

            StringWriter writer = new StringWriter();
            template.render(context, writer);
            String output = writer.toString();

            try {
                String subject = "欢迎您加入" + Roo.me().getOrDefault("mail.form", "Roo 社区");
                OhMyEmail.subject(subject)
                        .from(Roo.me().getOrDefault("mail.form", "Roo社区"))
                        .to(signupParam.getEmail())
                        .html(output)
                        .send();
            } catch (Exception e) {
                log.error("Email send error", e);
            }
        });
    }

    /**
     * 异步发送密码重置邮件
     */
    public static void sendForget(ForgetParam forgetParam) {
        executor.submit(() -> {
            JetTemplate         template = engine.getTemplate("/templates/emails/forget.html");
            Map<String, Object> context  = new HashMap<>();
            context.put("appname", Roo.me().getOrDefault("mail.form", "Roo 社区"));
            context.put("link", forgetParam.getLink());
            context.put("copyright", "Roo 社区 © 2017");

            StringWriter writer = new StringWriter();
            template.render(context, writer);
            String output = writer.toString();

            try {
                String subject = "密码重置 - " + Roo.me().getOrDefault("mail.form", "Roo 社区");
                OhMyEmail.subject(subject)
                        .from(Roo.me().getOrDefault("mail.form", "Roo社区"))
                        .to(forgetParam.getEmail())
                        .html(output)
                        .send();
            } catch (Exception e) {
                log.error("Email send error", e);
            }
        });
    }

}
import com.blade.ioc.annotation.Bean;
import com.blade.jdbc.Base;
import com.blade.kit.EncryptKit;
import com.blade.kit.UUID;
import com.blade.mvc.WebContext;
import com.blade.mvc.ui.RestResponse;
import social.roo.Roo;
import social.roo.RooConst;
import social.roo.enums.UserRole;
import social.roo.model.entity.Actived;
import social.roo.model.entity.Profile;
import social.roo.model.entity.Setting;
import social.roo.model.entity.User;
import social.roo.model.param.SigninParam;
import social.roo.model.param.SignupParam;
import social.roo.utils.EmailUtils;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
package social.roo.service;



/**
 * 账户相关Service
 * <p>
 * 登录、注册、激活、修改密码、忘记密码、找回密码
 *
 * @author biezhi
 * @date 2017/8/2
 */
@Bean
public class AccountService {

    public RestResponse<Boolean> register(SignupParam signupParam) {
        String vcode = WebContext.request().session().attribute("vcode");
//        if (!signupParam.getVcode().equals(vcode)) {
//            return RestResponse.fail("验证码输入错误");
//        }
        if (!signupParam.getPassword().equals(signupParam.getRepassword())) {
            return RestResponse.fail("密码输入不一致");
        }
        long count = new User().where("username", signupParam.getUsername())
                .and("state", 1).count();
        if (count > 0) {
            return RestResponse.fail("用户名已存在");
        }
        count = new User().where("email", signupParam.getUsername())
                .and("state", 1).count();
        if (count > 0) {
            return RestResponse.fail("邮箱已被注册");
        }

        String pwd = EncryptKit.md5(signupParam.getUsername() + signupParam.getPassword());

        LocalDateTime now = LocalDateTime.now();

        User user = new User();
        user.setUsername(signupParam.getUsername());
        user.setPassword(pwd);
        user.setEmail(signupParam.getEmail());
        user.setState(0);
        user.setCreated(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
        user.setUpdated(user.getCreated());
        user.setRole(UserRole.MEMBER.role());
        Long uid = user.save();

        String code       = UUID.UU64();
        String activeLink = Roo.me().getSiteUrl() + "/active?code=" + code;

        signupParam.setLink(activeLink);

        Actived actived = new Actived();
        actived.setCode(code);
        actived.setUid(uid);
        actived.setUsername(signupParam.getUsername());
        actived.setEmail(signupParam.getEmail());
        actived.setCreated(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
        // 有效期2小时
        actived.setExpired(Date.from(now.plusHours(2).atZone(ZoneId.systemDefault()).toInstant()));
        actived.setState(0);
        actived.save();

        // 发送注册邮件
        EmailUtils.sendRegister(signupParam);
        return RestResponse.ok();
    }

    public User getUserById(Long uid) {
        return new User().find(uid);
    }

    public RestResponse<User> login(SigninParam signinParam) {
        User user = new User();
        User u1   = user.where("username", signinParam.getUsername()).find();
        User u2   = user.where("email", signinParam.getUsername()).find();
        if (null == u1 && null == u2) {
            return RestResponse.fail("不存在该用户");
        }
        if (null != u1) {
            if (!u1.getPassword().equals(EncryptKit.md5(u1.getUsername() + signinParam.getPassword()))) {
                return RestResponse.fail("用户名或密码错误");
            }
            return RestResponse.ok(u1);
        }
        if (null != u2) {
            if (!u2.getPassword().equals(EncryptKit.md5(u2.getUsername() + signinParam.getPassword()))) {
                return RestResponse.fail("用户名或密码错误");
            }
            return RestResponse.ok(u2);
        }
        return null;
    }

    public void active(Actived actived) {
        Base.atomic(() -> {
            Actived temp = new Actived();
            temp.setState(1);
            temp.update(actived.getId());

            Profile profile = new Profile();