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 common

import (
	"context"
	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
	"log"
)

var DBName string
var Neo4jCtx = context.Background()
var Driver neo4j.DriverWithContext

func initNeo4jConfig(c neo4jConfig) {
	var err error
	// Driver is thread safe: can be shared by multiple threads
	Driver, 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.Neo4jCtx
	session := 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) * pageSize
	params["size"] = pageSize

	res, 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.Dept

	for res.Next(ctx) {
		m := res.Record().AsMap()
		var d model.Dept
		err = 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 neo

import (
	"commerce/common"
	"commerce/model"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
	"strings"
	"sync"
)

var permissionLock sync.Mutex

func PagePermission(pageNo, pageSize int, name string) (*common.Page, error) {

	var ctx = common.Neo4jCtx
	session := 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) * pageSize
	params["size"] = pageSize

	res, 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.Permission

	for res.Next(ctx) {
		m := res.Record().AsMap()
		var r model.Permission
		err = 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.Records
	if records == nil || len(records) == 0 {
		return nil, err
	}

	m := records[0].AsMap()
	var r model.Permission
	err = 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.Records
	if 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.Records
	var ps = make([]model.Permission, len(records))

	for i, r := range records {
		var p model.Permission
		err = 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.Records
	var ps = make([]model.Permission, len(records))

	for i, r := range records {
		var p model.Permission
		err = mapstructure.Decode(r.AsMap(), &p)
		if err != nil {
			return nil, err
		}
		ps[i] = p
	}

	return ps, nil
}

角色操作neo4j dao:

package neo

import (
	"commerce/common"
	"commerce/model"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
	"strings"
	"sync"
)

var roleLock sync.Mutex

func PageRole(pageNo, pageSize int, name string) (*common.Page, error) {

	var ctx = common.Neo4jCtx
	session := 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) * pageSize
	params["size"] = pageSize

	res, 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.Role

	for res.Next(ctx) {
		m := res.Record().AsMap()
		var r model.Role
		err = 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.Records
	if records == nil || len(records) == 0 {
		return nil, err
	}

	m := records[0].AsMap()
	var r model.Role
	err = 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.Records
	if 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 string
	if 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 r
				unwind $permissionIdList as pId
				match (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
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WPF UI 界面布局 魔术棒 文字笔记识别 技能提升 布局功能扩展与自定义 继承Panel的对象,测量与排列 系列七

应用开发第一步 功能分类&#xff1a;页面上的功能区域划分。。。。需求分析 业务逻辑 数据流 功能模块 UI/UX 编码 测试 发布 功能开发与布局 不用显式的方式设定元素的尺寸 不使用屏幕坐标来指定位置 Grid 功能最强大&#xff0c;布局最灵活的容器…

代码提交错分支了怎么办?

你有么有遇到过正在开发的代码&#xff0c;提交到生产环境的分支去&#xff0c;遇到这种情况怎么办&#xff1f; 问题重现&#xff1a; 这段注释// AAAAAAAAAAA 本来应该写在dev分支的&#xff0c;现在提交并push到master分支了 现在第一步&#xff0c;撤回提交 第二步&…

第五届机械工程与智能制造国际学术会议(MEIM 2024,7月26-28)

第五届机械工程与智能制造国际学术会议(MEIM 2024) 计划2024年7月26-28日在中国辽宁锦州隆重举行。本次会议由辽宁理工学院主办。 会议主要围绕机械工程与智能制造等研究领域展开讨论&#xff0c;旨在为从事机械工程与智能制造研究的专家学者、程技术人员、技术研发人员提供一个…

Midjourney 如何使用参考图像来提升图像的准确性和相似度?

🧙🏼图像提示 🧙🏼‍♂️ 您可以使用图像作为提示的一部分来影响作业的构图、样式和颜色。图像提示可以单独使用,也可以与文本提示一起使用 - 尝试组合具有不同样式的图像以获得最令人兴奋的结果。 🛠️实际图像提示操作步骤 点击加号按钮,双击上传文件,把小黄猫…

SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

何曾几时&#xff0c;在 SwiftUI 开发中的秃头小码农们迫切需要一种能够读取当前滚动状态的方法。 在过去&#xff0c;他们往往需要借助于 UIKit 的神秘力量。不过这一切在 SwiftUI 6.0 中已成“沧海桑田”。 在本篇博文中&#xff0c;您将学到如下内容&#xff1a; 1. Scroll…

Anubi WebKey开启去中心化数字革命的新纪元

随着技术的飞速发展&#xff0c;Web3正在重新定义未来互联网的架构&#xff0c;标志着从集中式控制向去中心化自主的历史性转变。在这场全球性的技术演变中&#xff0c;Anubi WebKey不仅仅是一款前沿的智能设备&#xff0c;它代表的是一种划时代的技术革命&#xff0c;一个重塑…

24.【C语言】getchar putchar的使用

1.基本作用 用户输入字符&#xff0c;getchar()获取字符&#xff08;含\n:即键入的Enter&#xff09;&#xff08;字符本质上是以ASCII值或EOF&#xff08;-1&#xff09;存储的&#xff09;&#xff08;与scanf有区别&#xff09; putchar() 打印字符&#xff08;把得到的A…

图像畸变矫正与透视变换

图像畸变矫正与透视变换 Halcon自动生成的圆形棋盘格Halcon透视变换 Halcon自动生成的圆形棋盘格 示例程序&#xff1a; *生成棋圆形棋盘格 行 列 直径 直径/距离比值 gen_caltab (12, 9, 0.002, 0.5, caltab_12X9.descr, caltab.ps) *生成相机参数 焦距 畸变系数 X解析度 Y解…

计算云服务1

前言 一直以来&#xff0c;计算资源都是整个企业业务系统发展所需的大动脉&#xff0c;没有计算资源&#xff0c;企业业务就无法正常运行。在云计算的时代里&#xff0c;计算服务也是云服务中的第一大类服务&#xff0c;计算资源的重要性由此可见。本章&#xff0c;我们将带领…

【数据结构】常见四类排序算法

1. 插入排序 1.1基本思想&#xff1a; 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。实际中我们…

HTML5+JavaScript单词游戏

HTML5 JavaScript单词游戏 数据字典格式&#xff1a;每行一个 单词 &#xff0c;单词和解释用空格分隔&#xff0c;如 a art.一(个)&#xff1b;每一(个) ability n.能力&#xff1b;能耐&#xff0c;本领 able a.有能力的&#xff1b;出色的 baby n.婴儿&#xff1b;孩子…

NET程序开发可能会用到的一些资料文档

NET程序开发使用的一些资料文件&#xff0c;NET高级调试&#xff0c;NET关键技术深入解析&#xff0c;WPF专业编程指南&#xff0c;程序员求职攻略&#xff0c;WPF编程宝典等。 下载链接&#xff1a;https://download.csdn.net/download/qq_43307934/89518582

Python入门 2024/7/6

目录 数据容器入门 列表的定义语法 基本语法 嵌套列表 ​编辑 列表的下表索引 ​编辑 列表的常用操作 列表的常见方法 查找元素的下标 修改下标索引的值 插入元素 追加元素 追加一批元素 删除元素 删除某元素在列表中的第一个匹配项 清空列表内容 统计元素在…

【Unity URP】通过代码动态添加URP渲染通道RendererFeature

URP的渲染通道RendererFeature可以很方便的实现一些渲染问题,比如渲染顺序问题,遮挡后的材质替换等等。 那么我们如何通过代码来动态添加和修改呢? 首先我们需要获取到当前的URP配置文件,在对配置文件进行添加 1.通过反射获取当前UniversalRendererData 我们通过Graphic…

Linux:文件系统与日志分析

一、block与inode 1.1、概述 文件是存储在硬盘上的&#xff0c;硬盘的最小存储单位叫做“扇区”(sector)&#xff0c;每个扇区存储512字节。 一般连续八个扇区组成一个"块”(block)&#xff0c;一个块是4K大小&#xff0c;是文件存取的最小单位。 文件数据包括实际数据…

【数据分享】国家级旅游休闲街区数据(Excel/Shp格式/免费获取)

之前我们分享过从我国文化和旅游部官网整理的2018-2023年我国50个重点旅游城市星级饭店季度经营状况数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01;文化和旅游部官网上也分享有很多与旅游相关的常用数据&#xff0c;我们基于官网发布的名单文件整理得到全国…

.net 调用海康SDK的跨平台解决方案

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔序言 上2篇海康SDK使用以及常见的坑…

【JavaEE精炼宝库】文件操作(1)——基本知识 | 操作文件——打开实用性编程的大门

目录 一、文件的基本知识1.1 文件的基本概念&#xff1a;1.2 树型结构组织和目录&#xff1a;1.3 文件路径&#xff08;Path&#xff09;&#xff1a;1.4 二进制文件 VS 文本文件&#xff1a;1.5 其它&#xff1a; 二、Java 操作文件2.1 方法说明&#xff1a;2.2 使用演示&…

第十五章 Nest Pipe(内置及自定义)

NestJS的Pipe是一个用于数据转换和验证的特殊装饰器。Pipe可以应用于控制器&#xff08;Controller&#xff09;的处理方法&#xff08;Handler&#xff09;和中间件&#xff08;Middleware&#xff09;&#xff0c;用于处理传入的数据。它可以用来转换和验证数据&#xff0c;确…

软通动力子公司鸿湖万联最新成果SwanLink AI亮相世界人工智能大会

7月4日&#xff0c;2024世界人工智能大会暨人工智能全球治理高级别会议&#xff08;WAIC 2024&#xff09;在上海拉开帷幕&#xff0c;软通动力董事长兼首席执行官刘天文受邀出席开幕式。其间&#xff0c;软通动力携子公司鸿湖万联深度参与到大会各项活动中&#xff0c;并全面展…