golang结合neo4j实现权限功能设计
neo4j 是非关系型数据库之图形数据库,这里不再赘述。
传统关系数据库基于rbac实现权限, user ---- role ------permission,加上中间表共5张表。
如果再添上部门的概念:用户属于部门,部门拥有 角色,则又多了一层:
user------ dept *-------- * role *------*permission,
如果再引入子部门概念。。。
1.权限设计
1.1 关系
user ---*-----> role --- * --> permission
user ------> dept [-->父dept -->父dept --->父dept] ---可让子部门继承*-> role --- * --> permission
user ------> dept ---不允许子部门继承*-> role --- * --> permission
1.2 图
用户和部门之间的关系:
部门和子部门之间的关系:
部门和角色关系:
角色和权限关系:
后台配置界面:
1.查询一个用户拥有的权限集:
match paths=(admin:Admin{name:'zs'})-[:HAS_ROLE]->(:Role)-[:HAS]->(p:Permission)
return p.id as id, p.name as name, p.url as url
union
match (admin:Admin{name:'zs'})
match paths=(admin)-[:BELONG_TO]->(:Dept)-[:CHILD_OF*0..3]->(d:Dept)-[:ALLOW_INHERIT]->(:Role)
-[:HAS]->(p:Permission)
return p.id as id, p.name as name, p.url as url
union
match (admin:Admin{name:'zs'})
match paths=(admin)-[:BELONG_TO]->(d:Dept)-[:ALLOW_NO_INHERIT]->(:Role)
-[:HAS]->(p:Permission)
return p.id as id, p.name as name, p.url as url
查询用户权限集:链路:
match paths=(admin:Admin{name:'xiaolan'})-[:HAS_ROLE]->(:Role)-[:HAS]->(p:Permission)
return paths
union
match (admin:Admin{name:'xiaolan'})
match paths=(admin)-[:BELONG_TO]->(:Dept)-[:CHILD_OF*0..3]->(d:Dept)-[:ALLOW_INHERIT]->(:Role)
-[:HAS]->(p:Permission)
return paths
union
match (admin:Admin{name:'xiaolan'})
match paths=(admin)-[:BELONG_TO]->(d:Dept)-[:ALLOW_NO_INHERIT]->(:Role)
-[:HAS]->(p:Permission)
return paths
2.判断一个用户是否可访问特定资源(url, 通过权限体现此概念):
match c=(admin:Admin{name:'xiaoQ'})-[:HAS_ROLE]->(:Role)-[:HAS]->(p:Permission{url:'/api/v2/goods/list'})
return count(c) as accessCount
union
match c=(admin:Admin{name:'xiaoQ'})-[:BELONG_TO]->(:Dept)-[:CHILD_OF*0..3]->(d:Dept)-[:ALLOW_INHERIT]->(:Role)
-[:HAS]->(p:Permission {url:'/api/v2/goods/list'})
where not ((admin)-[:BELONG_TO]->(:Dept)-[:CHILD_OF*0..3]->(d:Dept)-[:DENY]->(:Role))
return count(c) as accessCount
union
match c=(admin:Admin{name:'xiaoQ'})-[:BELONG_TO]->(d:Dept)-[:ALLOW_NO_INHERIT]->(:Role)
-[:HAS]->(p:Permission {url:'/api/v2/goods/list'})
return count(c) as accessCount
3.查看谁拥有指定资源(url) 的权限:
match (p:Permission{url:'/api/v2/admin/list'})
match (admin:Admin)-[:HAS_ROLE]->(:Role)-[:HAS]->(p)
return admin.id as id, admin.name as name
union
match (p:Permission{url:'/api/v2/admin/list'})
match (admin:Admin)-[:BELONG_TO]->(:Dept)-[:CHILD_OF*0..3]->(d:Dept)-[:ALLOW_INHERIT]->(:Role)-[:HAS]->(p)
where not ((admin)-[:BELONG_TO]->(:Dept)-[:CHILD_OF*0..3]->(d:Dept)-[:DENY]->(:Role))
return admin.id as id, admin.name as name
union
match (p:Permission{url:'/api/v2/admin/list'})
match (admin:Admin)-[:BELONG_TO]->(d:Dept)-[:ALLOW_NO_INHERIT]->(:Role)-[:HAS]->(p)
return admin.id as id, admin.name as name
附上完整关系图:
下面介绍golang代码整合处理:
先上成型图:
1.启动项目时读取配置,初始化neo4j driver:
package commonimport ("context""github.com/neo4j/neo4j-go-driver/v5/neo4j""log"
)var DBName string
var Neo4jCtx = context.Background()
var Driver neo4j.DriverWithContextfunc initNeo4jConfig(c neo4jConfig) {var err error// Driver is thread safe: can be shared by multiple threadsDriver, err = neo4j.NewDriverWithContext(c.DbUri, neo4j.BasicAuth(c.DbUser, c.DbPwd, ""))if err != nil {log.Println("new neo4j driver with context failed:", err.Error())return}err = Driver.VerifyConnectivity(Neo4jCtx)if err != nil {log.Printf("init neo4j failed:%s\n", c)return}log.Println("neo4j connection established...")DBName = c.DBName
}
2.neo4j列表分页查询数据
func PageDept(pageNo, pageSize int, name string, queryParentOnly string, parent uint64) (*common.Page, error) {var ctx = common.Neo4jCtxsession := common.Driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: common.DBName})defer session.Close(ctx)tx, err := session.BeginTransaction(ctx)if err != nil {return nil, err}defer tx.Rollback(ctx)whereSql, params := composeDeptSearchQuerySql(name, queryParentOnly, parent)res, err := tx.Run(ctx, whereSql+` return count(d.id) as c`, params)if err != nil {return nil, err}record, err := res.Single(ctx)if err != nil {return nil, err}var c = int64(0)if r, flg := record.Get("c"); flg && r != nil {c = r.(int64)}// 没有数据if c == int64(0) {return common.NewPage([]model.Dept{}, pageNo, pageSize, 0), nil}params["s"] = (pageNo - 1) * pageSizeparams["size"] = pageSizeres, err = tx.Run(ctx, whereSql + ` return `+row+` order by d.id SKIP $s limit $size`, params)if err != nil {return nil, err}var ds []model.Deptfor res.Next(ctx) {m := res.Record().AsMap()var d model.Depterr = mapstructure.Decode(m, &d)if err != nil {return nil, err}d.CreatedTimeStr = d.CreatedTime.Format(time.DateTime)ds = append(ds, d)}return common.NewPage(ds, pageNo, pageSize, int(c)), nil
}func composeDeptSearchQuerySql(name string, only string, parent uint64) (string, map[string]any) {var params = map[string]any{}sb := strings.Builder{}sb.WriteString("match (d:Dept) ")// 没有条件查询if name == "" && only == "" && parent == 0 {return sb.String(), params}// 只查询父分类if only == "on" {sb.WriteString(" where d.parent = 0")return sb.String(), params}// 查询指定的父分类if parent > 0 {sb.WriteString("-[:CHILD_OF]->(:Dept{id: $parent})")//sb.WriteString(" where d.parent = $parent")params["parent"] = parent}// 有部门名称的模糊查询if len(name) > 0 {sb.WriteString(" where d.name CONTAINS $name")params["name"] = name}return sb.String(), params
}
权限permission dao for neo4j操作:
package neoimport ("commerce/common""commerce/model""fmt""github.com/mitchellh/mapstructure""github.com/neo4j/neo4j-go-driver/v5/neo4j""strings""sync"
)var permissionLock sync.Mutexfunc PagePermission(pageNo, pageSize int, name string) (*common.Page, error) {var ctx = common.Neo4jCtxsession := common.Driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: common.DBName})defer session.Close(ctx)tx, err := session.BeginTransaction(ctx)if err != nil {return nil, err}defer tx.Rollback(ctx)whereSql, params := composePermissionSearchQuerySql(name)res, err := tx.Run(ctx, whereSql+` return count(p.id) as c`, params)if err != nil {return nil, err}record, err := res.Single(ctx)if err != nil {return nil, err}var c = int64(0)if r, flg := record.Get("c"); flg && r != nil {c = r.(int64)}// 没有数据if c == int64(0) {return common.NewPage([]model.Permission{}, pageNo, pageSize, 0), nil}params["s"] = (pageNo - 1) * pageSizeparams["size"] = pageSizeres, err = tx.Run(ctx, whereSql+` return p.id as id, p.name as name, p.priority as priority, p.status as status, p.public_res_flg as public_res_flg order by p.id SKIP $s limit $size`, params)if err != nil {return nil, err}var rs []model.Permissionfor res.Next(ctx) {m := res.Record().AsMap()var r model.Permissionerr = mapstructure.Decode(m, &r)if err != nil {return nil, err}rs = append(rs, r)}return common.NewPage(rs, pageNo, pageSize, int(c)), nil
}func GetPermissionById(id uint64) (*model.Permission, error) {sqlTpl := `match (p:Permission {id: $id}) return p.id as id, p.name as name, p.priority as priority, p.status as status, p.public_res_flg as public_res_flg`res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": id,}, neo4j.EagerResultTransformer,neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting(),)if err != nil {return nil, err}records := res.Recordsif records == nil || len(records) == 0 {return nil, err}m := records[0].AsMap()var r model.Permissionerr = mapstructure.Decode(m, &r)if err != nil {return nil, err}return &r, nil
}func AddPermission(c model.Permission) (uint64, error) {permissionLock.Lock()defer permissionLock.Unlock()res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, `match (p:Permission {name: $name}) return p.id as id limit 1`, map[string]any{"name": c.Name},neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting())if err != nil {return 0, err}if res.Records != nil && len(res.Records) > 0 {return 0, fmt.Errorf("%s 已存在,不允许创建", c.Name)}id := common.IdGenerator.Generate()sqlTpl := `create (p:Permission {id: $id, name: $name, priority: $priority, status:$status, public_res_flg: $publicResFlg, created_time: datetime({timezone: 'Asia/Shanghai'}), updated_time: datetime({timezone: 'Asia/Shanghai'})})`_, err = neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": id,"name": c.Name,"priority": c.Priority,"status": c.Status,"publicResFlg": c.PublicResFlg,}, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName))if err != nil {return 0, err}return id, nil
}func UpdatePermission(c model.Permission) (bool, error) {permissionLock.Lock()defer permissionLock.Unlock()res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, `match (p:Permission {name: $name}) return p.id as id limit 1`, map[string]any{"name": c.Name},neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting())if err != nil {return false, err}records := res.Recordsif records != nil && len(records) > 0 {r, _ := records[0].Get("id")dbId := uint64(r.(int64))if dbId != c.Id {return false, fmt.Errorf("%s 已存在,不允许更新部门名称为此值", c.Name)}}sqlTpl := `match (p:Permission {id: $id}) set p.name=$name, p.priority=$priority,p.public_res_flg=$publicResFlg, p.updated_time=datetime({timezone: 'Asia/Shanghai'})`res, err = neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": c.Id,"name": c.Name,"priority": c.Priority,"publicResFlg": c.PublicResFlg,}, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName))if err != nil {return false, err}return res.Summary.Counters().ContainsUpdates(), nil
}func composePermissionSearchQuerySql(name string) (string, map[string]any) {var params = map[string]any{}sb := strings.Builder{}sb.WriteString("match (p:Permission) ")// 没有条件查询if name == "" {return sb.String(), params}// 有权限名称的模糊查询if len(name) > 0 {sb.WriteString(" where p.name CONTAINS $name")params["name"] = name}return sb.String(), params
}func UpdatePermissionStatus(id uint64, status int8) error {sqlTpl := `match (p:Permission {id: $id}) set p.status = $status, p.updated_time=datetime({timezone: 'Asia/Shanghai'})`_, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": id,"status": status,}, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName))if err != nil {return err}return nil
}func ListAllPermission() ([]model.Permission, error) {res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver,`match (p:Permission) where p.status = $status return p.id as id, p.name as name order by p.priority`,map[string]any{"status": 1},neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting())if err != nil {return nil, err}records := res.Recordsvar ps = make([]model.Permission, len(records))for i, r := range records {var p model.Permissionerr = mapstructure.Decode(r.AsMap(), &p)if err != nil {return nil, err}ps[i] = p}return ps, nil
}func ListPermissionByRoleId(roleId uint64) ([]model.Permission, error) {res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver,`match (p:Permission {status: $status})<-[:HAS]-(r:Role {id: $roleId}) return distinct p.id as id, p.name as name`,map[string]any{"status": 1, "roleId": roleId},neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting())if err != nil {return nil, err}records := res.Recordsvar ps = make([]model.Permission, len(records))for i, r := range records {var p model.Permissionerr = mapstructure.Decode(r.AsMap(), &p)if err != nil {return nil, err}ps[i] = p}return ps, nil
}
角色操作neo4j dao:
package neoimport ("commerce/common""commerce/model""fmt""github.com/mitchellh/mapstructure""github.com/neo4j/neo4j-go-driver/v5/neo4j""strings""sync"
)var roleLock sync.Mutexfunc PageRole(pageNo, pageSize int, name string) (*common.Page, error) {var ctx = common.Neo4jCtxsession := common.Driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: common.DBName})defer session.Close(ctx)tx, err := session.BeginTransaction(ctx)if err != nil {return nil, err}defer tx.Rollback(ctx)whereSql, params := composeRoleSearchQuerySql(name)res, err := tx.Run(ctx, whereSql+` return count(r.id) as c`, params)if err != nil {return nil, err}record, err := res.Single(ctx)if err != nil {return nil, err}var c = int64(0)if r, flg := record.Get("c"); flg && r != nil {c = r.(int64)}// 没有数据if c == int64(0) {return common.NewPage([]model.Role{}, pageNo, pageSize, 0), nil}params["s"] = (pageNo - 1) * pageSizeparams["size"] = pageSizeres, err = tx.Run(ctx, whereSql+` return r.id as id, r.name as name, r.priority as priority, r.status as status order by r.id SKIP $s limit $size`, params)if err != nil {return nil, err}var rs []model.Rolefor res.Next(ctx) {m := res.Record().AsMap()var r model.Roleerr = mapstructure.Decode(m, &r)if err != nil {return nil, err}rs = append(rs, r)}return common.NewPage(rs, pageNo, pageSize, int(c)), nil
}func GetRoleById(id uint64) (*model.Role, error) {sqlTpl := `match (r:Role {id: $id}) return r.id as id, r.name as name, r.priority as priority, r.status as status`res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": id,}, neo4j.EagerResultTransformer,neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting(),)if err != nil {return nil, err}records := res.Recordsif records == nil || len(records) == 0 {return nil, err}m := records[0].AsMap()var r model.Roleerr = mapstructure.Decode(m, &r)if err != nil {return nil, err}return &r, nil
}func AddRole(c model.Role) (uint64, error) {roleLock.Lock()defer roleLock.Unlock()res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, `match (r:Role {name: $name}) return r.id as id limit 1`, map[string]any{"name": c.Name},neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting())if err != nil {return 0, err}if res.Records != nil && len(res.Records) > 0 {return 0, fmt.Errorf("%s 已存在,不允许创建", c.Name)}id := common.IdGenerator.Generate()sqlTpl := `create (r:Role {id: $id, name: $name,priority: $priority, status:$status, created_time: datetime({timezone: 'Asia/Shanghai'}), updated_time: datetime({timezone: 'Asia/Shanghai'})})`_, err = neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": id,"name": c.Name,"priority": c.Priority,"status": c.Status,}, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName))if err != nil {return 0, err}return id, nil
}func UpdateRole(c model.Role) (bool, error) {roleLock.Lock()defer roleLock.Unlock()res, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, `match (r:Role {name: $name}) return r.id as id limit 1`, map[string]any{"name": c.Name},neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName),neo4j.ExecuteQueryWithReadersRouting())if err != nil {return false, err}records := res.Recordsif records != nil && len(records) > 0 {r, _ := records[0].Get("id")dbId := uint64(r.(int64))if dbId != c.Id {return false, fmt.Errorf("%s 已存在,不允许更新部门名称为此值", c.Name)}}sqlTpl := `match (r:Role {id: $id}) set r.name=$name, r.priority=$priority, r.updated_time=datetime({timezone: 'Asia/Shanghai'})`res, err = neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": c.Id,"name": c.Name,"priority": c.Priority,}, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName))if err != nil {return false, err}return res.Summary.Counters().ContainsUpdates(), nil
}func composeRoleSearchQuerySql(name string) (string, map[string]any) {var params = map[string]any{}sb := strings.Builder{}sb.WriteString("match (r:Role) ")// 没有条件查询if name == "" {return sb.String(), params}// 有角色名称的模糊查询if len(name) > 0 {sb.WriteString(" where r.name CONTAINS $name")params["name"] = name}return sb.String(), params
}func UpdateRoleStatus(id uint64, status int8) error {sqlTpl := `match (r:Role {id: $id}) set r.status = $status, r.updated_time=datetime({timezone: 'Asia/Shanghai'})`_, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"id": id,"status": status,}, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName))if err != nil {return err}return nil
}func AttachRolePermissionList(roleId uint64, permissionIdList []uint64) error {var sqlTpl stringif len(permissionIdList) == 0 {sqlTpl = `match (:Role {id: $roleId})-[rel:HAS]->(:Permission) delete rel`} else {sqlTpl = `match (r:Role {id: $roleId})CALL {match (r:Role {id: $roleId})-[rel:HAS]->(:Permission) delete rel }with runwind $permissionIdList as pIdmatch (p:Permission {id: pId})merge (r)-[:HAS]->(p)`}_, err := neo4j.ExecuteQuery(common.Neo4jCtx, common.Driver, sqlTpl, map[string]any{"roleId": roleId,"permissionIdList": permissionIdList,}, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(common.DBName))if err != nil {return err}return nil
}
相关文章:

golang结合neo4j实现权限功能设计
neo4j 是非关系型数据库之图形数据库,这里不再赘述。 传统关系数据库基于rbac实现权限, user ---- role ------permission,加上中间表共5张表。 如果再添上部门的概念:用户属于部门,部门拥有 角色,则又多了一层: user-…...

java 参数传递(尤其注意参数是对象的情况)
8大基本数据类型为 值传递 类和数组为 引用传递,传递的是地址 但是要注意虽然类是引用传递,但是要注意,调用方法是新开一个栈 因此如果进行p null或者 Person p new Person()等语句,要格外注意: 如果主函数再次输出…...
拼音字符串相似度
拼音字符串相似度 拼音字符串相似度介绍参考代码**编辑距离****余弦相似度****Jaccard相似度**参考文档拼音字符串相似度 介绍 拼音相似度是指在拼音转换后,两个拼音字符串之间的相似程度。常用的拼音相似度度量方法包括编辑距离、余弦相似度和 Jaccard 相似度等。 编辑距离…...
如何创建一个基本的Mojolicious Web应用:探索Perl的现代Web框架
如何创建一个基本的Mojolicious Web应用:探索Perl的现代Web框架 Mojolicious是一个用Perl编写的简单、优雅的Web开发框架,它提供了一套丰富的工具和方法,让开发者能够快速构建高性能的Web应用。本文将详细介绍如何创建一个基本的Mojolicious…...
FPGA/数字IC复习八股
一、FPGA概念,与数字IC的区别 二、FPGA底层逻辑 三、同步电路、异步电路以及优缺点 四、同步复位、异步复位、异步复位同步释放 深入理解复位---同步复位,异步复位,异步复位同步释放(含多时钟域)_画出支持异步复位dff的电路图…...

