IndexedDB的包装器JsStore - 实现登录功能及事务处理

news2024/12/25 9:46:53

        JsStore是IndexedDB的包装器。它提供了简单的SQL像api,这是容易学习和使用。         IndexedDb查询可以在web worker内部执行,JsStore通过提供一个单独的worker文件来保持这种功能。

        最近有位叫Pioneer网友一直在问我关于事务的实现方式,关于这点的确在看JsStore时,并没太重视。由于近期疫情放开新冠病毒迅速传播,我也不例外18日中招了。近日身体已基本恢复,利用在家休息期间,了解了JsStore中的transaction事务。对于事务,简单的理解就是,一个事务里的操作,要么全部执行成功,要么全部执行失败。 这里将修改之前IndexedDB写的登录功能,来作为案例讲解,界面细节就不细讲了,有不清楚可以查看之前案例,地址:本地数据库IndexedDB - 学员管理系统之登录(一)_觉醒法师的博客-CSDN博客_indexdb实现管理系统

 一、框架搭建

1.1 项目结构

        由于该Demo主要是以之前 “本地数据库IndexedDB - 学员管理系统之登录(一)” 中代码实现,并且只讲登录功能,以及事务处理,所以有些地方不会做过多细讲,该篇重点会讲到api、db、store几处。

 1.2 路由定义

        路由这块并不复杂,只之前多于部分删除,保留现在Pages中Error、login、menge部分即可。

        Error页面代码如下:

<template>
  <div class="error-box">
      <h3>页面出错了 404 ~</h3>
      <p>当前页面不存在,<span class="blue" @click="backEvent">点击返回</span></p>
  </div>
</template>

<script>
  export default{
    data(){
      return {}
    },
    methods: {
      backEvent(){
        this.$router.go(-1);
      }
    }
  }
</script>

<style lang="scss" scoped>
@import './index.scss';
</style>

         由于该篇不讲主页功能,这里只保留极少代码,以实现登录后跳转到首页即可,mange页面代码:

<template>
<div class="index-wrap">
     Home
</div>
</template>

<script>
export default {
  data () {
    return {

    }
  },
  methods: {
  }
}
</script>

<style lang="scss" scoped>

</style>

        登录页面login样式代码如下:


.login-box{
	width: 600px;
	height: 390px;
	padding: 50px 70px;
	box-sizing: border-box;
	box-shadow: 0 0 10px rgba(0, 0, 0, .1);
	border-radius: 10px;
	overflow: hidden;
	position: absolute;
	left: 50%;
	top: 50%;
	margin-left: -300px;
	margin-top: -200px;
	z-index: 10;

	h3, h4{
		font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
		text-align: center;
	}

	h3{
		font-size: 26px;
		color: #409eff;
	}

	h4{
		font-size: 14px;
		color: #999999;
		font-weight: normal;
		padding: 10px 0 40px;

		span{
			display: inline-block;
			vertical-align: middle;
			&.tit{
				padding: 0 26px;
			}
		}
	}
}

        登录页面先实现校验等功能,具体实现待后期再详细讲解,login代码如下:

<template>
  <div class="login-box">
  	<h3>学员管理系统</h3>
  	<h4><span>———</span> <span class="tit">安全登录</span> <span>———</span></h4>
  	<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="ruleForm">
  		<el-form-item label="用户名" prop="username">
  			<el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input>
  		</el-form-item>
  		<el-form-item label="密码" prop="password">
  			<el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
  		</el-form-item>
  		<el-form-item>
  			<el-button type="primary" :disabled="disabledButton" @click="submitForm('ruleForm')">登录</el-button>
  			<el-button :disabled="disabledButton" @click="resetForm('ruleForm')">重置</el-button>
  		</el-form-item>
  	</el-form>
  </div>
</template>

