长安链使用Golang编写智能合约教程(二)
长安链2.3.0+的go合约虚拟机和2.3.0以下的不兼容,编译的方式也有差异,所以在ide上做了区分。
教程三会写一些,其他比较常用SDK方法的解释和使用方法
教程一:(长安链2.1.+的版本的智能合约)
教程三:(常见GO SDK的解释与使用)
编写前的注意事项:
1、运行一条带有Doker_GoVM的链
2、建议直接用官方的在线IDE去写合约,因为写完可以直接测,缺点只是调试不方便。
3、如果自己拉环境在本地写合约,编译时注意编译环境,官方有提醒你去Linux下去编译。
4、如果你的链是2.3.+,使用编译器前,请先切换到2.3.+,以防不测
1、首先去新建一个合约工程
这里选择空白模板,其他模板好像是一些web3的规范模板

2、打开main.go文件(有一份示例合约)
/*
Copyright (C) BABEC. All rights reserved.
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.SPDX-License-Identifier: Apache-2.0
*/package mainimport ("encoding/json""fmt""log""strconv""chainmaker/pb/protogo""chainmaker/sandbox""chainmaker/sdk"
)type FactContract struct {
}// 存证对象
type Fact struct {FileHash string FileName string Time int
}// 新建存证对象
func NewFact(fileHash string, fileName string, time int) *Fact {fact := &Fact{FileHash: fileHash,FileName: fileName,Time: time,}return fact
}func (f *FactContract) InitContract() protogo.Response {return sdk.Success([]byte("Init contract success"))
}func (f *FactContract) UpgradeContract() protogo.Response {return sdk.Success([]byte("Upgrade contract success"))
}func (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.Save()case "findByFileHash":return f.FindByFileHash()default:return sdk.Error("invalid method")}
}func (f *FactContract) Save() protogo.Response {params := sdk.Instance.GetArgs()// 获取参数fileHash := string(params["file_hash"])fileName := string(params["file_name"])timeStr := string(params["time"])time, err := strconv.Atoi(timeStr)if err != nil {msg := "time is [" + timeStr + "] not int"sdk.Instance.Errorf(msg)return sdk.Error(msg)}// 构建结构体fact := NewFact(fileHash, fileName, time)// 序列化factBytes, err := json.Marshal(fact)if err != nil {return sdk.Error(fmt.Sprintf("marshal fact failed, err: %s", err))}// 发送事件sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})// 存储数据err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)if err != nil {return sdk.Error("fail to save fact bytes")}// 记录日志sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)sdk.Instance.Infof("[save] fileName=" + fact.FileName)// 返回结果return sdk.Success([]byte(fact.FileName + fact.FileHash))}func (f *FactContract) FindByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to call get_state")}// 反序列化var fact Factif err = json.Unmarshal(result, &fact); err != nil {return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))}// 记录日志sdk.Instance.Infof("[find_by_file_hash] fileHash=" + fact.FileHash)sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)// 返回结果return sdk.Success(result)
}func main() {err := sandbox.Start(new(FactContract))if err != nil {log.Fatal(err)}
}
3、示例模板解析
21行:这里的FactContract是合约名称,对应的要和125行main函数err := sandbox.Start(new(FactContract))一致
type FactContract struct {
}
25行:Fact结构体就是要存在区块链中的结构体,根据你自己的需要去变更结构体的字段
type Fact struct {
FileHash string
FileName string
Time int
}
32行:新建存证对象,根据前面25行Fact 结构体的变化而变化
func NewFact(fileHash string, fileName string, time int) *Fact {
fact := &Fact{
FileHash: fileHash,
FileName: fileName,
Time: time,
}
return fact
}
41-47行:InitContract、UpgradeContract两个方法,不用动,这是实现合约必须要的两个方法,用于合约初始化和合约升级
func (f *FactContract) InitContract() protogo.Response {
return sdk.Success([]byte("Init contract success"))
}func (f *FactContract) UpgradeContract() protogo.Response {
return sdk.Success([]byte("Upgrade contract success"))
}
49行:InvokeContract是调用合约的方法,根据你的合约种有多少方法,依葫芦画瓢在case ....return 继续补充就行
func (f *FactContract) InvokeContract(method string) protogo.Response {
switch method {
case "save":
return f.Save()
case "findByFileHash":
return f.FindByFileHash()
default:
return sdk.Error("invalid method")
}
}
60行: Save(存证方法)
以下大部分依葫芦画瓢就好了,重点关注以下内容:
66-72行:做了time字段的字符校验,确保是数字
84行:发送事件函数EmitEvent,第一个参数是合约事件主题,第二个参数是合约事件参数、(注意合约事件的数据,参数数量不可大于16) 写了事件、订阅之后可以监听到事件状态
87行:存储数据函数sdk.Instance.PutStateByte,三个参数 key、field 、value ,(原本我以为弄个key-value的存储参数就行了,为什么官方要弄个field,我也不理解,但是官方有解释,不过用长安链就遵从他的规则吧) 这里就是说key是一个命名空间,相当于一个域,真正的key是一个拼接串,value是存证的内容。

