Explorar o código

tmp(权限系统): 权限管理
1. 权限管理

kindring hai 1 ano
pai
achega
16c608bb72
Modificáronse 28 ficheiros con 966 adicións e 9 borrados
  1. 7 0
      pom.xml
  2. 2 0
      src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
  3. 40 0
      src/main/java/com/genersoft/iot/vmp/conf/security/StpInterfaceImpl.java
  4. 15 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
  5. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
  6. 57 0
      src/main/java/com/genersoft/iot/vmp/service/IAccountService.java
  7. 7 0
      src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java
  8. 76 0
      src/main/java/com/genersoft/iot/vmp/service/impl/AccountServiceImpl.java
  9. 5 0
      src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
  10. 0 1
      src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
  11. 51 0
      src/main/java/com/genersoft/iot/vmp/storager/dao/AccountMapper.java
  12. 5 0
      src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
  13. 70 0
      src/main/java/com/genersoft/iot/vmp/storager/dao/dto/UserAccount.java
  14. 51 0
      src/main/java/com/genersoft/iot/vmp/storager/dao/dto/deviceBind.java
  15. 4 0
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/FailResult.java
  16. 5 0
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java
  17. 114 0
      src/main/java/com/genersoft/iot/vmp/vmanager/user/AccountController.java
  18. 12 0
      src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
  19. 17 0
      src/main/resources/application.yml
  20. 1 0
      web_src/src/assets/base.css
  21. 1 1
      web_src/src/components/com/preset.vue
  22. 282 0
      web_src/src/pages/account/AccountRegister.vue
  23. 20 0
      web_src/src/pages/account/index.html
  24. 16 0
      web_src/src/pages/account/main.js
  25. 1 0
      web_src/src/pages/index/main.js
  26. 39 0
      web_src/src/until/FieldCheck.js
  27. 11 6
      web_src/vue.config.js
  28. 56 0
      设备绑定机制.md

+ 7 - 0
pom.xml

@@ -252,6 +252,13 @@
 			<version>3.1.1</version>
 		</dependency>
 
+<!--		sa-Token 权限验证-->
+		<dependency>
+			<groupId>cn.dev33</groupId>
+			<artifactId>sa-token-spring-boot-starter</artifactId>
+			<version>1.37.0</version>
+		</dependency>
+
 		<!-- 获取系统信息 -->
 		<dependency>
 			<groupId>com.github.oshi</groupId>

+ 2 - 0
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java

@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp;
 
+import cn.dev33.satoken.SaManager;
 import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.utils.GitUtil;