<script>
  export default {
    data(){
      var validateUsername = (rule, value, callback) => {
      	if (value === '') {
      		callback(new Error('请输入用户名'));
      	} else {
      		callback();
      	}
      };
      var validatePass = (rule, value, callback) => {
      	if (value === '') {
      		callback(new Error('请输入密码'));
      	} else {
      		if (this.ruleForm.checkPass !== '') {
      			this.$refs.ruleForm.validateField('checkPass');
      		}
      		callback();
      	}
      };
      return {
        disabledButton: false,
      	ruleForm: {
      		username: '',
      		password: '',
      	},
      	rules: {
      		username: [
      			{ validator: validateUsername, trigger: 'blur' }
      		],
      		password: [
      			{ validator: validatePass, trigger: 'blur' }
      		]
      	},
      }
    },
    methods: {
      /**
       * 提交数据
       * @param {Object} formName
       */
      submitForm(formName) {
      	this.$refs[formName].validate((valid) => {
      		if (valid) {

            }
      	});
      },
      /**
       * 重置表单
       * @param {Object} formName
       */
      resetForm(formName) {
      	this.$refs[formName].resetFields();
      }
    }
  }
</script>

<style lang="scss" scoped>
@import './index.scss';
</style>

        路由中配置代码如下: 

import Vue from 'vue'
import Router from 'vue-router'
import { TOKEN } from '@/store/mutationsType'
import Layout from '@/components/Layout'
import Error404 from '@/pages/Error/err404'
import Mange from '@/pages/mange'
import Login from '@/pages/login'
import store from '@/store'

Vue.use(Router);

let _router = new Router({
  routes: [
    {
      path: '/',
      name: "Home",
      component: Layout,
      redirect: '/sys/index',
      children: [
        {
          path: '/sys/index',
          name: 'Index',
          component: Mange,
        }
      ]
    },
    {
      path: '/login',
      name: 'Login',
      component: Login,
    },
    {
      path: '*',
      name: 'Error404',
      component: Error404,
    },
  ]
});

//存储push
let originPush=Router.prototype.push
let originReplace=Router.prototype.replace

//重写
Router.prototype.push=function(location,resole,reject){
    if(resole&&reject){
        originPush.call(this,location,resole,reject)
    }else{
        originPush.call(this,location,()=>{},()=>{})
    }
}
Router.prototype.replace=function(location,resole,reject){
    if(resole&&reject){
        originReplace.call(this,location,resole,reject)
    }else{
        originReplace.call(this,location,()=>{},()=>{})
    }
}

_router.beforeEach((toRoute, fromRoute, next) => {
  store.dispatch('checkIsLogin').then(() => {
    next();
  }).catch(() => {
    if(toRoute.path=='/login'){
      next();
    }else{
      next('/login');
    }
  });
});

export default _router;

1.3 store状态管理器

        state.js代码如下:

/**
 * 状态,变量库
 */
const state = {
  /**
   * 访问令牌
   */
  token: "",
  /**
   * 用户信息
   */
  userInfo: null
}

export default state;

        getters.js代码如下:

/**
 * 计算器
 */
const getters = {
  /**
   * 用户信息
   */
  userInfo(state){
    return state.userInfo;
  },
  /**
   * 访问令牌
   */
  accessToken(state){
    return state.token;
  }
}
export default getters;

        mutationsType.js代码如下:

/**
 * 用户信息
 */
export const USERINFO = "USERINFO";

/**
 * 访问令牌
 */
export const TOKEN = "TOKEN";

        mutations.js代码如下:

import { USERINFO, TOKEN } from './mutationsType'

/**
 * 裂变器
 */
const mutations = {
  /**
   * 修改访问令牌信息
   */
  [TOKEN](state, param){
    state.token = param;
  },
  /**
   * 修改用户信息
   */
  [USERINFO](state, param){
    state.userInfo = param;
  }
}
export default mutations;

        在这里tokenIsFailure函数还未实现,所以代码中暂不体现,actions.js代码如下:

import Vue from 'vue'
import { USERINFO, TOKEN } from './mutationsType'
import { Loading } from 'element-ui'
import { tokenIsFailure } from '@/api'

/**
 * 业务层
 */