93、94行:记录日志,可记可不记,写了的话,节点的日志记录会存下来
97行:返回要遵从官方规范
func (f *FactContract) Save() protogo.Response {
params := sdk.Instance.GetArgs()// 获取参数
fileHash := string(params["file_hash"])
fileName := string(params["file_name"])
timeStr := string(params["time"])
time, err := strconv.Atoi(timeStr)
if err != nil {
msg := "time is [" + timeStr + "] not int"
sdk.Instance.Errorf(msg)
return sdk.Error(msg)
}// 构建结构体
fact := NewFact(fileHash, fileName, time)// 序列化
factBytes, err := json.Marshal(fact)
if err != nil {
return sdk.Error(fmt.Sprintf("marshal fact failed, err: %s", err))
}
// 发送事件
sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})// 存储数据
err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)
if err != nil {
return sdk.Error("fail to save fact bytes")
}// 记录日志
sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)
sdk.Instance.Infof("[save] fileName=" + fact.FileName)// 返回结果
return sdk.Success([]byte(fact.FileName + fact.FileHash))}
取证方法:
以下大部分依葫芦画瓢就好了,重点关注以下内容:
- 134行:取证的方法:sdk.Instance.GetStateByte ,这里的“fact_bytes”就是这个合约的域,所以这里填写的要和你在存证中填写的域一致才行。
- 其他按照规范以葫芦画瓢
func (f *FactContract) FindByFileHash() protogo.Response {
// 获取参数
fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果
result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)
if err != nil {
return sdk.Error("failed to call get_state")
}// 反序列化
var fact Fact
if err = json.Unmarshal(result, &fact); err != nil {
return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))
}// 记录日志
sdk.Instance.Infof("[find_by_file_hash] fileHash=" + fact.FileHash)
sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)// 返回结果
return sdk.Success(result)
}
125行:main函数,这个new合约对象的时候保证名称和最开始21行的结构体名称一样就行了,其他的不用变。
func main() {
err := sandbox.Start(new(FactContract))
if err != nil {
log.Fatal(err)
}
}
4、新增一个查询历史数据的方法
以上就是main.go模板内容的解析,但是一般情况下,我们还有查询历史数据的需求,模板没有提供,所以这里根据模板继续补充一个查询历史记录的方法:
func (f *FactContract) GetHistoryByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果iter, err := sdk.Instance.NewHistoryKvIterForKey("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}defer iter.Close()var keyModifications []*sdk.KeyModification// 遍历结果for {if !iter.HasNext() {break}keyModification, err := iter.Next()if err != nil {sdk.Instance.Infof("Error iterating: %v", err)}if keyModification == nil {break}keyModifications = append(keyModifications, keyModification)}jsonBytes, err := json.Marshal(keyModifications)if err != nil {return sdk.Error(fmt.Sprintf("Error marshaling keyModifications: %v", err))}// 返回结果return sdk.Success(jsonBytes)
}
方法解析:
这里我只解释重点步骤
1、调用查询历史数据接口
iter, err := sdk.Instance.NewHistoryKvIterForKey("fact_bytes", fileHash)
返回值类型:![]()
![]()
后面就是根据返回值的结构进行遍历,
var keyModifications []*sdk.KeyModification
把结果放在 keyModifications 然后进行序列化,返回
5、新增一个删除方法
注意,这里虽然是一个删除方法,但是不是真的删除,只有有一个isDelete的字段,如果调用了这个方法,某个域对应的hash会被标记为删除。代码不在解释
func (f *FactContract) DeleteByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果err := sdk.Instance.DelState("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}// 返回结果return sdk.Success(nil)
}
6、完整合约
/*
Copyright (C) BABEC. All rights reserved.
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.SPDX-License-Identifier: Apache-2.0
*/package mainimport ("chainmaker/pb/protogo""chainmaker/sandbox""chainmaker/sdk""encoding/json""fmt""log""strconv"
)type FactContract struct {
}// 存证对象
type Fact struct {FileHash stringFileName stringTime int
}// 新建存证对象
func NewFact(fileHash string, fileName string, time int) *Fact {fact := &Fact{FileHash: fileHash,FileName: fileName,Time: time,}return fact
}func (f *FactContract) InitContract() protogo.Response {return sdk.Success([]byte("Init contract success"))
}func (f *FactContract) UpgradeContract() protogo.Response {return sdk.Success([]byte("Upgrade contract success"))
}func (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.Save()case "findByFileHash":return f.FindByFileHash()case "deltedByFileHash":return f.DeleteByFileHash()case "getHistoryByFileHash":return f.GetHistoryByFileHash()default:return sdk.Error("invalid method")}
}func (f *FactContract) Save() protogo.Response {params := sdk.Instance.GetArgs()// 获取参数fileHash := string(params["file_hash"])fileName := string(params["file_name"])timeStr := string(params["time"])time, err := strconv.Atoi(timeStr)if err != nil {msg := "time is [" + timeStr + "] not int"sdk.Instance.Errorf(msg)return sdk.Error(msg)}// 构建结构体fact := NewFact(fileHash, fileName, time)// 序列化factBytes, err := json.Marshal(fact)if err != nil {return sdk.Error(fmt.Sprintf("传过来的参数序列化失败, err: %s", err))}// 发送事件sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})// 存储数据err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)if err != nil {return sdk.Error("fail to save fact bytes")}// 记录日志// sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)// sdk.Instance.Infof("[save] fileName=" + fact.FileName)createUser, _ := sdk.Instance.GetSenderRole()sdk.Instance.Infof("[saveUser] create=" + createUser)// 返回结果return sdk.Success([]byte(fact.FileName + fact.FileHash))}func (f *FactContract) FindByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to call get_state")}// 反序列化var fact Factif err = json.Unmarshal(result, &fact); err != nil {return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))}// 记录日志sdk.Instance.Infof("[find_by_file_hash] fileHash=" + fact.FileHash)sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)// 返回结果return sdk.Success(result)
}func (f *FactContract) DeleteByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果err := sdk.Instance.DelState("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}// 返回结果return sdk.Success(nil)
}func (f *FactContract) GetHistoryByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果iter, err := sdk.Instance.NewHistoryKvIterForKey("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}defer iter.Close()var keyModifications []*sdk.KeyModification// 遍历结果for {if !iter.HasNext() {break}keyModification, err := iter.Next()if err != nil {sdk.Instance.Infof("Error iterating: %v", err)}if keyModification == nil {break}keyModifications = append(keyModifications, keyModification)sdk.Instance.Infof("Key: %s, Field: %s, Value: %s, TxId: %s, BlockHeight: %d, IsDelete: %t, Timestamp: %s, \n",keyModification.Key, keyModification.Field, keyModification.Value, keyModification.TxId, keyModification.BlockHeight, keyModification.IsDelete, keyModification.Timestamp)}jsonBytes, err := json.Marshal(keyModifications)if err != nil {return sdk.Error(fmt.Sprintf("Error marshaling keyModifications: %v", err))}// 返回结果return sdk.Success(jsonBytes)
}func main() {err := sandbox.Start(new(FactContract))if err != nil {log.Fatal(err)}
}
7、结果展示
1、部署demo2合约