Android 简单快速实现 下弧形刻度尺(滑动事件)
效果图: 直接上代码: package com.my.view;import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Pai…...

【Go】常见的变量与常量
变量 常见的变量声明方式 一、声明单个变量的多种方式 1.声明一个变量初始化一个值 //声明变量 默认值是0,var a int//初始化一个值a 1fmt.Println(a) 2. 在初始化的时候省去数据类型,通过值自动匹配当前的变量的数据类型 var b 2fmt.Println(&quo…...

Qt使用sqlite数据库及项目实战
一.sqlite使用介绍 在Qt中使用SQLite数据库非常简单,SQLite是一个轻量级的嵌入式数据库,不需要单独的数据库服务器,完全使用本地文件来存储数据。 当在Qt中使用SQLite数据库时,需要涉及到一些SQL语句以及Qt中的相关函数…...

开源模型应用落地-FastAPI-助力模型交互-进阶篇(一)
一、前言 FastAPI 的高级用法可以为开发人员带来许多好处。它能帮助实现更复杂的路由逻辑和参数处理,使应用程序能够处理各种不同的请求场景,提高应用程序的灵活性和可扩展性。 在数据验证和转换方面,高级用法提供了更精细和准确的控制&#…...
精准选择广告工具,提升推广效果
在考虑使用巨量引擎之前,我们首先要明白它的本质。巨量引擎是一个付费广告平台,包含了多种推广工具,如巨量ID、巨量千川、巨量本地推,以及企业蓝V等。很多人希望通过这个平台提升抖音账号的流量和曝光度,但真正有效的流…...
Swagger的原理及应用详解(六)
本系列文章简介: 在当今快速发展的软件开发领域,特别是随着微服务架构和前后端分离开发模式的普及,API(Application Programming Interface,应用程序编程接口)的设计与管理变得愈发重要。一个清晰、准确且易于理解的API文档不仅能够提升开发效率,还能促进前后端开发者之…...