const actions = {
  /**
   * 重新加载缓存中用户信息
   */
  reloadUserInfo({commit}){
    let token = Vue.ls.get(TOKEN),
        userInfo = Vue.ls.get(USERINFO);
    if(token) {
      commit(TOKEN, token);
    }
    if(userInfo) {
      commit(USERINFO, userInfo);
    }
  },
  /**
   * 检查是否登录
   */
  checkIsLogin({commit}){
    let token = Vue.ls.get(TOKEN);
    return new Promise((resolve, reject) => {
      if(token){
        Vue.ls.set(TOKEN, token, 24 * 60 * 60 * 1000);    //重新计时,缓存1天
        resolve();
      }else{
        reject();
      }
    });
  },
  /**
   * 保存登录信息
   */
  saveLoginInfo({commit}, param){
    if(param['token']) {
      commit(TOKEN, param.token);
      Vue.ls.set(TOKEN, param.token, 24 * 60 * 60 * 1000);
    }
    if(param['userinfo']) {
      commit(USERINFO, param.userinfo);
      Vue.ls.set(USERINFO, param.userinfo);
    }
  },
  /**
   * 退出登录
   */
  exitLogin({commit}, param){
    commit(TOKEN, '');
    commit(USERINFO, '');
    Vue.ls.remove(TOKEN);
    Vue.ls.remove(USERINFO);
  }
}

export default actions;

         store/index.js代码如下:

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'

Vue.use(Vuex);

export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})

        在main.js中引入,代码如下:

import Vue from 'vue'
import App from './App'
import router from './router'
import elementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import 'element-ui/lib/theme-chalk/base.css'
import store from '@/store/index'
import Storage from 'vue-ls'

Vue.use(elementUI);
Vue.use(Storage, {
  namespace: 'jsstoredemo_',
  name: 'ls',
  storeage: 'local'
});


Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

        以上操作完成,基本框架已经完成,剩下就是功能实现了。

二、数据库模型

2.1 创建数据库链接

        首先在db目录下创建index.js文件,用来实现数据库连接和增删改查功能,代码如下:

import { Connection } from "jsstore";
import workerInjector from "jsstore/dist/worker_injector";

let connection = new Connection();
connection.addPlugin(workerInjector);

export default connection;

2.2 定义数据表

        在db目录下创建service.js文件,在这里定义数据中使用到的数据表,数据库初始化函数,以及版本迭代等功能操作,代码如下:

import connection from './index.js';
import { DATA_TYPE } from "jsstore";

const getDatabase = () => {
	//用户表
	const UserTable = {
		name: "Users",
		columns: {
			id: { primaryKey: true, autoIncrement: true },
			username: { notNull: true, dataType: DATA_TYPE.String },
			password: { notNull: true, dataType: DATA_TYPE.String },
			role: { notNull: true, dataType: DATA_TYPE.Number },
			token: { notNull: false, dataType: DATA_TYPE.String },
			createtime: { notNull: true, dataType: DATA_TYPE.DateTime },
			updatetime: { notNull: true, dataType: DATA_TYPE.DateTime },
		}
	}

	const dataBase = {
			name: "demo_manage",
			tables: [UserTable],
			version: 1
	};
	return dataBase;
}

export const initJsStore = async () => {
	const dataBase = await getDatabase();
	return await connection.initDb(dataBase);
}

2.3 定义实例操作对象

        在db/model目录下,创建user.js文件,用来定义用户数据表模型;由于登录功能需要使用事务来实现,这里暂不展示,待后续再讲解。代码如下:

import connection from "@/db/index.js";
import { hex_md5 } from '@/utils/md5'

export class UserService {
  constructor(){
    this.tableName = "Users";
  }

  /**
   * 获取所有用户信息
   * @param {Object} token
   */
  getUsers(){
    return connection.select({
      from: this.tableName
    });
  }

  /**
   * 判断是否存在token
   * @param {Object} token
   */
  hasToken(token){
    return connection.select({
      from: this.tableName,
      where: {
        token
      }
    })
  }

