From 649d980913fc93b05b6fd47439ac0bbf87e8129e Mon Sep 17 00:00:00 2001
From: xiaoyong931011 <15274802129@163.com>
Date: Tue, 14 Dec 2021 11:53:43 +0800
Subject: [PATCH] 20211214

---
 src/main/java/cc/mrbird/febs/video/dto/LoginDto.java                           |   20 +
 pom.xml                                                                        |    7 
 src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java                      |  548 ++++++++++++++++++++++++++++
 src/main/java/cc/mrbird/febs/video/entity/VideoMemberEntity.java               |   26 +
 src/main/java/cc/mrbird/febs/common/utils/AppContants.java                     |   70 +++
 src/main/java/cc/mrbird/febs/video/controller/ApiLoginController.java          |   36 +
 src/main/java/cc/mrbird/febs/video/service/IApiVideoMemberService.java         |   14 
 src/main/java/cc/mrbird/febs/video/dto/RegisterDto.java                        |   32 +
 src/main/java/cc/mrbird/febs/video/mapper/VideoMemberMapper.java               |   15 
 src/main/java/cc/mrbird/febs/video/service/impl/ApiVideoMemberServiceImpl.java |  151 +++++++
 src/main/resources/mapper/modules/VideoMemberMapper.xml                        |   17 
 src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java          |   94 ++++
 src/main/java/cc/mrbird/febs/common/utils/ShareCodeUtil.java                   |  110 +++++
 13 files changed, 1,140 insertions(+), 0 deletions(-)

diff --git a/pom.xml b/pom.xml
index 551d1cd..0af040f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,11 +19,18 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <mybatis.plus.version>3.3.1</mybatis.plus.version>
+        <hutool.version>5.3.1</hutool.version>
         <swagger.ui>2.9.2</swagger.ui>
         <tomcat.version>9.0.31</tomcat.version>
     </properties>
 
     <dependencies>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
         <!-- Spring系列 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