世界人工智能大会今日开幕:人工智能如何成为引领发展的新引擎
人工智能如何成为引领上海发展的新引擎?今日(7月4日)开幕的2024世界人工智能大会暨人工智能全球治理高级别会议(简称“WAIC 2024”)将带来答案。 “新”和“全”是今年大会的亮点所在:“新”在于技术新&…...

tinyshop项目部署
参考软件测试之测试用例设计(四)_管理后台 测试用例-CSDN博客 1、下载xampp 2、修改apache和mysql的端口分别为4431 ,8013和3306 3、访问页面:输入ip:端口号,出现以下页面即成功 4、安装tinyshop商城 将解压的tinys…...

Gemini for China 大更新,现已上架 Android APP!
官网:https://gemini.fostmar.online/ Android APP:https://gemini.fostmar.online/gemini_1.0.apk 一、Android APP 如果是 Android 设备,则会直接识别到并给下载链接。PC 直接对话即可。 二、聊天记录 现在 Gemini for Chinaÿ…...
Unity渲染管线介绍
Unity中的渲染管线渲染场景主要分为三个阶段 剔除(Culling) 剔除摄像机不可见对象(视锥体剔除Frustum Culling)和被遮挡对象(遮挡剔除Occlusion Culling)。 渲染(Rendering) 将可见…...