  /**
   * 退出
   * @param {Object} token
   */
  logout(token){
    return connection.update({
            in: this.tableName,
            set: {
              token: null
            },
            where: {
              token
            }
          });
  }

  /**
   * 登录
   * @param {Object} params
   */
  login(params){
    
  }

  /**
   * 添加用户数据
   * @param {Object} data
   */
  insertUser(data){
    //增加创建和更新时间
    Object.assign(data, {
      createtime: new Date(),
      updatetime: new Date()
    });
    //加密密码
    if('undefined'!==typeof data['password']){
      data['password'] = hex_md5(data.password);
    }
    //if end
    return connection.insert({
      into: this.tableName,
      values: [data]
    });
  }

}

        后面登录功能实现,将全部通过user.js模型中函数实现。

2.4 api接口

        这里虽然无后台服务接口调用,但为方便使用user实例,还是通过api方式实现功能调用。代码方式如下:

import { UserService } from '@/db/model/user'

// 实例Users对象
const Users = new UserService();

/**
 * 登录
 */
export const loginInfo = (params) => {
  return Users.login(params);
}

/**
 * 退出
 */
export const logoutInfo = token => {
  return Users.logout(token);
}

/**
 * 判断数据库中token是否失效
 */
export const tokenIsFailure = token => {
  return Users.hasToken(token);
}

/**
 * 添加用户信息
 */
export const addUserInfo = param => {
  return Users.insertUser(param);
}

/**
 * 获取用户列表
 */
export const userAllList = keyword => {
  return Users.getUsers(keyword);
}

 2.5 连接数据库

        在App.vue中,实现数据库的初始化,并且当数据库连接成功后,判断用户是否存在,不存在则创建一个默认用户,代码如下:

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
  import { mapGetters } from 'vuex'
  import { initJsStore } from '@/db/service.js'
  import { addUserInfo, userAllList } from '@/api'
  export default {
    name: 'App',
    data(){
      return {}
    },
    created() {
      this.$store.dispatch('reloadUserInfo');
      //初始化数据库
      initJsStore().then(() => {
        //判断用户是否存在,不存在默认添加一个
        userAllList().then(res => {
          if(res.length==0){
            //添加管理员
            addUserInfo({
              username: "admin",
              password: "123456",
              role: 1
            });
          }
        })
      });
    }
  }
</script>

<style>
  * {
    margin: 0;
    padding: 0;
  }

  html,
  body {
    width: 100%;
    height: 100%;
  }

  #app {
    width: 100%;
    font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-size: 12px;
    color: #2c3e50;
  }
</style>

三、功能实现

3.1 检测token是否存在

        在2.3中的代码,已实现了hasToken函数,在2.4中的代码,api接口函数已定义了tokenIsFailure()函数,此时我们修改actions.js中的checkIsLogin()函数即可。代码如下:

checkIsLogin({commit}){
	let token = Vue.ls.get(TOKEN);
	return new Promise((resolve, reject) => {
		if(token){
            //判断数据库中token是否存在
			tokenIsFailure(token).then(() => {
				Vue.ls.set(TOKEN, token, 24 * 60 * 60 * 1000);
				resolve();
			}).catch(e => {
				commit(TOKEN, '');
				Vue.ls.remove(TOKEN);
				reject();
			});
		}else{
			reject();
		}
	});
}

        这样一来,当数据库中的token因某些原因失效后,即可将localStorge中缓存及时清除,并在路由跳转时,及时跳转到登录页面。

 

3.2 登录功能 - 事务处理

        用户的登录功能相对较为复杂,完成登录这一步,需要实现以下几个步骤:

  1. 查询用户 - 判断用户名是否存在,
  2. 判断输入的密码是否正确
  3. 生成token,并将token保存到相对用户信息中
  4. 返回登录成功的用户信息和token信息

        此时,我们在db目录中创建transaction.js文件,代码发如下:

import { randomStrName } from '@/utils/utils'

const userTableName = "Users";

/**
 * 登录并生成token信息
 * @param {Object} ctx
 */
