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),并且学习了其…...

html三级菜单
示例 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Menu Example</title> <link re…...

【人工智能】—基于成都市各区(市)县租房价格预测建模研究
引言 随着城市化进程的加速,人口流动日益频繁,租房市场作为城市生活的重要组成部分,其价格波动对居民生活质量和城市经济发展具有显著影响。成都市,作为中国西部地区的经济、文化、交通和科技中心,近年来吸引了大量人…...

3213. 最小代价构造字符串
Powered by:NEFU AB-IN Link 文章目录 3213. 最小代价构造字符串题意思路代码 3213. 最小代价构造字符串 题意 给你一个字符串 target、一个字符串数组 words 以及一个整数数组 costs,这两个数组长度相同。 设想一个空字符串 s。 你可以执行以下操作任意次数&a…...

提取重复数据
直接上控制台代码: Module Module1Sub Main()Console.WriteLine("请输入数据,以"",""相隔:")Dim str As String Console.ReadLineDim result From x In str.Split(",")Group By x Int…...

Go语言标准库之log和三方库zap
一、Log 1.1 logger基本使用 Go语言内置的log包实现了简单的日志服务。本包也提供了一个预定义的“标准”logger,可以通过调用函数Print系列(Print|Printf|Println)、Fatal系列(Fatal|Fatalf|Fatalln)、和Panic系列(Panic|Panicf|Panicln)来…...

Linux:进程终止和进程替换
Linux:Linux:进程终止和进程替换 一、进程终止1.1 进程退出场景和创建退出方式 1.2 exit 和 _exit区别二、进程程序替换2.1 进程替换函数2.2 函数解释及命名解释函数解释命名解释 2.3 单进程程序替换(无子进程)2.3.1 带l函数进程替…...

使用Java实现异步消息处理与队列消费
使用Java实现异步消息处理与队列消费 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在现代软件系统中,处理异步消息和队列消费是常见的需求。通过…...

使用C++实现ATM系统,谈谈思路及代码实现
🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…...

相机光学(二十四)——CRA角度
CRA角度 0.参考资料1.什么是CRA角度2.为什么 CRA 会导致luma shading3.为什么 CRA 会导致color shading4.CRA相差过大的具体表现5.CRA Matching6.怎样选择sensor的CRA 0.参考资料 1.芯片CRA角度与镜头的匹配关系(一) 2.芯片CRA角度与镜头选型的匹配关…...

python函数和c的区别有哪些
Python有很多内置函数(build in function),不需要写头文件,Python还有很多强大的模块,需要时导入便可。C语言在这一点上远不及Python,大多时候都需要自己手动实现。 C语言中的函数,有着严格的顺…...