【UML用户指南】-31-对体系结构建模-制品图
目录 1、对源代码建模 2、对可执行程序的发布建模 3、对物理数据库建模 4、对可适应系统建模 制品图是对面向对象系统的物理方面进行建模时要用到的两种图之一。制品图展示一组制品之间的组织以及其间依赖关系。 利用制品图可以对系统的静态实现视图建模。这包括对存在于结…...

《基于 Kafka + Flink + ES 实现危急值处理措施推荐和范围校准》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,欢迎多多交流。&am…...

计算机的进制转换
十进制:以0-9这九个数字组成。 二进制:由0和1两个数字组成。 八进制:由0-7数字组成,不存在8和9。 十六进制:由0-9和A-F组成。A-F对应的是10-15。 ____________ 十进制 1 2 3 4 5 6 7 8 9 10 二进制 0 1 10 11 1…...

String类(STL开始)
相信大家都知道STL在C中的重要性,作为其模板库中的一部分,包含了常见的数据结构和算法,是C的标准库 而我们今天要讲的String类(String底层是一个字符顺序数组的顺序表对象,可以归类为容器),其实…...

【数据结构】单链表:数据结构中的舞者,穿梭于理论与实践的舞池
欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 数据结构与算法 先赞后看,已成习惯 创作不易,多多支持! 一、链表的概念和结构 1.1 链表的概念 在上一篇文章中,我们了解了线性表(linear list),并且学习了其…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...
八、【ESP32开发全栈指南:UDP客户端】
1. 环境准备 安装ESP-IDF v4.4 (官方指南)确保Python 3.7 和Git已安装 2. 创建项目 idf.py create-project udp_client cd udp_client3. 完整优化代码 (main/main.c) #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h&…...

XXE漏洞知识
目录 1.XXE简介与危害 XML概念 XML与HTML的区别 1.pom.xml 主要作用 2.web.xml 3.mybatis 2.XXE概念与危害 案例:文件读取(需要Apache >5.4版本) 案例:内网探测(鸡肋) 案例:执行命…...

JS面试常见问题——数据类型篇
这几周在进行系统的复习,这一篇来说一下自己复习的JS数据结构的常见面试题中比较重要的一部分 文章目录 一、JavaScript有哪些数据类型二、数据类型检测的方法1. typeof2. instanceof3. constructor4. Object.prototype.toString.call()5. type null会被判断为Obje…...