async function login2accesstoken(ctx) {

  ctx.start(); // 开始事务

  //1.获取用户信息
  const userRes = await ctx.select({
    from: userTableName,
    where: {
      username: ctx.data.username
    }
  });

  let userinfo;
  //用户存在
  if(userRes.length==1){
    userinfo = userRes[0];
    ctx.setResult('userinfo', userinfo);
  }
  //用户不存在
  else{
    ctx.setResult('code', -1);
    ctx.setResult('msg', "用户名不存在");
    ctx.abort();
    return;
  }

  //2.判断密码是否正确
  if(userinfo.password!=ctx.data.password){
    ctx.setResult('code', -1);
    ctx.setResult('msg', "密码错误");
    ctx.abort();
    return;
  }else{
    //删除密码信息
    delete userinfo.password;
  }

  //3.生成token
  const accesstoken = randomStrName(60);

  //4.存入token 信息
  ctx.update({
    in: userTableName,
    set: {
      token: accesstoken
    },
    where: {
      id: userinfo.id
    }
  });

  //5.返回结果信息
  ctx.setResult('code', 0);
  ctx.setResult('msg', "登录成功");
  ctx.setResult('data', {
    accesstoken,
    userinfo
  });
}

// 将函数 login2accesstoken 赋值到window对象上。
window.login2accesstoken = login2accesstoken;

        如上代码,我们按照实现步骤,依次完成用户查询、账号和密码校验、token生成和保存,以及登录成功后数据返回一系列操作。

        此时,我们可以完成user.js中的login()函数了,首先是将transaction.js文件引入,代码如下:

import connection from "@/db/index.js";
import { hex_md5 } from '@/utils/md5'
import '../transaction.js'

export class UserService {
  constructor(){
    this.tableName = "Users";
  }

  /**
   * 获取所有用户信息
   * @param {Object} token
   */
  getUsers(){
    return connection.select({
      from: this.tableName
    });
  }

  /**
   * 判断是否存在token
   * @param {Object} token
   */
  hasToken(token){
    return connection.select({
      from: this.tableName,
      where: {
        token
      }
    })
  }

  /**
   * 退出
   * @param {Object} token
   */
  logout(token){
    return connection.update({
            in: this.tableName,
            set: {
              token: null
            },
            where: {
              token
            }
          });
  }

  /**
   * 登录
   * @param {Object} params
   */
  login(params){
    return connection.transaction({
					tables: [this.tableName],
					method: "login2accesstoken",
					data: {
						username: params.username,
						password: hex_md5(params.password)
					}
				});
  }

  /**
   * 添加用户数据
   * @param {Object} data
   */
  insertUser(data){
    //增加创建和更新时间
    Object.assign(data, {
      createtime: new Date(),
      updatetime: new Date()
    });
    //加密密码
    if('undefined'!==typeof data['password']){
      data['password'] = hex_md5(data.password);
    }
    //if end
    return connection.insert({
      into: this.tableName,
      values: [data]
    });
  }

}

        将transaction.js中定义的login2accesstoken()函数,赋值到transaction的method参数上即可。

3.3 实现登录功能

        通过以上功能实现,用户模型中已完成登录接口功能,在2.4的代码中,我们也实现登录接口函数定义,这里我们直接使用loginInfo()函数即可。我们找到登录页并打开,代码如下:

submitForm(formName) {
	this.$refs[formName].validate((valid) => {
		if (valid) {
            //调用登录函数,实现用户登录
			loginInfo(this.ruleForm).then(res => {
				if(res.code==0){
                    //缓存用户信息
					this.$store.dispatch('saveLoginInfo', {
						token: res.data.accesstoken,
						userinfo: res.data.userinfo
					})
					this.$message.success(res.msg);
                    //跳转到首页
					setTimeout(() => {
						this.$router.push('/');
					}, 200);
				}else{
					this.$message.error(res.msg);
					this.resetForm('ruleForm');
				}
			}).catch(e => {
				this.$message.error(e.message);
				this.resetForm('ruleForm');
			});

		} else {
			console.log('error submit!!');
			return false;
		}
	});
}