diff --git a/src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java b/src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java
new file mode 100644
index 0000000..c14164d
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java
@@ -0,0 +1,94 @@
+package cc.mrbird.febs.common.interceptor;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.utils.AppContants;
+import cc.mrbird.febs.common.utils.RedisUtils;
+import cc.mrbird.febs.common.utils.SpringContextUtil;
+import cc.mrbird.febs.video.entity.VideoMemberEntity;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.RSA;
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Slf4j
+public class LoginInterceptor implements HandlerInterceptor {
+
+    private final RedisUtils redisUtils = SpringContextUtil.getBean(RedisUtils.class);
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String headerToken = request.getHeader("token");
+        if (StringUtils.isBlank(headerToken)) {
+            responseUnAuth(response);
+            return false;
+        }
+
+        String token = resolveToken(headerToken);
+
+        if (token == null || AppContants.TIME_OUT.equals(token)) {
+            responseUnAuth(response);
+            return false;
+        }
+
+        String userJsonStr = redisUtils.getString(token);
+        if (StringUtils.isBlank(userJsonStr)) {
+            responseUnAuth(response);
+            return false;
+        }
+        VideoMemberEntity member = JSON.parseObject(userJsonStr, VideoMemberEntity.class);
+
+        request.getSession().setAttribute("member", member);
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+
+    }
+
+    private void responseUnAuth(HttpServletResponse response) throws IOException {
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json; charset=utf-8");
+        response.getWriter().write(new ObjectMapper().writeValueAsString(new FebsResponse().code(HttpStatus.UNAUTHORIZED)));
+    }
+
+    private String resolveToken(String token) {
+        try {
+            RSA rsa = new RSA(AppContants.PRIVATE_KEY, null);
+            String[] tokens = StrUtil.split(rsa.decryptStr(token, KeyType.PrivateKey), "_");
+            if (verifyTokenExpired(Long.parseLong(tokens[1]))) {
+                return tokens[0];
+            } else {
+                return AppContants.TIME_OUT;
+            }
+        } catch (Exception e) {
+            log.error("#解析token异常#", e);
+            return null;
+        }
+    }
+
+    private Boolean verifyTokenExpired(Long time) {
+        boolean isDebug = true;
+        if (!isDebug) {
+            long currentTime = System.currentTimeMillis();
+            return currentTime - time <= 30000;
+        }
+        return true;
+    }
+}
+
diff --git a/src/main/java/cc/mrbird/febs/common/utils/AppContants.java b/src/main/java/cc/mrbird/febs/common/utils/AppContants.java
new file mode 100644
index 0000000..a2abac1
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/AppContants.java
@@ -0,0 +1,70 @@
+package cc.mrbird.febs.common.utils;
+
+public class AppContants {
+
+    /**
+     * 系统用户
+     */
+    public static final String SYSTEM_USER = "system";
+
+    public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzIfAF0gZs9zn9ENRtz6ocHa8MGljmMmCsjLsRvTIAilBMypMJz/VNooAOhd8GTdsWm8FNGVhRauv7RfxorFJ4Um2UbweUQBIZP2pzZMnclHxhUmYZsn/6IaPzijiUNfEjygtE7ezvso/67ecZJwqfrtlbEjqUbRgo17Qj23suwQIDAQAB";
+
+    public static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALMh8AXSBmz3Of0Q1G3PqhwdrwwaWOYyYKyMuxG9MgCKUEzKkwnP9U2igA6F3wZN2xabwU0ZWFFq6/tF/GisUnhSbZRvB5RAEhk/anNkydyUfGFSZhmyf/oho/OKOJQ18SPKC0Tt7O+yj/rt5xknCp+u2VsSOpRtGCjXtCPbey7BAgMBAAECgYAAgn+23IalJ7z0AejA2T7wLotxet+24/zPcOUVY1bxlnMtDMSHWh6mFmjL4cilMXKGqXHO4NwV+zppsCTMPXVMniI1IhfcyECgFjUrpWNCk30DlqhKePtIUCHdyLrc21mMLLMOQD/Hbga5kHZpR8r5poUAJ5Tnm5rjeyggwDj3MwJBAOnY+dyd39cRPtNLH0ANuR0Hd/WuA/RSRNbBTlXIVlc0hF1QXkgIWT2zA4uvwrFsz3F11YdPdfLgUhkFHlQuhVMCQQDEGhJELXqZ3AMlE9ykhUgv9HaCofGuCvzJnBGDKh4B3ufWG728gCNruoaRmzU8TOeVCABIQ2Un3SAenq0ylYUbAkB1y9PJm0lneAtyulPKm18VTW7TNk5No6eDmqqQMbO0iALpUpO7q2Dw4J03n1jusUYp/FaMq61ZpEAW1Go7s5d7AkEArgsJjTLj7ewDaoPfPrD/6XfJOpVqPvKHepOmQ0g9C6H/FtrWIZeEWFdamZ4ruFH08yL/xSLzg1bQ6/wecZecYwJALY4OP1Z81fhNjzg1AQd1CQQJJXUIkQpxXD/zAS5Cgf7XWfELIA4+86WA8qU1ILYHClFuV0SfxyGvI4ZEmpFosg==";
+
+    /**
+     * app用户登陆redis前缀
+     */
+    public static final String APP_LOGIN_PREFIX = "app_";
+
+    public static final String PC_LOGIN_PREFIX = "pc_";
+
+    /**
+     * token头部
+     */
+    public static final String TOKEN_HEADER = "Authorization";
+
+    /**
+     * token start with
+     */
+    public static final String TOKEN_START_WITH = "Bearer ";
+
+    /**
+     * 账号类型-手机号
+     */
+    public static final String ACCOUNT_TYPE_MOBILE = "1";
+
+    /**
+     * 账号类型-邮箱
+     */
+    public static final String ACCOUNT_TYPE_EMAIL = "2";
+
+    /**
+     * 系统推荐人id
+     */
+    public static final String SYSTEM_REFERER = "rxadr3";
+
+
+    /**
+     * 验证码前缀 手机
+     */
+    public static final String VERIFY_CODE_PREFIX = "CODE_SMS_";
+
+    /**
+     * 图片后缀
+     */
+    public static final String UPLOAD_IMAGE_SUFFIX = ".jpg";
+
+    public static final String TIME_OUT = "time_out";
+
+    public static final int DEL_FLAG_Y = 1;
+
+    public static final int DEL_FLAG_N = 2;
+
+    public static final String REDIS_ORDER_OVERTIME_PREFIX = "order_overtime_";
+    public static final String REDIS_ORDER_OVERTIME = "order_overtime_{}_{}";
+
+    public static final String AGENT_LEVEL = "AGENT_LEVEL";
+    public static final String AGENT_LEVEL_REQUIRE = "AGENT_LEVEL_REQUIRE";
+
+
+}
diff --git a/src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java b/src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java
new file mode 100644
index 0000000..7637e04
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java
@@ -0,0 +1,548 @@
+package cc.mrbird.febs.common.utils;
+
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class RedisUtils {
+
+
+    @Resource
+    private RedisTemplate<String, Object> redisTemplate;
+
+
+    // =============================common============================
+    /**
+     * 指定缓存失效时间
+     * @param key 键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    // ============================String=============================
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public String getString(String key) {
+        Object obj = key == null ? null : redisTemplate.opsForValue().get(key);
+        if(obj!=null){
+            return obj.toString();
+        }
+        return null;
+    }
+
+    /**
+     * 普通缓存放入
+     * @param key 键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(String key, Object value, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean setNotExist(String key, Object value, long time) {
+        return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 递增
+     * @param key 键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     * @param key 键
+     * @param delta 要减少几(小于0)
+     * @return
+     */
+    public long decr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+
+    // ================================Map=================================
+
+    /**
+     * HashGet
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+    /**
+     * HashSet
+
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * HashSet 并设置时间
+     * @param key 键
+     * @param map 应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 删除hash表中的值
+     * @param key 键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+    /**
+     * 判断hash表中是否有该项的值
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     * @param key 键
+     * @param item 项
+     * @param by 要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     * @param key 键
+     * @param item 项
+     * @param by 要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+    // ============================set=============================
+    /**
+     * 根据key获取Set中的所有值
+     * @param key 键
+     * @return
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+    /**
+     * 根据value从一个set中查询,是否存在
+     * @param key 键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 将数据放入set缓存
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+
+            return 0;
+        }
+    }
+    /**
+     336
+     * 将set数据放入缓存
+     337
+     * @param key 键
+     * @param time 时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0)
+                expire(key, time);
+            return count;
+
+        }catch(Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    /**
+     * 获取set缓存的长度
+     * @param key 键
+     * @return
+     */
+
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+
+    }
+
+    /**
+     * 移除值为value的
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+
+     */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    // ===============================list=================================
+    /**
+     * 获取list缓存的内容     * @param key 键
+     * @param start 开始
+     * @param end 结束 0 到 -1代表所有值
+     * @return
+     */
+    public List<Object> lGet(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+    }
+
+    /**
+     * 获取list缓存的长度
+     * @param key 键
+
+     * @return
+     */
+    public long lGetListSize(String key) {        try {
+        return redisTemplate.opsForList().size(key);
+    } catch (Exception e) {
+
+        e.printStackTrace();
+        return 0;
+    }
+    }    /**
+     * 通过索引 获取list中的值
+     * @param key 键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public Object lGetIndex(String key, long index) {
+
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 将list放入缓存
+     *
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 根据索引修改list中的某条数据
+     * @param key 键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+
+    /**
+     * 移除N个值为value
+     * @param key 键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(String key, long count, Object value) {
+        try {
+            return redisTemplate.opsForList().remove(key, count, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/common/utils/ShareCodeUtil.java b/src/main/java/cc/mrbird/febs/common/utils/ShareCodeUtil.java
new file mode 100644
index 0000000..5381938
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/ShareCodeUtil.java
@@ -0,0 +1,110 @@
+package cc.mrbird.febs.common.utils;
+
+import java.util.Random;
+
+public class ShareCodeUtil {
+
+    /**
+     * 自定义进制(0,1没有加入,容易与o,l混淆)
+     */
+//    private static final char[] r=new char[]{'q', 'w', 'e', '8', 'a', 's', '2', 'd', 'z', 'x', '9', 'c', '7', 'p', '5', 'i', 'k', '3', 'm', 'j', 'u', 'f', 'r', '4', 'v', 'y', 'l', 't', 'n', '6', 'b', 'g', 'h'};
+    private static final char[] r = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+    /**
+     * (不能与自定义进制有重复)
+     */
+    private static final char b = '0';
+
+    /**
+     * 进制长度
+     */
+    private static final int binLen = r.length;
+
+    /**
+     * 序列最小长度
+     */
+    private static final int s = 8;
+
+    /**
+     * 根据ID生成六位随机码
+     *
+     * @param id ID
+     * @return 随机码
+     */
+    public static String toSerialCode(long id) {
+        char[] buf = new char[32];
+        int charPos = 32;
+
+        while ((id / binLen) > 0) {
+            int ind = (int) (id % binLen);
+            buf[--charPos] = r[ind];
+            id /= binLen;
+        }
+        buf[--charPos] = r[(int) (id % binLen)];
+        String str = new String(buf, charPos, (32 - charPos));
+        // 不够长度的自动随机补全
+        if (str.length() < s) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(b);
+            Random rnd = new Random();
+            for (int i = 1; i < s - str.length(); i++) {
+                sb.append(r[rnd.nextInt(binLen)]);
+            }
+            str += sb.toString();
+        }
+        return str;
+    }
+
+    /**
+     * 根据ID生成六位随机码
+     *
+     * @param id ID
+     * @return 随机码
+     */
+    public static String toSerialNumberCode(long id) {
+        char[] buf = new char[32];
+        int charPos = 32;
+
+        while ((id / binLen) > 0) {
+            int ind = (int) (id % binLen);
+            buf[--charPos] = r[ind];
+            id /= binLen;
+        }
+        buf[--charPos] = r[(int) (id % binLen)];
+        String str = new String(buf, charPos, (32 - charPos));
+        // 不够长度的自动随机补全
+        if (str.length() < s) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(b);
+            Random rnd = new Random();
+            for (int i = 1; i < s - str.length(); i++) {
+                sb.append(r[rnd.nextInt(binLen)]);
+            }
+            str += sb.toString();
+        }
+        return str;
+    }
+
+    public static long codeToId(String code) {
+        char chs[] = code.toCharArray();
+        long res = 0L;
+        for (int i = 0; i < chs.length; i++) {
+            int ind = 0;
+            for (int j = 0; j < binLen; j++) {
+                if (chs[i] == r[j]) {
+                    ind = j;
+                    break;
+                }
+            }
+            if (chs[i] == b) {
+                break;
+            }
+            if (i > 0) {
+                res = res * binLen + ind;
+            } else {
+                res = ind;
+            }
+        }
+        return res;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/cc/mrbird/febs/video/controller/ApiLoginController.java b/src/main/java/cc/mrbird/febs/video/controller/ApiLoginController.java
new file mode 100644
index 0000000..e4bff9e
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/video/controller/ApiLoginController.java
@@ -0,0 +1,36 @@
+package cc.mrbird.febs.video.controller;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.video.dto.LoginDto;
+import cc.mrbird.febs.video.dto.RegisterDto;
+import cc.mrbird.febs.video.service.IApiVideoMemberService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@CrossOrigin("*")
+@RequiredArgsConstructor
+@RestController
+@RequestMapping(value = "/api/login")
+@Api(value = "ApiLoginController", tags = "登录注册类")
+public class ApiLoginController {
+
+    private final IApiVideoMemberService memberService;
+
+    @ApiOperation(value = "app注册接口", notes = "app注册接口")
+    @PostMapping(value = "/register")
+    public FebsResponse register(@RequestBody RegisterDto registerDto) {
+        return memberService.register(registerDto);
+    }
+
+
+    @ApiOperation(value = "账号密码登录接口", notes = "账号密码登录接口")
+    @PostMapping(value = "/toLogin")
+    public FebsResponse login(@RequestBody LoginDto loginDto) {
+        return memberService.toLogin(loginDto);
+    }
+
+}
diff --git a/src/main/java/cc/mrbird/febs/video/dto/LoginDto.java b/src/main/java/cc/mrbird/febs/video/dto/LoginDto.java
new file mode 100644
index 0000000..ef7e64a
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/video/dto/LoginDto.java
@@ -0,0 +1,20 @@
+package cc.mrbird.febs.video.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel(value = "LoginDto", description = "登录接口参数接收类")
+public class LoginDto {
+
+    @NotBlank(message = "账号不能为空")
+    @ApiModelProperty(value = "账号", example = "15773001234")
+    private String account;
+
+    @NotBlank(message = "密码不能为空")
+    @ApiModelProperty(value = "密码", example = "123456")
+    private String password;
+}
diff --git a/src/main/java/cc/mrbird/febs/video/dto/RegisterDto.java b/src/main/java/cc/mrbird/febs/video/dto/RegisterDto.java
new file mode 100644
index 0000000..c2d6a1f
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/video/dto/RegisterDto.java
@@ -0,0 +1,32 @@
+package cc.mrbird.febs.video.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel(value = "RegisterDto", description = "用户注册参数接收类")
+public class RegisterDto {
+
+    @NotBlank(message = "账号不能为空")
+    @ApiModelProperty(value = "手机号", example = "15773001234")
+    private String account;
+
+    @NotBlank(message = "密码不能为空")
+    @ApiModelProperty(value = "密码", example = "123456")
+    private String password;
+
+    @ApiModelProperty(value = "类型 1-手机号 2-邮箱", example = "1")
+    private String type = "1";
+
+//    @NotBlank(message = "验证码不能为空")
+//    @ApiModelProperty(value = "验证码", example = "123456")
+//    private String code;
+
+//    @NotBlank(message = "邀请码不能为空")
+//    @ApiModelProperty(value = "邀请码")
+//    private String inviteId;
+
+}
diff --git a/src/main/java/cc/mrbird/febs/video/entity/VideoMemberEntity.java b/src/main/java/cc/mrbird/febs/video/entity/VideoMemberEntity.java
index db571e2..c018e54 100644
--- a/src/main/java/cc/mrbird/febs/video/entity/VideoMemberEntity.java
+++ b/src/main/java/cc/mrbird/febs/video/entity/VideoMemberEntity.java
@@ -59,12 +59,38 @@
     private Integer isVip;
 
     /**
+     * 是
+     */
+    public static final Integer ISVIP_STATUS_YES = 1;
+    /**
+     * 否
+     */
+    public static final Integer ISVIP_STATUS_NO = 2;
+
+    /**
      * 账号状态 1/正常 2/禁用
      */
     private Integer accountStatus;
+    /**
+     * 启用
+     */
+    public static final Integer ACCOUNT_STATUS_ENABLE = 1;
+    /**
+     * 禁用
+     */
+    public static final Integer ACCOUNT_STATUS_DISABLED = 2;
 
     /**
      * 账号类型 1/正常 2/测试
      */
     private Integer accountType;
+
+    /**
+     * 正常账号
+     */
+    public static final Integer ACCOUNT_TYPE_NORMAL = 1;
+    /**
+     * 测试账号
+     */
+    public static final Integer ACCOUNT_TYPE_TEST = 2;
 }
