16 | 实现简洁架构的 Store 层
提示:
- 所有体系课见专栏:Go 项目开发极速入门实战课;
- 欢迎加入 云原生 AI 实战 星球,12+ 高质量体系课、20+ 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);
- 本节课最终源码位于 fastgo 项目的 feature/s12 分支;
- 更详细的课程版本见:Go 项目开发中级实战课:25 | 业务实现(2):实现 Store 层代码
上一节课我们实现了 Store 层依赖的数据结构。本节课,我们就可以来实现 Store 层的代码。
Store 层代码用来跟数据库或者其他第三方微服务进行交互,实现 Store 层的思路如下:
- 定义一个 Store 层接口,用来定义 Store 层需要实现的方法。在 Go 开发中接口通常会被命名为
XXXer
、XXXInterface
、IXXX
,其中XXX
是接口的功能描述符。这里,将 Store 层的接口命名为IStore
。Biz 层的接口同样使用了该命名方式,命名为IBiz
,以保持整个项目接口命名方式的统一; - 创建一个结构体,该结构体用来实现 Store 层的核心方法。通常会在该结构体中包含一个
*gorm.DB
对象,用于与数据库交互进行 CRUD 操作; - 实现一个
NewStore
函数,用于创建 Store 层的实例; - 为了方便直接通过 store 包引用 Store 层实例,我们可以设置一个包级别的 Store 实例。访问 Store 层的全局实例在项目开发中很少,也不建议使用。但在某些场景下还是挺好用的。这里,保留灵活的扩展能力;
- 为了避免实例被重复创建,通常需要使用
sync.Once
来确保实例只被初始化一次。
IStore 接口定义及实现
Store 层的代码实现位于 feature/s12 分支的 internal/apiserver/store/ 目录下。核心实现位于 internal/apiserver/store/store.go 文件中,代码如代码清单 16-1 所示:
代码清单 16-1:Store 层代码实现
package storeimport ("context""sync""github.com/onexstack/onexstack/pkg/store/where""gorm.io/gorm"
)var (once sync.Once// 全局变量,方便其它包直接调用已初始化好的 datastore 实例.S *datastore
)// IStore 定义了 Store 层需要实现的方法.
type IStore interface {// 返回 Store 层的 *gorm.DB 实例,在少数场景下会被用到.DB(ctx context.Context, wheres ...where.Where) *gorm.DBTX(ctx context.Context, fn func(ctx context.Context) error) errorUser() UserStorePost() PostStore
}// transactionKey 用于在 context.Context 中存储事务上下文的键.
type transactionKey struct{}// datastore 是 IStore 的具体实现.
type datastore struct {core *gorm.DB// 可以根据需要添加其他数据库实例// fake *gorm.DB
}// 确保 datastore 实现了 IStore 接口.
var _ IStore = (*datastore)(nil)// NewStore 创建一个 IStore 类型的实例.
func NewStore(db *gorm.DB) *datastore {// 确保 S 只被初始化一次once.Do(func() {S = &datastore{db}})return S
}// DB 根据传入的条件(wheres)对数据库实例进行筛选.
// 如果未传入任何条件,则返回上下文中的数据库实例(事务实例或核心数据库实例).
func (store *datastore) DB(ctx context.Context, wheres ...where.Where) *gorm.DB {db := store.core// 从上下文中提取事务实例if tx, ok := ctx.Value(transactionKey{}).(*gorm.DB); ok {db = tx}// 遍历所有传入的条件并逐一叠加到数据库查询对象上for _, whr := range wheres {db = whr.Where(db)}return db
}// TX 返回一个新的事务实例.
// nolint: fatcontext
func (store *datastore) TX(ctx context.Context, fn func(ctx context.Context) error) error {return store.core.WithContext(ctx).Transaction(func(tx *gorm.DB) error {ctx = context.WithValue(ctx, transactionKey{}, tx)return fn(ctx)},)
}// Users 返回一个实现了 UserStore 接口的实例.
func (store *datastore) User() UserStore {return newUserStore(store)
}// Posts 返回一个实现了 PostStore 接口的实例.
func (store *datastore) Post() PostStore {return newPostStore(store)
}
DB
方法会尝试从 context 中获取事务实例,如果没有则直接返回 *gorm.DB
类型的实例。TX
方法则将*gorm.DB
类型的实例注入到 context 中。通过 DB
和 TX
在 Biz 层实现事务,在 Store 层执行事务。
IStore
接口中的 User()
方法和 Post()
方法,分别返回 User 资源的 Store 层方法和 Post 资源的 Store 层方法。这种设计方式其实是软件开发中的抽象工厂模式。通过使用不同的方法,创建不同的对象,对象之间相互独立,以此提高代码的可维护性。另外使用这种方法,也能有效提高资源接口的标准化。标准化的资源接口更易理解和使用。
代码清单 16-1 中,使用以下代码创建 *datastore
类型的实例:
// NewStore 创建一个 IStore 类型的实例.
func NewStore(db *gorm.DB) *datastore {// 确保 S 只被初始化一次once.Do(func() {S = &datastore{db}})return S
}
上述代码使用了 sync.Once
来确保实例只被初始化一次。实例创建完成后,将其赋值给包级变量 S
,以便通过 store.S.User().Create()
来调用 Store 层接口。在 Go 的最佳实践中,建议尽量减少使用包级变量,因为包级变量的状态通常难以感知,会增加维护的复杂度。然而,这并非绝对规则,可以根据实际需要选择是否设置包级变量来简化开发。
UserStore 接口定义及实现
为了提高代码的可维护性,将 User 资源的 Store 代码单独存放在 internal/apiserver/store/user.go 文件中。UserStore
接口定义如下:
// UserStore 定义了 user 模块在 store 层所实现的方法.
type UserStore interface {Create(ctx context.Context, obj *model.UserM) errorUpdate(ctx context.Context, obj *model.UserM) errorDelete(ctx context.Context, opts *where.Options) errorGet(ctx context.Context, opts *where.Options) (*model.UserM, error)List(ctx context.Context, opts *where.Options) (int64, []*model.UserM, error)UserExpansion
}// UserExpansion 定义了用户操作的附加方法.
type UserExpansion interface{}
UserStore
接口中的方法分为两大类:资源标准 CURD 方法和扩展方法。通过将方法分类,可以进一步提高接口中方法的可维护性和代码的可读性。将方法分为标准方法和扩展方法的开发技巧,在 Kuberentes client-go 项目中被大量使用,fastgo 的设计思路正是来源于 client-go 的设计。
创建用户:Create 方法实现
userStore
结构体实现了 UserStore
接口。userStore
结构体的 Create
方法实现如下:
// userStore 是 UserStore 接口的实现.
type userStore struct {store *datastore
}// 确保 userStore 实现了 UserStore 接口.
var _ UserStore = (*userStore)(nil)// newUserStore 创建 userStore 的实例.
func newUserStore(store *datastore) *userStore {return &userStore{store}
}// Create 插入一条用户记录.
func (s *userStore) Create(ctx context.Context, obj *model.UserM) error {if err := s.store.DB(ctx).Create(&obj).Error; err != nil {slog.Error("Failed to insert user into database", "err", err, "user", obj)return errorsx.ErrDBWrite.WithMessage(err.Error())}return nil
}
在 Create
方法中,会调用 s.store.DB(ctx)
方法尝试从 context 中获取事务实例,如果没有则直接返回*gorm.DB
类型的实例,并调用 *gorm.DB
提供的 Create
方法,完成数据库表记录的插入操作。
在 Create
方法中,如果插入失败,根据本课程第 13 节课的 fastgo 错误返回规范,直接返回了 errorsx.ErrorX
类型的错误 errno.ErrDBWrite
,并设置了自定义错误消息:err.Error()
。
在 Go 项目开发中,直接返回 gorm 包的错误,可能会暴露一些敏感信息,但绝大部分项目中,可以直接返回 gorm 包的报错信息。将 gorm 包的报错信息,直接在接口中返回,可以大幅提高接口失败时的排障效率。
删除用户:Delete 方法实现
userStore
结构体的 Delete
接口代码实现如下:
// Delete 根据条件删除用户记录.
func (s *userStore) Delete(ctx context.Context, opts *where.Options) error {err := s.store.DB(ctx, opts).Delete(new(model.UserM)).Errorif err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {slog.Error("Failed to delete user from database", "err", err, "conditions", opts)return errorsx.ErrDBWrite.WithMessage(err.Error())}return nil
}
在 Delete
方法中,会判断删除记录时的错误类型是否是 gorm.ErrRecordNotFound
,如果不是会返回自定义错误,如果是则返回删除成功,以此实现幂等删除。在 Go 项目开发中,删除行为在绝大部分项目中是一个幂等的操作。
查询用户列表:List 方法实现
userStore
结构体的 List
方法实现如下:
// List 返回用户列表和总数.
// nolint: nonamedreturns
func (s *userStore) List(ctx context.Context, opts *where.Options) (count int64, ret []*model.UserM, err error) {err = s.store.DB(ctx, opts).Order("id desc").Find(&ret).Offset(-1).Limit(-1).Count(&count).Errorif err != nil {slog.Error("Failed to list users from database", "err", err, "conditions", opts)err = errorsx.ErrDBRead.WithMessage(err.Error())}return
}
在 List
方法中,会调用 s.store.DB(ctx, opts)
方法,根据 opts
入参来配置 *gorm.DB
实例的查询条件。查询的记录会按数据库 id
字段降序排列(从大到小的顺序)。将最新的记录放在返回列表的最前面可以提高返回数据的阅读体验,也是大部分平台的默认排序规则。
整个 UserStore
接口中的方法实现较为简洁,仅直接对数据库记录进行增删改查操作,并未封装任何业务逻辑。Post 资源的 Store 层代码实现与 User 资源保持一致,故本课程不再解读 Post 资源的 Store 层实现。
在 Go 项目开发中,不少开发者会在 Store 层封装业务代码,为了实现不同的查询条件,会在 Store 层封装很多查询类方法,例如:ListUser
、ListUserByName
、ListUserByID
等。这都会使 Store 层代码变得臃肿,难以维护。其实 Store 层只需要对数据库记录进行简单的增删改查即可。对插入数据或查询数据的处理可以放在业务逻辑层。对查询条件的定制,可以通过提供灵活的查询参数来实现。
where 查询条件实现
为了实现在查询数据库记录时,配置灵活的查询参数,fastgo 项目引用了 onexstack 项目的 where 包,包名为 github.com/onexstack/onexstack/pkg/store/where
。整个 OneX 技术体系,都通过 where 包来定制化查询条件。
where 包更多的设计方法、思考及使用方法可以参考 云原生 AI 实战营 中 Go 项目开发中级实战课 的第 25 | 讲 业务实现(2):实现 Store 层代码。
相关文章:
16 | 实现简洁架构的 Store 层
提示: 所有体系课见专栏:Go 项目开发极速入门实战课;欢迎加入 云原生 AI 实战 星球,12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);本节课最终…...
华为hcia——Datacom实验指南——以太网帧和IPV4数据包格式(一)
实验开始 第一步配置环境 第二步配置客户端 如图所示,我们把客户端的ip配置成192.168.1.10,网关设为192.168.1.1 第三步配置交换机1 system-view sysname LSW1 vlan batch 10 interface ethernet0/0/1 port link-type access port default vlan 10 qu…...
ubuntu软件——视频、截图、图片、菜单自定义等
视频软件,大部分的编码都能适应 sudo apt install vlc图片软件 sudo apt install gwenview截图软件 sudo apt install flameshot设置快捷键 flameshot flameshot gui -p /home/cyun/Pictures/flameshot也就是把它保存到一个自定义的路径 菜单更换 sudo apt r…...
CSS中z-index使用详情
定位层级 1.定位元素的显示层级比普通元素高,无论什么定位,显示层级都是一样的; 2.如果位置发生重叠,默认情况是:后面的元素,会显示在前面元素之上; 3.可以通过CSS属性z-index调整元素的显示层级; 4.z-index的属性值是数字,没有单位,值越大显示层级越高; 5.只有定位的元素…...
qt 自带虚拟键盘的编译使用记录
一、windows 下编译 使用vs 命令窗口,分别执行: qmake CONFIG"lang-en_GB lang-zh_CN" nmake nmake install 如果事先没有 指定需要使用的输入法语言就进行过编译,则需要先 执行 nmake distclean 清理后执行 qmake 才能生效。 …...
杨辉三角形(信息学奥赛一本通-2043)
【题目描述】 例5.11 打印杨辉三角形的前n(2≤n≤20)行。杨辉三角形如下图: 当n5时 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 输出: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 【输入】 输入行数n。 【输出】 输出如题述三角形。n行&#…...
CentOS 7 系统上安装 SQLite
1. 检查系统更新 在安装新软件之前,建议先更新系统的软件包列表,以确保使用的是最新的软件源和补丁。打开终端,执行以下命令: sudo yum update -y -y 选项表示在更新过程中自动回答 “yes”,避免手动确认。 2. 安装 …...
程序化广告行业(13/89):DSP的深入解析与运营要点
程序化广告行业(13/89):DSP的深入解析与运营要点 大家好!一直以来,我都对程序化广告行业保持着浓厚的学习兴趣,在探索的过程中积累了不少心得。今天就想把这些知识分享出来,和大家一起学习进步…...
使用 Doris 和 LakeSoul
作为一种全新的开放式的数据管理架构,湖仓一体(Data Lakehouse)融合了数据仓库的高性能、实时性以及数据湖的低成本、灵活性等优势,帮助用户更加便捷地满足各种数据处理分析的需求,在企业的大数据体系中已经得到越来越…...
datax源码分析
文章目录 前言一、加载配置文件二、根据加载的配置文件进行调度三、根据配置文件执行读取写入任务总结 前言 在上一篇文章当中我们已经了解了datax的启动原理,以及datax的最基础的配置,datax底层java启动类的入口及关键参数。 接下来我将进行启动类执行…...
【HDLbits--分支预测器简单实现】
HDLbits--分支预测器简单实现 1 timer2.branche predicitors3.Branch history shift4.Branch direction predictor 以下是分支预测器的简单其实现; 1 timer 实现一个计时器,当load1’b1时,加载data进去,当load1’b0时进行倒计时&…...
优化Go错误码管理:构建清晰、优雅的HTTP和gRPC错误码规范
在系统开发过程中,如何优雅地管理错误信息一直是个棘手问题。传统的错误处理方式往往存在不统一、难以维护等缺点。而 errcode 模块通过对错误码进行规范化管理,为系统级和业务级错误提供了统一的编码标准。本文将带您深入了解 errcode 的设计原理、错误…...
批量压缩与优化 Excel 文档,减少 Excel 文档大小
当我们在 Excel 文档中插入图片资源的时候,如果我们插入的是原图,可能会导致 Excel 变得非常的大。这非常不利于我们传输或者共享。那么当我们的 Excel 文件非常大的时候,我们就需要对文档做一些压缩或者优化的处理。那有没有什么方法可以实现…...
MongoDB分页实现方式对比:PageRequest vs Skip/Limit
MongoDB分页实现方式对比:PageRequest vs Skip/Limit 一、基本概念1.1 PageRequest分页1.2 Skip/Limit分页 二、主要区别2.1 使用方式2.2 参数计算2.3 适用场景PageRequest适用场景:Skip/Limit适用场景: 三、性能考虑3.1 PageRequest的性能特…...
SAP Commerce(Hybris)营销模块(一):商城产品折扣配置
基于Hybris的Backoffice后台管理系统,创建一个基于模板的营销规则,并配置上对应的优惠活动。 架构设计 先从一张架构图说起 Hybris的促销模块,是基于Promotion引擎来实现的,可以通过Backoffice来进行配置。 通过上面的架构图又可…...
如何在 React 中实现错误边界?
在 React 中实现错误边界 错误边界是 React 提供的一种机制,用于捕获子组件树中的 JavaScript 错误,并展示回退 UI。它可以帮助开发者更好地处理错误,提升用户体验。本文将详细介绍如何在 React 中实现错误边界,包括其工作原理、…...
从头开始开发基于虹软SDK的人脸识别考勤系统(python+RTSP开源)(五)完整源码已上传!
本篇是对照之前代码剩余的部分代码做补充,分享给大家,便于对照运行测试。 完整版的全功能单文件版本已上传!https://download.csdn.net/download/xiaomage_cn/90484179 人脸识别抽象层,这个大家应该都知道,就是为了方…...
PySide(PyQT)的mouseMoveEvent()和hoverMoveEvent()的区别
在 PySide中,mouseMoveEvent 和 hoverMoveEvent 都是用于处理鼠标移动相关操作的事件,但它们之间存在明显的区别: 事件触发条件 • mouseMoveEvent: 当鼠标在对应的图形项(如 QGraphicsPixmapItem)…...
【通缩螺旋的深度解析与科技破局路径】
通缩螺旋的深度解析与科技破局路径 一、通缩螺旋的形成机制与恶性循环 通缩螺旋(Deflationary Spiral)是经济学中描述价格持续下跌与经济衰退相互强化的动态过程,其核心逻辑可拆解为以下链条: 需求端萎缩:居民消费信…...
【如何使用云服务器与API搭建专属聊天系统:宝塔面板 + Openwebui 完整教程】
文章目录 不挑电脑、不用技术,云服务器 API 轻松搭建专属聊天系统,对接 200 模型,数据全在自己服务器,安全超高一、前置准备:3 分钟快速上手指南云服务器准备相关账号注册 二、手把手部署教程(含代码块&a…...
Oracle数据库存储结构--逻辑存储结构
数据库存储结构:分为物理存储结构和逻辑存储结构。 物理存储结构:操作系统层面如何组织和管理数据 逻辑存储结构:Oracle数据库内部数据组织和管理数据,数据库管理系统层面如何组织和管理数据 Oracle逻辑存储结构 数据库的逻…...
C++ 左值(lvalue)和右值(rvalue)
在 C 中,左值(lvalue)和右值(rvalue)是指对象的不同类别,区分它们对于理解 C 中的表达式求值和资源管理非常重要,尤其在现代 C 中涉及到移动语义(Move Semantics)和完美转…...
《实战AI智能体》DeepSearcher 的架构设计
DeepSearcher 的架构设计 一个通往搜索AGI的Agentic RAG应该如何设计? 从架构上看,DeepSearcher 主要分为两大模块。 一个是数据接入模块,通过Milvus向量数据库来接入各种第三方的私有知识。这也是DeepSearcher相比OpenAI的原本DeepResearc…...
Kotlin 继承
Kotlin 继承 概述 Kotlin 是一种现代的编程语言,它具有简洁、安全、互操作性等特点。在面向对象编程中,继承是一种非常重要的特性,它允许我们创建具有共同属性和方法的类。本文将详细介绍 Kotlin 中的继承机制,包括继承的基本概…...
【6】树状数组学习笔记
前言 树状数组是我学的第一个高级数据结构,属于 log \log log 级数据结构。 其实现在一般不会单独考察数据结构,主要是其在其他算法(如贪心,DP)中起到优化作用。 长文警告:本文一共 995 995 995 行…...
【RISCV LAB】0x01-安装实验仿真辅助工具
安装实验辅助工具 实验环境搭建安装 Verilator编译依赖下载源码编译安装测试安装 安装 RISC-V 交叉编译工具链编译依赖下载源码编译安装编译并安装添加环境变量并测试 安装 GTKWave其他模拟器推荐RARSemulsiV FAQ 实验环境搭建 Verilator 是一款开源的支持 Verilog 和 SystemV…...
OSPF-2 邻接建立关系
上一期我们说了OSPF的邻居建立关系以及OSPF邻居关系建立中建立失败的因素以及相关实验案例 这一期我们来说说OSPF的邻接关系建立时需要交互哪些报文以及失败因素及原因和相关实验案例 一、概述 在运行了OSPF的网络当中为了交互链路状态信息和路由信息,互相之间需要建立邻接关…...
操作系统知识点29
1.当用户使用外部设备时,其控制设备的命令传递途径依次为用户应用层->设备独立层->设备驱动层->设备硬件 2.通常用于管理空闲物理内存的方法:空闲快链表法;位示图法;空闲页面表 3. 可用于文件的存取控制和保护的方法&a…...
【Java篇】行云流水,似风分岔:编程结构中的自然法则
文章目录 Java 程序逻辑控制:顺序、分支与循环结构全面解析一、顺序结构二、分支结构2.1 if 语句2.1.1 基本语法2.1.2 if-else 语句2.1.3 if-else if-else 语句 2.2 switch 语句 三、循环结构3.1 while 循环3.2 break 语句3.3 continue 语句3.4 for 循环 四、输入输…...
代码块与设计模式
文章目录 1.代码块1.1基本介绍基本语法 1.2代码块的好处和案例演示1.3代码块使用注意事项和细节讨论!!! 2.单例设计模式2.1什么是设计模式2.2什么是单例模式2.2.1饿汉式2.2.2懒汉式2.2.3比较 1.代码块 1.1基本介绍 代码化块又称为初始化块,属于类中的成员[即是类的一部分]&am…...
要登录的设备ip未知时的处理方法
目录 1 应用场景... 1 2 解决方法:... 1 2.1 wireshark设置... 1 2.2 获取网口mac地址,wireshark抓包前预过滤掉自身mac地址的影响。... 2 2.3 pc网口和设备对接... 3 2.3.1 情况1:... 3 2.3.2 情…...
CentOS 系统安装 docker 以及常用插件
博主用的的是WindTerm软件链接的服务器,因为好用 1.链接上服务器登入后,在/root/目录下 2.执行以下命令安装docker sudo yum install -y yum-utilssudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.reposudo…...
统计字符(字符串)(gets与fgets的区别)
统计字符 #include<stdio.h> #include<string.h> int main(){char str1[5],str2[80];while(gets(str1)){if(strcmp(str1,"#")0)break;gets(str2);for(int i0;i<strlen(str1);i){int sum0;for(int j0;j<strlen(str2);j){if(str1[i]str2[j])sum;}p…...
Node.js REPL 深入解析
Node.js REPL 深入解析 引言 Node.js 作为一种流行的 JavaScript 运行环境,在服务器端开发中扮演着重要角色。REPL(Read-Eval-Print Loop,读取-求值-打印循环)是 Node.js 的一个核心特性,它允许开发者在一个交互式环境中执行 JavaScript 代码。本文将深入探讨 Node.js R…...
【测试语言基础篇】Python基础之List列表
一、Python 列表(List) 序列是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。 Python有6个序列的内置类型,但最常见的是列表和元组。序列都可…...
中山六院团队发表可解释多模态融合模型Brim,可以在缺少分子数据时借助病理图像模拟生成伪基因组特征|顶刊解读·25-02-14
小罗碎碎念 在癌症诊疗领域,精准预测患者预后对临床决策意义重大。传统的癌症分期系统,如TNM分期,因无法充分考量肿瘤异质性,难以准确预测患者的临床结局。而基于人工智能的多模态融合模型虽有潜力,但在实际临床应用中…...
《基於Python的网络爬虫抓包技术研究与应用》
## 摘要 本文探讨了基于Python的网络爬虫抓包技术及其应用。随着互联网数据的快速增长,网络爬虫技术在数据采集和分析中扮演着越来越重要的角色。本研究首先介绍了网络爬虫的基本概念和Python在爬虫开发中的优势,然后深入分析了抓包技术的原理和常用工具…...
从零开始探索C++游戏开发:性能、控制与无限可能
一、为何选择C开发游戏? 在虚幻引擎5渲染的次世代画面背后,在《巫师3》的庞大开放世界中,在《毁灭战士》的丝滑60帧战斗里,C始终扮演着核心技术角色。这门诞生于1983年的语言,至今仍占据着游戏引擎开发语言使用率榜首…...
TypeScript 高级类型 vs JavaScript:用“杂交水稻”理解类型编程
如果把 JavaScript 比作乐高积木,TypeScript 就是一套智能积木系统。本文将用最生活化的比喻,带你理解 TypeScript 那些看似复杂的高级类型。 一、先看痛点:JavaScript 的“薛定谔类型” // 场景:用户信息处理 function getUserI…...
几款可用于绘制工艺原理图的开源框架
一、LogicFlow 由滴滴团队开发的开源流程图框架,支持高度定制的工艺原理图绘制。 • 核心特性: • 提供拖拽式界面和丰富的节点类型(矩形、圆形、多边形等),支持自定义节点形状、样式和交互逻辑。 • 支持插件扩展&am…...
STM32如何精准控制步进电机?
在工业自动化、机器人控制等场合,步进电机以其高精度、开环控制的特性得到了广泛应用。而在嵌入式系统中,使用STM32进行步进电机的精确控制,已成为开发者的首选方案之一。 本文将从嵌入式开发者的角度,深入探讨如何基于STM32 MCU…...
Go语言入门基础详解
一、语言历史背景 Go语言由Google工程师Robert Griesemer、Rob Pike和Ken Thompson于2007年设计,2009年正式开源。设计目标: 兼具Python的开发效率与C的执行性能内置并发支持(goroutine/channel)简洁的类型系统现代化的包管理跨…...
WPF窗口读取、显示、修改、另存excel文件——CAD c#二次开发
效果如下: using System.Data; using System.IO; using System.Windows; using Microsoft.Win32; using ExcelDataReader; using System.Text; using ClosedXML.Excel;namespace IfoxDemo {public partial class SimpleWindow : Window{public SimpleWindow(){Initi…...
Ubuntu 服务器安装 Python 环境 的详细指南
以下是 在 Ubuntu 上安装 Python 3.10 的详细步骤(兼容 Ubuntu 20.04/22.04): 方法一:通过 PPA 仓库安装(推荐) 1. 添加 deadsnakes PPA sudo apt update sudo apt install software-properties-common s…...
鸿蒙next 多行文字加图片后缀实现方案
需求 实现类似iOS的YYLabel之类的在文字后面加上图片作为后缀的样式,多行时文字使用…省略超出部分,但必须保证图片的展现。 系统方案 在当前鸿蒙next系统提供的文字排版方法基本没有合适使用的接口,包括imagespan和RichEditor,根据AI的回…...
STM32---FreeRTS队列集
一、简介 一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集 ! 作用:用于对多个队列或信号量进行“监听”,其中不管哪一个消息到来,都可让…...
oracle11.2.0.4 RAC 保姆级静默安装(二) DB数据库软件
1.响应文件配置 [rootdb11g1 software]# su - oracle [oracledb11g1 ~]$ cd /software/database/ [oracledb11g1 database]$ cd response/ [oracledb11g1 response]$ vi db_install.rsp oracle.install.optionINSTALL_DB_SWONLY ORACLE_HOSTNAMEdb11g1 UNIX_GROUP_NAME…...
用python代码将excel中的数据批量写入Json中的某个字段,生成新的Json文件
需求 需求: 1.将execl文件中的A列赋值给json中的TrackId,B列赋值给json中的OId 要求 execl的每一行,对应json中的每一个OId json 如下: {"List": [{"BatchNumber": "181-{{var}}",// "Bat…...
【每日学点HarmonyOS Next知识】状态变量、动画UI残留、Tab控件显示、ob前缀问题、文字背景拉伸
1、HarmonyOS 怎么用一个变量观察其他很多个变量的变化? 有一个提交按钮的颜色,需要很多个值非空才变为红色,否则变为灰色,可不可以用一个变量统一观察这很多个值,去判断按钮该显示什么颜色,比如Button().…...
【第五节】windows sdk编程:windows 控件基础
目录 一、控件概述 二、标准控件 三、通用控件 四、控件的创建 五、控件风格 六、控件相关的消息 6.1 控件控制消息 6.2 控件通知消息 一、控件概述 控件是 Windows 系统内置的窗口类,它们只能是某个窗口的子窗口。因此,创建控件时必须使用 WS_C…...