四、属性

         JsStore在事务api中接受一个方法名,该方法名通过上下文调用。

        这个上下文所包含的属性如下:

start启动事务
select查询数据表
count统计查询数据总条数
update更新数据表信息
remove移除
insert插入
setResult setResult接受键和值。setResult用于保存事务完成时返回的值。事务返回一个对象,该对象是键和值的形式,使用setResult设置。
abortAbort用于中止事务。
getResultgetResult用于获取setResult设置的值。
data在事务API中作为数据传递的值。

        这里通过事务实现的功能相对较为简单,大家可以了解下JsStore官网提供的案例。希望此篇对大家有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/113644.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷粒学院——Day13【微信扫描登录】

OAuth2 OAuth2的使用场景 一、OAuth2解决什么问题 1. OAuth2提出的背景 照片拥有者想要在云冲印服务上打印照片&#xff0c;云冲印服务需要访问云存储服务上的资源。 2. 图例 资源拥有者&#xff1a;照片拥有者。 客户应用&#xff1a;云冲印。 受保护的资源&#xff…

Python pandas库|任凭弱水三千,我只取一瓢饮(2)

上一篇链接&#xff1a; Python pandas库&#xff5c;任凭弱水三千&#xff0c;我只取一瓢饮&#xff08;2&#xff09;_Hann Yang的博客-CSDN博客 I~Q&#xff1a; Function10~25 Types[Function][9:25] [infer_freq, interval_range, isna, isnull, json_normalize, lres…

④【Maven】Maven的构建命令

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Maven的构建命令一、注意二、&#x1f680;清理…

指针与数组的联系与区别【一万六千字超详解】

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;《初识C语言》 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言数组的性质1.1 数组的内存布局1…

第四章 Spring的基础用法

文章目录 Spring的起源和背景理解依赖注入Spring容器理解Spring容器中的Bean管理容器中的Bean及其依赖注入自动装配使用Java类进行配置管理使用静态工厂、实例工厂创建Bean实例抽象Bean与子Bean容器中的工厂Bean管理Bean的生命周期几种特殊的依赖注入Spring的简化配置SpEL的功…

kali中间人攻击

数据来源 一、中间人攻击原理 1. 利用的ARP协议的漏洞 2. ARP协议原理&#xff1a; 1&#xff09;发送ARP广播请求目标MAC地址 2&#xff09;目标主机发送ARP单播应答&#xff0c;响应MAC地址 3. ARP攻击原理 攻击人通过发送虚假的ARP应答实现ARP缓存投毒!而受害人没有办法进行…

文件存储案例

1.文件存储-File文件存储案例 1.1.案例要求 1.2参考代码 文件读取 百度安全验证 文件最终的保存的目录在/data/data/user/0/包/files下&#xff08;1&#xff09;布局文件 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android&q…

kubernetes中使用Service反向代理外部服务

当我们的某个服务在外部集群的时候&#xff0c;但是又想k8s集群内的应用连接它&#xff0c;这是可以创建一个service&#xff0c;用service代理外部服务&#xff0c;然后集群内就能连接该service&#xff0c;从而间接的访问外部服务。 创建一个service代理外部的服务 创建一个…

代码挑战画 魔法圣诞树

一、前言 本文会基于C# GDI技术 从零到一 实现一颗 魔法圣诞树&#xff01;源码和素材在文末全部都有&#xff01; 二、魔法圣诞树 对于用代码画圣诞树&#xff0c;网上各种编程语言像python、css、java、c/c我们都有见到过了&#xff0c;那么在绘图方面&#xff0c;还有一位…

FastDDS(6)核心库综述

Fast DDS(前身为Fast RTPS)是DDS规范的高效高性能实现,DDS规范是一种用于分布式应用软件的以数据为中心的通信中间件(DCPS)。本次回顾Fast DDS的体系结构、操作和关键特性。 架构 Fast DDS的架构如下图所示,其中可以看到具有以下不同环境的层模型。 Application layer应…