@@ -45,6 +46,7 @@ public class VManageBootstrap extends SpringBootServletInitializer {
 		logger.info("构建版本: {}", gitUtil1.getBuildVersion());
 		logger.info("构建时间: {}", gitUtil1.getBuildDate());
 		logger.info("GIT最后提交时间: {}", gitUtil1.getCommitTime());
+		logger.info("SaManager.getConfig: {}", SaManager.getConfig());
 	}
 	// 项目重启
 	public static void restart() {

+ 40 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/StpInterfaceImpl.java

@@ -0,0 +1,40 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import cn.dev33.satoken.stp.StpInterface;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 自定义权限验证接口扩展
+ */
+@Component    // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
+public class StpInterfaceImpl implements StpInterface {
+    /**
+     * 返回一个账号所拥有的权限码集合
+     */
+    @Override
+    public List<String> getPermissionList(Object loginId, String loginKey) {
+        // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
+        List<String> list = new ArrayList<>();
+        list.add("101");
+        list.add("user-add");
+        list.add("user-delete");
+        list.add("user-update");
+        list.add("user-get");
+        list.add("article-get");
+        return list;
+    }
+    /**
+     * 返回一个账号所拥有的角色标识集合
+     */
+    @Override
+    public List<String> getRoleList(Object loginId, String loginKey) {
+        // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
+        List<String> list = new ArrayList<>();
+        list.add("admin");
+        list.add("super-admin");
+        return list;
+    }
+}

+ 15 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java

@@ -10,6 +10,12 @@ import io.swagger.v3.oas.annotations.media.Schema;
 @Schema(description = "国标设备/平台")
 public class Device {
 
+	/**
+	 * 设备表id, 用于绑定设备
+	 */
+	@Schema(description = "设备id, 数据库Id")
+	private int id;
+
 	/**
 	 * 设备国标编号
 	 */
@@ -214,6 +220,7 @@ public class Device {
 	@Schema(description = "分享中的播放通道")
 	private String playChannel;
 
+
 	public String getDeviceId() {
 		return deviceId;
 	}
@@ -516,4 +523,12 @@ public class Device {
 	public void setPlayChannel(String playChannel) {
 		this.playChannel = playChannel;
 	}
+
+	public int getId() {
+		return id;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java

@@ -49,7 +49,7 @@ public class SIPSender {
     public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
         try {
             ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
-            logger.info("[server ---> device] ip:{} \n{}",ip,message);
+            logger.info("[server ---> device] transmitRequest ip:{} \n{}",ip,message);
             String transport = "UDP";
             if (viaHeader == null) {
                 logger.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据");

+ 57 - 0
src/main/java/com/genersoft/iot/vmp/service/IAccountService.java

@@ -0,0 +1,57 @@
+package com.genersoft.iot.vmp.service;
+
+import com.genersoft.iot.vmp.storager.dao.dto.UserAccount;
+
+public interface IAccountService {
+
+    /**
+     * 注册账号
+     * @param name
+     * @param account
+     * @param password
+     * @return
+     */
+    boolean registerAccount(String name, String account, String password);
+
+    /**
+     * 检查账号是否允许注册
+     * @param account
+     * @return
+     */
+    UserAccount checkAccount(String account);
+
+    /**
+     * 登录账号
+     * @param account
+     * @param password
+     * @return
+     */
+    UserAccount login(String account, String password);
+
+    /**
+     * 绑定设备
+     * @param id
+     * @param devId
+     * @param devCode
+     * @return
+     */
+    int bindDevice(int id, int devId, String devCode);
+
+    /**
+     * 解绑设备
+     * @param userId
+     * @param devId
+     * @return
+     */
+    int unBindDevice(int userId, int devId);
+
+    /**
+     * 检查绑定码是否能够被绑定, 能够绑定则 允许 true
+     * @param bindCode
+     * @return
+     */
+    Boolean checkIsAllowBind(String bindCode);
+
+
+
+}

+ 7 - 0
src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java

@@ -167,4 +167,11 @@ public interface IDeviceService {
      * 获取所有设备
      */
     List<Device> getAll();
+
+    /**
+     * 获取能够绑定的设备
+     * @param bindCode 绑定码
+     * @return
+     */
+    Device getBindDevice(String bindCode);
 }

+ 76 - 0
src/main/java/com/genersoft/iot/vmp/service/impl/AccountServiceImpl.java

@@ -0,0 +1,76 @@
+package com.genersoft.iot.vmp.service.impl;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.service.IAccountService;
+import com.genersoft.iot.vmp.storager.dao.AccountMapper;
+import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
+import com.genersoft.iot.vmp.storager.dao.dto.UserAccount;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AccountServiceImpl implements IAccountService {
+
+    @Autowired
+    private AccountMapper accountMapper;
+
+    private DeviceMapper deviceMapper;
+
+    /**
+     * 注册设备
+     * @param name
+     * @param account
+     * @param password
+     * @return
+     */
+    public boolean registerAccount(String name, String account, String password) {
+       UserAccount userAccount = accountMapper.findByAccount(account);
+       if (userAccount != null) {
+           return false;
+       }
+       // 注册设备
+       return accountMapper.register(name, account, password) > 0;
+    }
+
+    /**
+     * 获取设备
+     * @param account
+     * @return
+     */
+    public UserAccount checkAccount(String account) {
+        return accountMapper.findByAccount(account);
+    }
+
+    /**
+     * 登录
+     * @param account
+     * @param password
+     * @return
+     */
+    public UserAccount login(String account, String password) {
+        return accountMapper.login(account, password);
+    }
+
+    public int bindDevice(int id, int devId, String devCode)
+    {
+        return accountMapper.bindDevice(id, devId, devCode);
+    }
+
+    public int unBindDevice(int userId, int devId)
+    {
+        return accountMapper.unBindDevice(userId, devId);
+    }
+
+
+    public Boolean checkIsAllowBind(String bindCode)
+    {
+        if (deviceMapper.getBindDevice(bindCode) == null){
+            return false;
+        }
+        Device device = accountMapper.findDeviceByDevCode(bindCode);
+        return device == null;
+    }
+
+
+
+}

+ 5 - 0
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java

@@ -684,4 +684,9 @@ public class DeviceServiceImpl implements IDeviceService {
     public List<Device> getAll() {
         return deviceMapper.getAll();
     }
+
+
+    public Device getBindDevice(String bindCode) {
+        return deviceMapper.getBindDevice(bindCode);
+    }
 }

+ 0 - 1
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java

@@ -32,7 +32,6 @@ public interface IRedisCatchStorage {
      */
     boolean startPlay(StreamInfo stream);
 
-
     /**
      * 停止播放时删除
      *

+ 51 - 0
src/main/java/com/genersoft/iot/vmp/storager/dao/AccountMapper.java

@@ -0,0 +1,51 @@
+package com.genersoft.iot.vmp.storager.dao;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.storager.dao.dto.UserAccount;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Mapper
+@Repository
+public interface AccountMapper {
+
+    @Insert("INSERT INTO account (name, account, password) VALUES" +
+            "(#{name}, #{account}, #{password})")
+    int register(String name, String account, String password);
+
+    @Update(value = {" <script>" +
+            "UPDATE account " +
+            "SET reginCode=#{reginCode} ," +
+            "phone=#{phone} " +
+            "WHERE id=#{id}" +
+            "</script>"})
+    int bindPhone(int id, String phone, String reginCode);
+
+
+    @Insert("INSERT INTO device_bind " +
+            "(accountId, deviceId, devCode) VALUES" +
+            "(#{id}, #{deviceId}, #{devCode})")
+    int bindDevice(int id, int deviceId, String devCode);
+
+    @Delete("DELETE FROM device_bind WHERE userId=#{userId} AND devId=#{deviceId}")
+    int unBindDevice(int userId, int deviceId);
+
+    @Select("SELECT * FROM device_bind where devCode = #{bindCode}")
+    Device findDeviceByDevCode(String devCode);
+
+    // 查询用户设备
+    //    SELECT * FROM `device_bind` as b LEFT JOIN `device` as d ON b.deviceId = d.id where b.userId = 1
+    @Select("SELECT * FROM `device_bind` as b LEFT JOIN `device` as d ON b.devId = d.id where b.userId = #{id}")
+    List<Device> getAccountDevices(int id);
+
+    @Select("SELECT * FROM account WHERE id = #{id}")
+    UserAccount selectById(int id);
+
+    @Select("SELECT * FROM account WHERE account = #{account}")
+    UserAccount findByAccount(String account);
+
+    @Select("SELECT * FROM account WHERE account = #{account} AND password = #{password}")
+    UserAccount login(String account, String password);
+}

+ 5 - 0
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java

@@ -409,4 +409,9 @@ public interface DeviceMapper {
     // 根据设备id查询autoUpdate字段
     @Select("select autoUpdate from device where deviceId = #{deviceId}")
     Integer getAutoUpdateByDeviceId(String deviceId);
+
+    @Select("select * from device where bindCode = #{bindCode}")
+    Device getBindDevice(String bindCode);
+
+
 }

+ 70 - 0
src/main/java/com/genersoft/iot/vmp/storager/dao/dto/UserAccount.java

@@ -0,0 +1,70 @@
+package com.genersoft.iot.vmp.storager.dao.dto;
+
+public class UserAccount {
+    private int id;
+    private String account;
+    private String name;
+    private String password;
+    private String createTime;
+    private String phone;
+    private String reginCode;
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getReginCode() {
+        return reginCode;
+    }
+
+    public void setReginCode(String reginCode) {
+        this.reginCode = reginCode;
+    }
+
+
+
+
+}

+ 51 - 0
src/main/java/com/genersoft/iot/vmp/storager/dao/dto/deviceBind.java

@@ -0,0 +1,51 @@
+package com.genersoft.iot.vmp.storager.dao.dto;
+
+public class deviceBind {
+    private int id;
+    private int userId;
+    private int deviceId;
+    private String devCode;
+    private String createTime;
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public int getUserId() {
+        return userId;
+    }
+
+    public void setUserId(int userId) {
+        this.userId = userId;
+    }
+
+    public int getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(int deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getDevCode() {
+        return devCode;
+    }
+
+    public void setDevCode(String devCode) {
+        this.devCode = devCode;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+
+
+
+}

+ 4 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/bean/FailResult.java

@@ -0,0 +1,4 @@
+package com.genersoft.iot.vmp.vmanager.bean;
+
+public class FailResult {
+}

+ 5 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java

@@ -18,6 +18,7 @@ public class WVPResult<T> implements Cloneable{
         this.data = data;
     }
 
+
     // todo 返回结果携带搜索结果等页数
     public WVPResult(int code, String msg, T data, int page, int limit, int total) {
         this.code = code;
@@ -68,6 +69,10 @@ public class WVPResult<T> implements Cloneable{
         return new WVPResult<>(code, msg, null);
     }
 
+    public static <T> WVPResult<T> fail(ErrorCode errorCode, String msg) {
+        return new WVPResult<>(errorCode.getCode(), msg, null);
+    }
+
     public static <T> WVPResult<T> fail(ErrorCode errorCode) {
         return fail(errorCode.getCode(), errorCode.getMsg());
     }

+ 114 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/user/AccountController.java

@@ -0,0 +1,114 @@
+package com.genersoft.iot.vmp.vmanager.user;
+
+import com.genersoft.iot.vmp.conf.security.SecurityUtils;
+import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.service.IAccountService;
+import com.genersoft.iot.vmp.service.IDeviceService;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name  = "普通用户管理")
+@CrossOrigin(origins = "*")
+@RestController
+@RequestMapping("/account")
+public class AccountController {
+    private Logger logger = LoggerFactory.getLogger(AccountController.class);
+
+    @Autowired
+    private IAccountService accountService;
+
+    @Autowired
+    private IDeviceService deviceService;
+
+    @PostMapping("/register")
+    @Operation(summary = "注册用户")
+    @Parameter(name = "name", description = "用户名", required = true)
+    @Parameter(name = "account", description = "登陆账号", required = true)
+    @Parameter(name = "password", description = "密码", required = true)
+    public WVPResult<String> register(String name, String account, String password) {
+        logger.info("register account: " + account + " password: " + password);
+        WVPResult wvpResult = new WVPResult();
+        if(accountService.checkAccount(account) != null)
+        {
+            wvpResult.setCode(ErrorCode.ERROR100.getCode());
+            wvpResult.setMsg("账号已存在");
+            return wvpResult;
+        }
+        accountService.registerAccount(name, account, password);
+        wvpResult.setCode(ErrorCode.SUCCESS.getCode());
+        wvpResult.setMsg("注册成功");
+        return wvpResult;
+    }
+
+    @PostMapping("/login")
+    @Operation(summary = "登录")
+    @Parameter(name = "account", description = "登陆账号", required = true)
+    @Parameter(name = "password", description = "密码", required = true)
+    public WVPResult<String> login(String account, String password)
+    {
+        logger.info("[登录账号] account {}", account);
+        if (accountService.login(account, password) == null)
+        {
+            return WVPResult.fail(
+                    ErrorCode.ERROR404,
+                    "账号或者密码错误");
+        }
+//        SecurityUtils.login(username, password, authenticationManager);
+        return WVPResult.success(null, "ok");
+    }
+
+    @GetMapping("/bind/device")
+    @Operation(summary = "绑定设备")
+    @Parameter(name = "devCode", description = "设备码", required = true)
+    public WVPResult<String> bindDevice( String devCode)
+    {
+        logger.info("[绑定设备] bind account ");
+        WVPResult wvpResult = new WVPResult();
+        LoginUser loginUser = SecurityUtils.getUserInfo();
+        if(loginUser == null){
+            logger.warn("[绑定设备] 用户未登录");
+            return WVPResult.fail(ErrorCode.ERROR401);
+        }
+        Device device = deviceService.getBindDevice(devCode);
+        if(device == null){
+            return WVPResult.fail(ErrorCode.ERROR404, "无法找到对应设备");
+        }
+        // 判断设备是否已经绑定
+        if (!accountService.checkIsAllowBind(devCode)){
+            return WVPResult.fail(
+                    ErrorCode.ERROR403,
+                    "设备已经绑定, 无法重新绑定");
+        }
+        accountService.bindDevice(
+                loginUser.getId(),
+                device.getId(),
+                devCode );
+
+        return wvpResult;
+    }
+
+    @GetMapping("/unbind/device")
+    @Operation(summary = "解绑设备")
+    @Parameter(name = "deviceId", description = "设备id", required = true)
+    public WVPResult<String> unbindDevice( String deviceId)
+    {
+        LoginUser loginUser = SecurityUtils.getUserInfo();
+        if(loginUser == null){
+            logger.warn("[绑定设备] 用户未登录");
+            return WVPResult.fail(ErrorCode.ERROR401);
+        }
+        accountService.unBindDevice(loginUser.getId(), Integer.parseInt(deviceId));
+        return WVPResult.success("ok");
+    }
+
+
+
+}

+ 12 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java

@@ -179,6 +179,18 @@ public class UserController {
         return userService.getAllUsers();
     }
 
+    public void register(String username, String password) {
+        User user = new User();
+        user.setUsername(username);
+        user.setPassword(DigestUtils.md5DigestAsHex(password.getBytes()));
+        user.setCreateTime(DateUtil.getNow());
+        user.setUpdateTime(DateUtil.getNow());
+        int addResult = userService.addUser(user);
+        if (addResult <= 0) {
+            throw new ControllerException(ErrorCode.ERROR100);
+        }
+    }
+
     /**
      * 分页查询用户
      *

+ 17 - 0
src/main/resources/application.yml

@@ -59,6 +59,23 @@ spring:
             connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000
             #stat-view-servlet.url-pattern: /admin/druid/*
 
+############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
+sa-token:
+    # token 名称(同时也是 cookie 名称)
+    token-name: hfy
+    # token 有效期(单位:秒) 默认30天,-1 代表永久有效
+    timeout: 2592000
+    # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
+    active-timeout: -1
+    # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
+    is-concurrent: true
+    # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
+    is-share: true
+    # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
+    token-style: uuid
+    # 是否输出操作日志
+    is-log: true
+
 # druid管理监控页面的一些配置
 rj-druid-manage:
     allow:                        # 访问druid监控页面的IP白名单

+ 1 - 0
web_src/src/assets/base.css

@@ -1,4 +1,5 @@
 *{
     padding: 0;
     margin: 0;
+    box-sizing: border-box;
 }

+ 1 - 1
web_src/src/components/com/preset.vue

@@ -113,7 +113,7 @@ export default {
       }
 
       if (cmdCode === 129 || cmdCode === 131){
-        this.queryPresetPos();
+        await this.queryPresetPos();
       }
 
     },

+ 282 - 0
web_src/src/pages/account/AccountRegister.vue

@@ -0,0 +1,282 @@
+<script>
+
+import { FieldCheck, FormVerify } from 'kind-form-verify';
+import fieldCheck from '@/until/FieldCheck'
+import handle from "@/until/handle";
+import crypto from "crypto";
+
+
+let fieldVerify = null;
+let registerFieldVerify = null;
+export default {
+  name: 'app',
+  data(){
+    return {
+      form: {
+        account: {
+          val: "",
+          oldVal: "",
+          init: "",
+          msg: "",
+          showText: ""
+        },
+        password: {
+          val: "",
+          oldVal: "",
+          init: "",
+          msg: "",
+          showText: ""
+        }
+      },
+      registerForm: {
+        name: {
+          val: "",
+          oldVal: "",
+          init: "",
+          msg: "",
+          showText: ""
+        },
+        account: {
+          val: "",
+          oldVal: "",
+          init: "",
+          msg: "",
+          showText: ""
+        },
+        password: {
+          val: "",
+          oldVal: "",
+          init: "",
+          msg: "",
+          showText: ""
+        },
+
+      },
+      loginTitle: "用户登录",
+      rememberPassword: false,
+      showRegister: false,
+      isLoading: false
+    }
+  },
+  beforeMount() {
+    console.log("FormVerify ")
+    fieldVerify = new FormVerify(this.form, fieldCheck.fieldCheck)
+    registerFieldVerify = new FormVerify(this.registerForm, fieldCheck.fieldCheck)
+  },
+
+  mounted() {
+
+  },
+
+  methods: {
+
+    initForm() {
+      fieldVerify.init()
+    },
+    initRegisterForm() {
+      registerFieldVerify.init()
+    },
+    login(){
+      this.sendLogin()
+    },
+    register(){
+      this.sendRegister()
+    },
+    toRegister(){
+      this.loginTitle = "注册账号"
+      this.showRegister = true
+    },
+    toLogin(){
+      this.loginTitle = "用户登录"
+      this.showRegister = false
+    },
+    async sendLogin(){
+      if( !fieldVerify.check() )
+      {
+        this.$message.warning("请填写数据")
+        return
+      }
+      let formData = fieldVerify.getFormData()
+      console.log(formData)
+      formData.password = crypto.createHash('md5').update(formData.password, "utf8").digest('hex')
+      let url = `/account/login`
+      let [err,res] = await handle(this.$axios.post(
+          url,
+          formData
+      ));
+      this.isLoging = false;
+      if (err) {
+        console.log(err);
+        this.$message.error(`登录失败 ${err.message}`);
+        return;
+      }
+      console.log(res);
+      if (res.data.code !== 0) {
+        this.$message.error(`登录失败 ${res.data.msg}`);
+        return;
+      }
+    },
+
+    async sendRegister(){
+      if( !registerFieldVerify.check() )
+      {
+        this.$message.warning("请填写数据")
+        return
+      }
+      let formData = registerFieldVerify.getFormData()
+      formData.password = crypto.createHash('md5').update(formData.password, "utf8").digest('hex')
+      console.log(formData)
+      let url = `/account/register`
+      let [err,res] = await handle(this.$axios.post(
+          url,
+          formData
+      ));
+      this.isLoging = false;
+      if (err) {
+        console.log(err);
+        this.$message.error(`账号注册失败 ${err.message}`);
+        return;
+      }
+      console.log(res);
+      if (res.data.code !== 0) {
+        this.$message.error(`账号注册失败 ${res.data.msg}`);
+        return;
+      }
+    },
+
+    loginBlurHandle(){
+      console.log("loginBlurHandle")
+      fieldVerify.check()
+    },
+
+    registerBlurHandle(){
+      console.log("registerBlurHandle")
+      registerFieldVerify.check()
+    }
+
+
+  }
+}
+</script>
+
+<template>
+  <div id="app">
+
+<!--  登录界面与注册界面切换  -->
+      <el-form class="form-box" v-if="!showRegister">
+
+        <div class="title">{{loginTitle}}</div>
+
+        <el-form-item class="form-item" prop="account" :error="form.account.msg">
+          <el-input v-model="form.account.val" placeholder="请输入内容" @blur="loginBlurHandle">
+          </el-input>
+        </el-form-item>
+
+        <el-form-item class="form-item"  prop="password" :error="form.password.msg">
+          <el-input v-model="form.password.val" show-password placeholder="用户密码" @blur="loginBlurHandle">
+          </el-input>
+        </el-form-item>
+
+        <div class="form-item line">
+          <el-checkbox v-model="rememberPassword">记住密码</el-checkbox>
+          <span class="register_url" @click="toRegister">注册账户</span>
+        </div>
+
+        <el-form-item class="form-btn">
+          <el-button class="btn" type="primary" @click="login">登录</el-button>
+        </el-form-item>
+
+      </el-form>
+
+<!--  注册账号 -->
+      <el-form class="form-box" v-if="showRegister">
+        <div class="title">{{loginTitle}}</div>
+
+        <el-form-item class="form-item" prop="name" :error="registerForm.name.msg">
+          <el-input v-model="registerForm.name.val"
+                    placeholder="名称"
+                    @blur="registerBlurHandle">
+          </el-input>
+        </el-form-item>
+
+        <el-form-item class="form-item" prop="account" :error="registerForm.account.msg">
+          <el-input v-model="registerForm.account.val"
+                    placeholder="登录账号"
+                    @blur="registerBlurHandle"
+          >
+          </el-input>
+        </el-form-item>
+
+        <el-form-item class="form-item"  prop="password" :error="registerForm.password.msg">
+          <el-input v-model="registerForm.password.val"
+                    show-password placeholder="用户密码"
+                    @blur="registerBlurHandle">
+          </el-input>
+        </el-form-item>
+
+        <div class="form-item line">
+<!--          <el-checkbox v-model="rememberPassword">记住密码</el-checkbox>-->
+          <span class="register_url" @click="toLogin">前往登录</span>
+        </div>
+
+        <el-form-item class="form-btn">
+          <el-button class="btn" type="primary" @click="register">注册</el-button>
+        </el-form-item>
+      </el-form>
+
+  </div>
+</template>
+
+<style scoped>
+
+
+#app {
+  margin: 0 0;
+  width: 100vw;
+  height: 100vh;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: #eee !important;
+}
+
+.form-box{
+  width: 360px;
+  height: auto;
+  padding: 15px 35px;
+  background-color: #fff;
+  border: 1px solid rgba(0,0,0,0.1);
+  border-radius: 5px;
+}
+
+.title{
+  width: 100%;
+  height: 50px;
+  font-size: 1.5rem;
+  font-weight: bold;
+}
+
+.form-item{
+  width: 100%;
+}
+
+.line{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.el-form-item{
+  margin-bottom: 15px;
+}
+
+.form-btn{
+  margin-top: 25px;
+}
+
+.register_url{
+  color: #00bfff;
+  cursor: pointer;
+}
+
+</style>

+ 20 - 0
web_src/src/pages/account/index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <title>合方圆-国标28181</title>
+    <link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon">
+    <link rel="stylesheet" type="text/css" href="/static/css/iconfont.css">
+    <link rel="stylesheet" type="text/css" href="/static/css/login.css">
+  </head>
+  <body>
+    <script type="text/javascript" src="/static/dist/missile.js"></script>
+    <script type="text/javascript" src="/static/dist/h265webjs-v20221106.js"></script>
+    <script type="text/javascript" src="/static/js/jessibuca/jessibuca.js"></script>
+    <script type="text/javascript" src="/static/js/EasyWasmPlayer.js"></script>
+<!--    <script type="text/javascript" src="static/js/liveplayer-lib.min.js"></script>-->
+    <script type="text/javascript" src="/static/js/config.js"></script>
+    <div id="app"></div>
+  </body>
+</html>

+ 16 - 0
web_src/src/pages/account/main.js

@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import App from './AccountRegister.vue';
+import '@/assets/base.css'
+
+import ElementUI from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+
+import axios from '@/apiStore/axios';
+
+let _axios = axios.axios;
+
+Vue.prototype.$axios = _axios;
+Vue.use(ElementUI);
+new Vue({
+   render: h => h(App),
+}).$mount("#app")

+ 1 - 0
web_src/src/pages/index/main.js

@@ -79,6 +79,7 @@ _axios.interceptors.request.use(
     return Promise.reject(error);
   }
 );
+
 _axios.interceptors.request.use(config => {
   config.headers.withCredentials = true;
   // App.$message.info('test')

+ 39 - 0
web_src/src/until/FieldCheck.js

@@ -0,0 +1,39 @@
+import {FieldCheck} from "kind-form-verify";
+
+let fieldCheck = new FieldCheck();
+
+let requiredRuleItem = {require: true, message:'请填写数据'}
+
+fieldCheck.addRuleItem('nameRule',['name'],[
+    requiredRuleItem,
+    {
+        type: 'string',
+        minLength: 2,
+        maxLength: 10,
+        message: '姓名必须为2-10个字符'
+    }
+]);
+
+fieldCheck.addRuleItem('account',['account'],[
+    requiredRuleItem,
+    {
+        type: 'string',
+        minLength: 5,
+        maxLength: 30,
+        message: '账号长度限制为 5-30 个字符'
+    }
+]);
+
+fieldCheck.addRuleItem('password',['password'],[
+    requiredRuleItem,
+    {
+        type: 'string',
+        minLength: 8,
+        maxLength: 30,
+        message: "登录密码为 8-30 个字符"
+    }
+]);
+
+export default {
+    fieldCheck
+}

+ 11 - 6
web_src/vue.config.js

@@ -12,7 +12,7 @@ module.exports = {
         port: 8080,
         https: true,
         proxy: {
-            '/api/':{
+            '/api/': {
                 target: baseUrl,
                 ws: true,
                 changeOrigin: true,
@@ -25,17 +25,17 @@ module.exports = {
                     '^/debug': '/'
                 }
             },
-            '/ai':{
+            '/ai': {
                 target: baseUrl,
                 secure: false,
                 changeOrigin: true,
             },
-            '/aiLib':{
+            '/aiLib': {
                 target: baseUrl,
                 secure: false,
                 changeOrigin: true,
             },
-            '/mFile':{
+            '/mFile': {
                 target: baseUrl,
                 secure: false,
                 changeOrigin: true,
@@ -49,7 +49,7 @@ module.exports = {
     },
     outputDir: '../src/main/resources/static/', // 输出文件目录
     // publicPath: '', // 项目部署的基本路径
-    pages:{
+    pages: {
         index: {
             entry: 'src/pages/index/main.js',
             template: 'src/pages/index/index.html',
@@ -59,10 +59,15 @@ module.exports = {
             entry: 'src/pages/sharePlay/shareMain.js',
             template: 'src/pages/sharePlay/index.html',
             filename: 'share.html'
+        },
+        account: {
+            entry: 'src/pages/account/main.js',
+            template: 'src/pages/account/index.html',
+            filename: 'account.html'
         }
     },
     configureWebpack: webPackConfig,
-    css:{
+    css: {
         loaderOptions: {
             sass: {
                 additionalData:`@import "./src/assets/scss/style.scss";`

+ 56 - 0
设备绑定机制.md

@@ -1 +1,57 @@
 # 合方圆国标平台绑定流程
+
+## 设备上线绑定服务流程
+1. 设备上线平台
+2. 用户注册账号
+3. 用户输入鉴定码
+4. 用户绑定设备
+5. 用户查询自己设备
+
+### 平台角色管理
+1. 主管理员 唯一 平台的全部权限, 包括创建域名管理员
+2. 域主管理员 多个 管理对应sip域下的对应设备 是否对对应的域信息进行设备绑定
+3. 域子管理员 多个 管理对应账户下的设备
+4. 普通用户 多个 管理对应账户下的设备
+
+#### 用户表数据库设计
+##### 普通用户表 account
+| 字段        | 类型           | 值介绍   | 默认     | 备注      |
+|-----------|--------------|-------|--------|---------|
+| id        | int          | 主键    | 无      | 无       |
+| account   | varchar(30)  | 登录账号  | 无      | 登录账号    |
+| name      | varchar(100) | 昵称    | 随机用户id | 用户名称    |
+| reginCode | varchar(10)  | 手机号区号 | 86     | 用户手机号区号 |
+| phone     | varchar(16)  | 手机号   | 无      | 用户手机号   |
+| passwd    | varchar(255) | 密码    | 无      | 用户密码摘要  |
+
+##### 用户绑定设备表 device_bind
+| 字段         | 类型           | 值介绍  | 默认 | 备注   |
+|------------|--------------|------|----|------|
+| bindId     | int          | 主键   | 无  | 无    |
+| accountId  | int          | 用户id | 无  | 用户id |
+| devId      | int          | 设备id | 无  | 设备id |
+| devCode    | varchar(255) | 绑定码  | 无  | 绑定码  |
+| createTime | datetime     | 创建时间 | 无  | 创建时间 |
+
+
+##### 管理员表 user
+| 字段      | 类型           | 值介绍  | 默认     | 备注     |
+|---------|--------------|------|--------|--------|
+| id      | int          | 主键   | 无      | 无      |
+| account | varchar(30)  | 登录账号 | 无      | 登录账号   |
+| name    | varchar(100) | 昵称   | 随机用户id | 用户名称   |
+| passwd  | varchar(255) | 密码   | 无      | 用户密码摘要 |
+
+##### 管理员sip配置表
+| 字段      | 类型           | 值介绍  | 默认     | 备注     |
+|---------|--------------|------|--------|--------|
+| id      | int          | 主键   | 无      | 无      |
+
+
+#### 设备表扩展
+> 使用字段 `devCode` 设备注册时获取
+### 设备上线平台
+1. 设备发送连接请求
+2. 平台根据域将设备分配至不同的管理账户下
+3. 对应域的管理员账户能够看自己域下方的设备
+4. 普通用户只能看自己的设备