diff --git a/src/main/java/cc/mrbird/febs/video/mapper/VideoMemberMapper.java b/src/main/java/cc/mrbird/febs/video/mapper/VideoMemberMapper.java
new file mode 100644
index 0000000..1ac1464
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/video/mapper/VideoMemberMapper.java
@@ -0,0 +1,15 @@
+package cc.mrbird.febs.video.mapper;
+
+import cc.mrbird.febs.video.entity.VideoMemberEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+public interface VideoMemberMapper extends BaseMapper<VideoMemberEntity> {
+
+    VideoMemberEntity selectInfoByAccount(@Param("account") String account);
+
+    VideoMemberEntity selectInfoByInviteId(@Param("inviteId")String parentId);
+
+    VideoMemberEntity selectInfoByAccountAndPwd(@Param("account") String account, @Param("password") String password);
+
+}
diff --git a/src/main/java/cc/mrbird/febs/video/service/IApiVideoMemberService.java b/src/main/java/cc/mrbird/febs/video/service/IApiVideoMemberService.java
new file mode 100644
index 0000000..36a707d
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/video/service/IApiVideoMemberService.java
@@ -0,0 +1,14 @@
+package cc.mrbird.febs.video.service;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.video.dto.LoginDto;
+import cc.mrbird.febs.video.dto.RegisterDto;
+import cc.mrbird.febs.video.entity.VideoMemberEntity;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+public interface IApiVideoMemberService extends IService<VideoMemberEntity> {
+
+    FebsResponse register(RegisterDto registerDto);
+
+    FebsResponse toLogin(LoginDto loginDto);
+}
diff --git a/src/main/java/cc/mrbird/febs/video/service/impl/ApiVideoMemberServiceImpl.java b/src/main/java/cc/mrbird/febs/video/service/impl/ApiVideoMemberServiceImpl.java
new file mode 100644
index 0000000..57c6e7e
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/video/service/impl/ApiVideoMemberServiceImpl.java
@@ -0,0 +1,151 @@
+package cc.mrbird.febs.video.service.impl;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.exception.FebsException;
+import cc.mrbird.febs.common.utils.AppContants;
+import cc.mrbird.febs.common.utils.RedisUtils;
+import cc.mrbird.febs.common.utils.ShareCodeUtil;
+import cc.mrbird.febs.video.dto.LoginDto;
+import cc.mrbird.febs.video.dto.RegisterDto;
+import cc.mrbird.febs.video.entity.VideoMemberEntity;
+import cc.mrbird.febs.video.mapper.VideoMemberMapper;
+import cc.mrbird.febs.video.service.IApiVideoMemberService;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.RSA;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ApiVideoMemberServiceImpl extends ServiceImpl<VideoMemberMapper, VideoMemberEntity> implements IApiVideoMemberService {
+
+    private final RedisUtils redisUtils;
+
+    @Override
+    public FebsResponse register(RegisterDto registerDto) {
+        VideoMemberEntity videoMember = this.baseMapper.selectInfoByAccount(registerDto.getAccount());
+        if (videoMember != null) {
+            throw new FebsException("该账号已被占用");
+        }
+
+//        String account = registerDto.getAccount();
+//        String code = registerDto.getCode();
+//        boolean flags = commonService.verifyCode(account, code);
+//        if(!flags) {
+//            throw new FebsException("验证码错误");
+//        }
+
+        videoMember = new VideoMemberEntity();
+        videoMember.setPassword(SecureUtil.md5(registerDto.getPassword()));
+
+        // 判断账号类型
+        if (AppContants.ACCOUNT_TYPE_MOBILE.equals(registerDto.getType())) {
+            videoMember.setPhone(registerDto.getAccount());
+        } else {
+            videoMember.setEmail(registerDto.getAccount());
+        }
+
+//        Integer count = this.baseMapper.selectCount(null);
+//        if (count != null && count != 0) {
+//            MallMember inviteMember = this.baseMapper.selectInfoByInviteId(registerDto.getInviteId());
+//            if (inviteMember == null) {
+//                throw new FebsException("邀请码不存在");
+//            }
+//
+//            mallMember.setReferrerId(registerDto.getInviteId());
+//
+//        }
+        videoMember.setName(registerDto.getAccount());
+        videoMember.setAccountStatus(VideoMemberEntity.ACCOUNT_STATUS_ENABLE);
+        videoMember.setAccountType(VideoMemberEntity.ACCOUNT_TYPE_NORMAL);
+        videoMember.setIsVip(VideoMemberEntity.ISVIP_STATUS_NO);
+
+        this.baseMapper.insert(videoMember);
+
+        String inviteId = ShareCodeUtil.toSerialCode(videoMember.getId());
+        videoMember.setInviteId(inviteId);
+
+        //推荐人和推荐人链
+        boolean flag = false;
+        String parentId = videoMember.getRefererId();
+        if (StrUtil.isBlank(parentId)) {
+            flag = true;
+        }
+        String ids = "";
+        while (!flag) {
+            if (StrUtil.isBlank(ids)) {
+                ids += parentId;
+            } else {
+                ids += ("," + parentId);
+            }
+            VideoMemberEntity parentMember = this.baseMapper.selectInfoByInviteId(parentId);
+            if (parentMember == null) {
+                break;
+            }
+            parentId = parentMember.getRefererId();
+            if (StrUtil.isBlank(parentMember.getRefererId())) {
+                flag = true;
+            }
+        }
+
+        if (StrUtil.isNotBlank(ids)) {
+            videoMember.setRefererIds(ids);
+        }
+        this.baseMapper.updateById(videoMember);
+
+//        MallMemberWallet wallet = new MallMemberWallet();
+//        wallet.setBalance(BigDecimal.ZERO);
+//        wallet.setMemberId(mallMember.getId());
+//        mallMemberWalletMapper.insert(wallet);
+        return new FebsResponse().success().message("注册成功");
+    }
+
+    @Override
+    public FebsResponse toLogin(LoginDto loginDto) {
+        String md5Pwd = SecureUtil.md5(loginDto.getPassword());
+
+        VideoMemberEntity videoMember = this.baseMapper.selectInfoByAccountAndPwd(loginDto.getAccount(), md5Pwd);
+        if (videoMember == null) {
+            throw new FebsException("用户不存在或账号密码错误");
+        }
+
+        if (videoMember.ACCOUNT_STATUS_DISABLED.equals(videoMember.getAccountStatus())) {
+            throw new FebsException("该账号存在异常, 暂限制登录");
+        }
+
+        String redisKey = AppContants.APP_LOGIN_PREFIX + videoMember.getId();
+        String existToken = redisUtils.getString(redisKey);
+        if (StrUtil.isNotBlank(existToken)) {
+            Object o = redisUtils.get(existToken);
+            if (ObjectUtil.isNotEmpty(o)) {
+                redisUtils.del(existToken);
+            }
+        }
+
+        String token = IdUtil.simpleUUID();
+        redisUtils.set(token, JSONObject.toJSONString(videoMember), 360000);
+        redisUtils.set(redisKey, token, 360000);
+        Map<String, Object> authInfo = new HashMap<>();
+        authInfo.put("token", token);
+        authInfo.put("rasToken", generateAsaToken(token));
+        return new FebsResponse().success().data(authInfo);
+    }
+
+
+    public String generateAsaToken(String token) {
+        RSA rsa = new RSA(null, AppContants.PUBLIC_KEY);
+        return rsa.encryptBase64(token + "_" + System.currentTimeMillis(), KeyType.PublicKey);
+    }
+}
diff --git a/src/main/resources/mapper/modules/VideoMemberMapper.xml b/src/main/resources/mapper/modules/VideoMemberMapper.xml
new file mode 100644
index 0000000..aa36d31
--- /dev/null
+++ b/src/main/resources/mapper/modules/VideoMemberMapper.xml
@@ -0,0 +1,17 @@
+<?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="cc.mrbird.febs.video.mapper.VideoMemberMapper">
+
+    <select id="selectInfoByAccount" resultType="cc.mrbird.febs.video.entity.VideoMemberEntity">
+        select * from video_member where phone=#{account} or email=#{account} or invite_id=#{account}
+    </select>
+
+    <select id="selectInfoByInviteId" resultType="cc.mrbird.febs.video.entity.VideoMemberEntity">
+        select * from video_member where invite_id=#{inviteId}
+    </select>
+
+    <select id="selectInfoByAccountAndPwd" resultType="cc.mrbird.febs.video.entity.VideoMemberEntity">
+        select * from mall_member where (phone=#{account} or email=#{account}) and password=#{password}
+    </select>
+
+</mapper>
\ No newline at end of file

--
Gitblit v1.9.1