人员工装未穿戴识别检测 opencv

人员工装未穿戴识别检测基于OpenCvyolo计算机视觉深度学习技术对现场画面中人员行为着装穿戴实时监测识别&#xff0c;发现不按要求着装违规行为立即抓拍存档同步后台。OpenCV-Python使用Numpy&#xff0c;这是一个高度优化的数据库操作库&#xff0c;具有MATLAB风格的语法。所…

RabbitMQ 第一天 基础 1 MQ的基本概念 1.4 MQ 的劣势 1.5 常见的MQ 产品

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础1 MQ的基本概念1.4 MQ 的劣势1.4.1 MQ 的劣势1.4.2 小结1.5 常见的MQ 产品第一天 基础 1 MQ的基本概念 1.4 MQ 的劣势 1.4.1 MQ 的劣势 从远程调用 到 利用 MQ 作…

css实现九宫格

首先是实现九宫格的样式&#xff0c;对每一行进行偏移&#xff0c;当鼠标放上去会使他们形成一张图片。 html <div class"img_container"><div class"img1"></div><div class"img1"></div><div class"i…

2022年,来者犹可追

始料未及的是&#xff0c; 疫情持续到了2022年。好在“大疫不过三年”&#xff0c;只不过是结束来的同样措不及防&#xff0c;全家的一次高烧免疫&#xff0c;没有朋友圈中的云淡风轻&#xff0c;冷暖自知&#xff0c;希望明年能够拥有平安喜乐的时光。回首这一年&#xff0c;“…

kotlin与java实现混编基础看这篇就够了

前几年一直关注安卓&#xff0c;想换个方向&#xff0c;奔着移动端大步向前&#xff0c;由于比较懒就一直停留在想法&#xff0c;这不今天勤快点&#xff0c;动手搞了一个基础的java和kotlin混编&#xff0c;和大家总结分享一下。 首先需要了解什么事kotlin&#xff0c;kotlin…

如何使用腾讯云轻量应用服务器挂载 CFS 文件系统

文件存储&#xff08;Cloud File Storage&#xff0c;CFS&#xff09;提供了可扩展的共享文件存储服务&#xff0c;可与腾讯云云服务器 、容器、批量计算、轻量应用服务器等服务搭配使用。CFS 提供了标准的 NFS 及 CIFS/SMB 文件系统访问协议&#xff0c;可为计算服务提供共享的…

【Unity】【Pico】手柄摇杆控制第一人称移动和旋转

【Unity】【Pico】手柄摇杆控制第一人称移动和旋转 背景&#xff1a;开发影院系统 环境&#xff1a;Unity2021.3、PicoNeo3ProEye 描述&#xff1a;已经在Unity项目中实现第一人称WASD移动和鼠标旋转&#xff08;代码见我的其他博文&#xff09; 需求&#xff1a;希望项目在Pi…

Cobalt Strike Beacon 初探

背景 RTO I 的课程结束了&#xff0c;Cobalt Strike 算是会用了。然后继上一篇文章之后&#xff0c;我还没有机会用 Cobalt Strike Beacon 做一下 Windows Defender Bypass。之后会写。 另外&#xff0c;我也想问一下我自己&#xff0c;Cobalt Strike 里面最基本的 payload -…

Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-应用订阅端(北向应用)

之前实现了使用SpringbootNetty基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-设备终端&#xff08;南向设备&#xff09;&#xff0c;模拟设备发送的数据给到了AIOT平台&#xff0c;那么在第三方应用也需要去订阅AIOT平台的数据&#xff0c;以及对设备进行下发指令(消…

FastGithub的下载和使用

前言 github访问很不稳定&#xff0c;时断时续&#xff0c;有时候根本打不开&#xff01; 下载 方式一&#xff1a;官方地址下载&#xff08;有及时更新&#xff09; FastGithub1.1.7下载、FastGithub2.1.4_windows、FastGithub2.1.4_Linux、 更多 方式二&#xff1a;本地上传…