1.链码开发
先设计一个简单的应用场景,假设有这样的业务需求:
- 可以添加学校,信息包括学校名称、学校ID;
- 添加该学校的学生,信息包括姓名,用户ID,所在学校ID,所在班级名称;
- 更新学生信息;
- 根据学生ID查询学生信息;
- 根据学生ID删除学生信息;
- 根据学校ID删除学校信息,包括该学校下的所有学生。
接下来开发满足这些需求的链码。关键代码如下
1.1 定义School,Student结构体
type StudentChaincode struct {
}
type Student struct {
UserId int `json:"user_id"` //学生id
Name string `json:"name"` //姓名
SchoolId string `json:"school_id"` //学校id
Class string `jsong:"class"` //班级名称
}
type School struct {
SchoolId string `json:"id"` //学校id
School string `json:"name"` //学校名称
}
1.2 部分业务需求的实现
1.2.1 添加学校
这里用到了shim.ChaincodeStubInterface的CreateCompositeKey,来创建联合主键。其实Fabric就是用U+0000来把各个联合主键的字段拼接起来,因为这个字符太特殊,所以很适合,对应的拆分联合主键的字段的方法是SplitCompositeKey。
func (t *StudentChaincode) initSchool(stub shim.ChaincodeStubInterface, args []string) pd.Response {
if len(args) != 2 {
return shim.Error("Incorrect number of arguments. Expecting 2(school_id, school_name)")
}
schoolId := args[0]
schoolName := args[1]
school := &School{schoolId, schoolName}
//这里利用联合主键,使得查询school时,可以通过主键的“school”前缀找到所有school
schoolKey, err := stub.CreateCompositeKey("School", []string{
"school", schoolId})
if err != nil {
return shim.Error(err.Error())
}
//结构体转json字符串
schoolJSONasBytes, err := json.Marshal(school)
if err != nil {
return shim.Error(err.Error())
}
//保存
err = stub.PutState(schoolKey, schoolJSONasBytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(schoolJSONasBytes)
}
1.2.2 添加学生,需要检查所属学校是否已经存在。
抽出来的工具类方法querySchoolIds 中用到的GetStateByPartialCompositeKey 是对联合主键进行前缀匹配的查询
func (t *StudentChaincode) addStudent(stub shim.ChaincodeStubInterface, args []string) pd.Response {
st, err := studentByArgs(args)
if err != nil {
return shim.Error(err.Error())
}
useridAsString := strconv.Itoa(st.UserId)
//检查学校是否存在,不存在则添加失败
schools := querySchoolIds(stub)
if len(schools) > 0 {
for _, schoolId := range schools {
if schoolId == st.SchoolId {
goto SchoolExists;
}
}
fmt.Println("school " + st.SchoolId+ " does not exist")
return shim.Error("school " + st.SchoolId+ " does not exist&