2、发起上链,上链了3条数据,其中file_hash我都是输入的1

3、查询某一个结果
4、删除一个结果
这也是一个上链操作

5、查询历史结果

这里没有格式化,现在拿去专门json格式化一下
[{"Key": "fact_bytes","Field": "1","Value": "eyJGaWxlSGFzaCI6IjEiLCJGaWxlTmFtZSI6IjIiLCJUaW1lIjozfQ==","TxId": "254071cabbca415186ae64956644d2230be111ebb94144d79f168a2252995a88","BlockHeight": 14,"IsDelete": false,"Timestamp": "1716864398"},{"Key": "fact_bytes","Field": "1","Value": "eyJGaWxlSGFzaCI6IjEiLCJGaWxlTmFtZSI6IjIiLCJUaW1lIjozfQ==","TxId": "ce705bbbaedb4315858b4e68b6331f4a947c3eb5a262433dbe2823ad3c87ee06","BlockHeight": 15,"IsDelete": false,"Timestamp": "1716864410"},{"Key": "fact_bytes","Field": "1","Value": "eyJGaWxlSGFzaCI6IjEiLCJGaWxlTmFtZSI6IjIiLCJUaW1lIjozfQ==","TxId": "6e099f7ce81543bf8fd0bf565f741478de53b4bc72834112a8bc0bc5f06f9a47","BlockHeight": 16,"IsDelete": false,"Timestamp": "1716864421"},{"Key": "fact_bytes","Field": "1","Value": "","TxId": "5f76c217063e41ad8c0f2b4ab3fae2418d784c9f0ade416b94715e95214acfc5","BlockHeight": 17,"IsDelete": true,"Timestamp": "1716864504"}
]
value就是存证的字符串,但是这里是base64编码,转码结果如下
(注意:这里你会发现所有的value都是一样的,因为是我存证的数据都是输入的一样的)
转码结果如下:

8、个人理解
- 官方文档有错:官方文档把key叫做命名空间取了一个固定值,field作为存证hash,这里应该是他们写反了,field 才有域、空间的意思。上面的解析,我还是按照官方错误的来说的。因为他最后存在level_db中的key是拼接的,所以写反写没事,都一样。你可以改成对的。
- 之所以引入一个命名空间的概念,我猜测应该是为了在数据库中方便查看,因为同一份id可能会被存多次,加了一个空间会方便区分。不过官方也提供了没有命名空间的存储方法
PutStateFromKey(key string, value string) error 就是教程一用的方法 - 删除函数,并不是真的删除,会新上链一条数据,标明某个域、某个key被删除了,但是已经上链的数据不会变动。
相关文章:
长安链使用Golang编写智能合约教程(二)
长安链2.3.0的go合约虚拟机和2.3.0以下的不兼容,编译的方式也有差异,所以在ide上做了区分。 教程三会写一些,其他比较常用SDK方法的解释和使用方法 教程一:(长安链2.1.的版本的智能合约) 教程三ÿ…...
jpom linux发布前端 ruoyi
前置条件 辅助安装 安装jdk curl -fsSL https://jpom.top/docs/install.sh | bash -s Server jdkonly-moduledefault 一键安装maven 后端必备 curl -fsSL https://jpom.top/docs/install.sh | bash -s Server mvnonly-moduledefault 一键安装node 前端必备 curl -fsSL http…...
K8S认证|CKA题库+答案| 15. 备份还原Etcd
目录 15、 备份还原Etcd CKA v1.29.0模拟系统 下载试用 题目: 开始操作: 1)、切换集群 2)、登录master并提权 3)、备份Etcd现有数据 4)、验证备份数据快照 5)、查看节点和Pod状态 6࿰…...
软件架构设计属性之一:功能性属性浅析
文章目录 引言一、定义二、关键要素1. 需求满足2. 操作性3. 数据处理4. 业务流程支持5. 合规性 三、 设计原则1. 用户中心设计2. 模块化3. 抽象化4. 可扩展性5. 可维护性6. 设计原则的实施策略7. 设计原则的评估 四、实现策略1. 需求分析2. 功能分解3. 接口设计4. 技术选型5. 迭…...
学习Java的日子 Day48 函数,DOM
Day48 1.流程控制语句 if else for for-in(遍历数组时,跟Java是否一样) While do while break 语句用于跳出循环 continue 用于跳过循环中的一个迭代 2.函数 2.1 JavaScript 函数语法 函数就是包裹在花括号中的代码块,前面使用了关键词 function funct…...
Scikit-Learn朴素贝叶斯
Scikit-Learn朴素贝叶斯 1、朴素贝叶斯1.1、贝叶斯分类1.2、贝叶斯定理1.3、贝叶斯定理的推导1.4、朴素贝叶斯及原理1.5、朴素贝叶斯的优缺点2、Scikit-Learn朴素贝叶斯2.1、Sklearn中的贝叶斯分类器2.2、Scikit-Learn朴素贝叶斯API2.3、Scikit-Learn朴素贝叶斯实践(新闻分类与…...
网页设计步骤总结
第一步:css重置 https://blog.csdn.net/BradenHan/article/details/132122504 第二步:媒体查询不同尺寸加载不同的css文件https://blog.csdn.net/Yi_Lesama/article/details/131184469 <!-- link元素中的CSS媒体查询 --> <link rel"styl…...
第十三期Big Demo Day亮点项目:CCarbon重塑碳交易生态,助力全球绿色发展
第十三期Big Demo Day活动即将于2024年5月28日在香港数码港的CyberArena隆重举行。我们荣幸地宣布,利用区块链技术优化全球碳交易CCarbon项目将亮相,参与精彩的项目路演。本次活动由ZeeprLabs、BiKing Exchange、Gather冠名赞助,Central Rese…...
python基础知识总结(第一节)
一、python简介: Python是一种解释型,面向对象的高级语言。 Pyhton的语法和动态类型,以及解释性语言的本质,使它一跃成为多数平台上写脚本和快速开发应用的编程语言。 python语言百度百科介绍 二、Python基础语法:…...
DOS学习-目录与文件应用操作经典案例-comp
新书上架~👇全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.使用 三.案例 案例 1: 基本比较 案例 2: 十进制显示差异 案例 3: 字符形式显…...
原神抽卡点名程序教程(直接下载用)
今天我要给大家分享一个在抖音上特别火的视频——原神抽卡点名程序教程。 废话不多说,直接上链接 :--点击下载原神抽卡程序 步骤1: 步骤2&am…...
gpt-4o继续迭代考场安排程序 一键生成考场清单
接上两篇gpt-4o考场安排-CSDN博客,考场分层次安排,最终exe版-CSDN博客 当然你也可以只看这一篇。 今天又添加了以下功能,程序见后。 1、自动分页,每个考场打印一页 2、添加了打印试场单页眉 3、添加了页脚 第X页,…...
php 设置时区
date_default_timezone_set() 设置所有日期/时间函数使用的默认时区 除了在脚本中使用此函数设置默认时区,还可以使用 INI 设置 date.timezone 设置默认时区。 参数 timezoneId 时区标识符,像 UTC、Africa/Lagos、Asia/Hong_Kong 或 Europe/Lisbon。…...
ArcGIS不同图斑设置不同的透明度
对于设置一个图层的整体的透明度,我们在 ArcGIS制作带蒙版的遥感影像地图http://mp.weixin.qq.com/s?__bizMzIzNjM2NTYxMg&mid2247509080&idx1&sn38dccf0a52bb3bb3758f57114ee38b72&chksme8da161bdfad9f0d363da90959a8524dcf2b60d0e8d999f8ebeef0…...
前端 CSS 经典:图片边框
前言:有这么一个业务,需要边框随着图片宽度的变化而变化,比如一些聊天的气泡框等。 实现原理:使用 border-image 属性 效果图: 实现代码: <!DOCTYPE html> <html lang"en"><he…...
异常有什么,异常类有什么
在Java中,异常(Exception)是一种在程序运行过程中出现的不正常情况。异常机制提供了一种从错误中恢复的途径。异常分为两大类:检查异常(Checked Exception)和运行时异常(Runtime Exception&…...
如何解压忘记了密码的加密zip压缩包?这两个方法收藏好!
加密是一种保护信息不被未经授权访问的重要手段。ZIP压缩包作为一种常见的文件压缩格式,zip文件加密是很多人都回去做的一件事情,那么zip加密文件如何解密?有几种方法可以解密呢?今天介绍几种方法给大家。 一、尝试常用密码 我们…...
java中,怎样用最简单方法实现写word文档
在跨平台环境中实现写word时,如果用现成的库,就会涉及跨平台兼容性问题,比如在安卓与java中实现写word的功能。还有一个问题就是,完全用程序生成word文档,工作量较大。所以采用了模板替换的方法。 docx文档本质就是一…...
大语言模型的工程技巧(三)——分布式计算
相关说明 这篇文章的大部分内容参考自我的新书《解构大语言模型:从线性回归到通用人工智能》,欢迎有兴趣的读者多多支持。 本文将讨论如何利用多台机器进行神经网络的分布式训练。利用多台机器来加速大语言模型的训练,是其获得成功的重要原…...
AI开发初体验:昇腾加持,OrangePi AIpro 开发板
文章目录 一、前言二、板子介绍2.1 拆箱2.2 板子规格2.2.1 常规项目2.2.2 扩展项目2.2.3 操作系统 2.3 点板画面 三、AI程序初体验3.1 新奇的地方3.2 运行第一个AI程序3.2.1 硬件连接3.2.2 串口连接3.2.3 开启外部IP端口3.2.4 查询板子IP地址3.2.5 了解 juypter lab 启动脚本&a…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
