github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/examples/student/student.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/inklabsfoundation/inkchain/core/chaincode/shim"
     6  	pb "github.com/inklabsfoundation/inkchain/protos/peer"
     7  	"strings"
     8  	"strconv"
     9  	"encoding/json"
    10  )
    11  
    12  const (
    13  	SCHOOL_TYPE_FULL_TIME = 1
    14  	SCHOOL_TYPE_PART_TIME = 2
    15  	SEX_MALE              = 1
    16  	SEX_FAMEL             = 2
    17  	SCHOOL_PREFIX         = "school"
    18  	STUDENT_PREFIX        = "student"
    19  )
    20  
    21  const (
    22  	SPECIALTY_DEGREE = iota
    23  	BACHELOR_DEGREE
    24  	MASTER_DEGREE
    25  	DOCTOR_DEGREE
    26  )
    27  
    28  const (
    29  	STUDY_LOG_KEY      = "studyLog"
    30  	GRADUATION_LOG_KEY = "graduationLog"
    31  )
    32  
    33  type School struct {
    34  	Number       string `json:"number"`      //school number
    35  	Name         string `json:"name"`        //school name
    36  	Address      string `json:"address"`     //school address
    37  	CreateAt     string `json:"createAt"`    //school create date
    38  	Manager      string `json:"manager"`     //school manager
    39  	SchoolLevel  int    `json:"schoolLevel"` //school level
    40  	RegisterTime string `json:"registerTime"`
    41  }
    42  
    43  type Student struct {
    44  	Number               string `json:"number"`               //personal card number
    45  	StudentNumber        string `json:"studentNumber"`        //student number
    46  	Name                 string `json:"name"`                 //student name
    47  	Age                  int    `json:"age"`                  //student age
    48  	Sex                  int    `json:"sex"`                  //student sex
    49  	Credit               int    `json:"credit"`               //academic credit number
    50  	Grade                string `json:"grade"`                //student grade
    51  	Class                string `json:"class"`                //student class
    52  	CurrentSchool        string `json:"currentSchool"`        //student current school
    53  	CurrentSchoolName    string `json:"currentSchoolName"`    //student current school name
    54  	CurrentLevel         int    `json:"currentLevel"`         //student current level
    55  	AdmissionTime        string `json:"admissionTime"`        //student admission time
    56  	GraduationSchool     string `json:"graduationSchool"`     //graduation school number
    57  	GraduationSchoolName string `json:"graduationSchoolName"` //graduation school name
    58  	GraduationTime       string `json:"graduationTime"`       //student graduation time
    59  	GraduationLevel      int    `json:"graduationLevel"`      //student graduation level
    60  	RegisterTime         string `json:"registerTime"`
    61  }
    62  
    63  type StudyLog struct {
    64  	SchoolName    string `json:"schoolName"`    //study school name
    65  	SchoolNumber  string `json:"schoolNumber"`  //study school number
    66  	StudentName   string `json:"studentName"`   //student name
    67  	StudentNumber string `json:"studentNumber"` //student number
    68  	Level         int    `json:"level"`         //student study level
    69  	Class         string `json:"class"`         //student study class
    70  	Grade         string `json:"grade"`         //student study grade
    71  	DateTime      string `json:"dateTime"`
    72  }
    73  
    74  type GraduationInfo struct {
    75  	SchoolName      string `json:"schoolName"`      //school name
    76  	SchoolNumber    string `json:"schoolNumber"`    //school number
    77  	StudentName     string `json:"studentName"`     //student name
    78  	StudentNumber   string `json:"studentNumber"`   //student number
    79  	GraduationLevel int    `json:"graduationLevel"` //student graduation level
    80  	Description     string `json:"description"`     //description
    81  	Class           string `json:"class"`           //student class
    82  	Grade           string `json:"grade"`           //student grade
    83  	Credit          string `json:"credit"`          //credit number
    84  	DateTime        string `json:"dateTime"`
    85  }
    86  
    87  const (
    88  	RegisterSchool            = "registerSchool"
    89  	RegisterStudent           = "registerStudent"
    90  	Enrolment                 = "enrolment"
    91  	Graduate                  = "graduate"
    92  	QuerySchoolInfo           = "querySchoolInfo"
    93  	QueryStudentInfo          = "queryStudentInfo"
    94  	QueryStudentStudyLog      = "queryStudentStudyLog"
    95  	QueryStudentGraduationLog = "queryStudentGraduationLog"
    96  )
    97  
    98  type StudentChaincode struct {
    99  }
   100  
   101  func (s *StudentChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
   102  	return shim.Success(nil)
   103  }
   104  
   105  func (s *StudentChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
   106  	function, args := stub.GetFunctionAndParameters()
   107  	switch function {
   108  	case RegisterSchool:
   109  		if len(args) < 4 {
   110  			return shim.Error("RegisterSchool, Incorrect number of arguments. Expecting 4")
   111  		}
   112  		return s.registerSchool(stub, args)
   113  	case RegisterStudent:
   114  		if len(args) < 4 {
   115  			return shim.Error("RegisterStudent, Incorrect number of arguments. Expecting 3")
   116  		}
   117  		return s.registerStudent(stub, args)
   118  	case Enrolment:
   119  		if len(args) < 6 {
   120  			return shim.Error("Enrolment, Incorrect number of arguments. Expecting 6")
   121  		}
   122  		return s.enrolment(stub, args)
   123  	case Graduate:
   124  		if len(args) < 5 {
   125  			return shim.Error("Graduate, Incorrect number of arguments. Expecting 5")
   126  		}
   127  		return s.graduate(stub, args)
   128  	case QuerySchoolInfo:
   129  		if len(args) < 1 {
   130  			return shim.Error("QuerySchoolInfo, Incorrect number of arguments. Expecting 1")
   131  		}
   132  		return s.querySchoolInfo(stub, args)
   133  	case QueryStudentInfo:
   134  		if len(args) < 1 {
   135  			return shim.Error("QueryStudentInfo, Incorrect number of arguments. Expecting 1")
   136  		}
   137  		return s.queryStudentInfo(stub, args)
   138  	case QueryStudentStudyLog:
   139  		if len(args) < 1 {
   140  			return shim.Error("QueryStudentStudyLog, Incorrect number of arguments. At least 1")
   141  		}
   142  		return s.queryStudentStudyLog(stub, args)
   143  	case QueryStudentGraduationLog:
   144  		if len(args) < 1 {
   145  			return shim.Error("QueryStudentGraduationLog, Incorrect number of arguments. At least 1")
   146  		}
   147  		return s.queryStudentGraduationLog(stub, args)
   148  	}
   149  	return shim.Error("Function not found")
   150  }
   151  
   152  //school register
   153  func (s *StudentChaincode) registerSchool(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   154  	//get transaction time
   155  	timeStamp, err := stub.GetTxTimestamp()
   156  	if err != nil {
   157  		return shim.Error("Failed to get transaction timestamp : " + err.Error())
   158  	}
   159  	registerTime := fmt.Sprintf("%d", timeStamp.GetSeconds())
   160  	//get sender
   161  	sender, err := stub.GetSender()
   162  	if err != nil {
   163  		return shim.Error("Failed to get sender info : " + err.Error())
   164  	}
   165  	//validate args
   166  	number := strings.TrimSpace(strings.ToLower(args[0]))
   167  	if len(number) <= 0 {
   168  		return shim.Error("1st arg must be non-empty string")
   169  	}
   170  	name := strings.TrimSpace(strings.ToLower(args[1]))
   171  	if len(name) <= 0 {
   172  		return shim.Error("2st arg must be non-empty string")
   173  	}
   174  	address := strings.TrimSpace(strings.ToLower(args[2]))
   175  	if len(address) <= 0 {
   176  		return shim.Error("3st arg must be non-empty string")
   177  	}
   178  	level, err := strconv.Atoi(args[3])
   179  	if err != nil {
   180  		return shim.Error("3st arg failed to parse int : " + err.Error())
   181  	}
   182  	if level != SCHOOL_TYPE_FULL_TIME && level != SCHOOL_TYPE_PART_TIME {
   183  		return shim.Error("3st arg must be 1 or 2")
   184  	}
   185  	createTime := strings.TrimSpace(strings.ToLower(args[4]))
   186  	if len(createTime) <= 0 {
   187  		return shim.Error("4st arg must be non-empty string")
   188  	}
   189  	//build school record
   190  	school := &School{
   191  		Number:       number,
   192  		Name:         name,
   193  		Address:      address,
   194  		SchoolLevel:  level,
   195  		Manager:      sender,
   196  		CreateAt:     createTime,
   197  		RegisterTime: registerTime,
   198  	}
   199  	schoolKey := SCHOOL_PREFIX + number
   200  	//validate school exists
   201  	old, err := stub.GetState(schoolKey)
   202  	if err != nil {
   203  		return shim.Error("Failed to check school info : " + err.Error())
   204  	} else if old != nil {
   205  		return shim.Error("School exists")
   206  	}
   207  	//marshal to json
   208  	schoolJson, err := json.Marshal(school)
   209  	if err != nil {
   210  		return shim.Error("Failed to marshal school : " + err.Error())
   211  	}
   212  	err = stub.PutState(schoolKey, schoolJson)
   213  	if err != nil {
   214  		return shim.Error("Failed to save school data : " + err.Error())
   215  	}
   216  	return shim.Success(nil)
   217  }
   218  
   219  //student register
   220  func (s *StudentChaincode) registerStudent(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   221  	//get transaction time
   222  	timeStamp, err := stub.GetTxTimestamp()
   223  	if err != nil {
   224  		return shim.Error("Failed to get transaction timestamp : " + err.Error())
   225  	}
   226  	registerTime := fmt.Sprintf("%d", timeStamp.GetSeconds())
   227  	//get sender
   228  	sender, err := stub.GetSender()
   229  	if err != nil {
   230  		return shim.Error("Failed to get sender info : " + err.Error())
   231  	}
   232  	//validate args
   233  	number := strings.TrimSpace(strings.ToLower(args[0]))
   234  	if len(number) <= 0 {
   235  		return shim.Error("1st arg must be non-empty")
   236  	}
   237  	name := strings.TrimSpace(strings.ToLower(args[1]))
   238  	if len(name) <= 0 {
   239  		return shim.Error("2st arg must be non-empty")
   240  	}
   241  	age, err := strconv.Atoi(args[2])
   242  	if err != nil {
   243  		return shim.Error("3st arg parse to int failed : " + err.Error())
   244  	}
   245  	if age <= 0 {
   246  		return shim.Error("3st arg must be more than 0")
   247  	}
   248  	sex, err := strconv.Atoi(args[3])
   249  	if err != nil {
   250  		return shim.Error("4st arg parse to int failed : " + err.Error())
   251  	}
   252  	if sex != SEX_FAMEL && sex != SEX_MALE {
   253  		return shim.Error("4st arg must be 0 or 1")
   254  	}
   255  	//build student struct
   256  	student := &Student{
   257  		Number:       number,
   258  		Name:         name,
   259  		Age:          age,
   260  		Sex:          sex,
   261  		RegisterTime: registerTime,
   262  	}
   263  	//check sender exists
   264  	studentKey := STUDENT_PREFIX + sender
   265  	old, err := stub.GetState(studentKey)
   266  	if err != nil {
   267  		return shim.Error("Failed to validate sender register status : " + err.Error())
   268  	} else if old != nil {
   269  		return shim.Error("Sender has registered")
   270  	}
   271  	//marshal json
   272  	studentJson, err := json.Marshal(student)
   273  	if err != nil {
   274  		return shim.Error("Marshal student info error : " + err.Error())
   275  	}
   276  	err = stub.PutState(studentKey, studentJson)
   277  	if err != nil {
   278  		return shim.Error("Failed to save student info : " + err.Error())
   279  	}
   280  	return shim.Success(nil)
   281  }
   282  
   283  //student enrolment
   284  func (s *StudentChaincode) enrolment(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   285  	//get transaction time
   286  	timeStamp, err := stub.GetTxTimestamp()
   287  	if err != nil {
   288  		return shim.Error("Failed to get transaction timestamp : " + err.Error())
   289  	}
   290  	admissionTime := fmt.Sprintf("%d", timeStamp.GetSeconds())
   291  	//get sender
   292  	sender, err := stub.GetSender()
   293  	if err != nil {
   294  		return shim.Error("Failed to get sender info : " + err.Error())
   295  	}
   296  	//validate args
   297  	schoolNumber := strings.TrimSpace(strings.ToLower(args[0]))
   298  	if len(schoolNumber) <= 0 {
   299  		return shim.Error("1st arg must be non-empty string")
   300  	}
   301  	studentAddr := strings.TrimSpace(strings.ToLower(args[1]))
   302  	if len(studentAddr) <= 0 {
   303  		return shim.Error("2st arg must be non-empty string")
   304  	}
   305  	grade := strings.TrimSpace(strings.ToLower(args[2]))
   306  	if len(grade) <= 0 {
   307  		return shim.Error("3st arg must be non-empty string")
   308  	}
   309  	class := strings.TrimSpace(strings.ToLower(args[3]))
   310  	if len(class) <= 0 {
   311  		return shim.Error("4st arg must be non-empty string")
   312  	}
   313  	studentNumber := strings.TrimSpace(strings.ToLower(args[4]))
   314  	if len(studentNumber) <= 0 {
   315  		return shim.Error("5st arg must be non-empty string")
   316  	}
   317  	level, err := strconv.Atoi(args[5])
   318  	if err != nil {
   319  		return shim.Error("6st parse int failed : " + err.Error())
   320  	}
   321  	if level < SPECIALTY_DEGREE && level > DOCTOR_DEGREE {
   322  		return shim.Error("6st must be 0 , 1 , 2 , 3")
   323  	}
   324  	//check school info
   325  	schoolJson, err := stub.GetState(SCHOOL_PREFIX + schoolNumber)
   326  	if err != nil {
   327  		return shim.Error("Failed to get school info : " + err.Error())
   328  	} else if schoolJson == nil {
   329  		return shim.Error("School not exists")
   330  	}
   331  	//unmarshal school info
   332  	school := &School{}
   333  	err = json.Unmarshal(schoolJson, school)
   334  	if err != nil {
   335  		return shim.Error("Failed unmarshal school info : " + err.Error())
   336  	}
   337  	//check sender
   338  	if school.Manager != sender {
   339  		return shim.Error("Authority failed")
   340  	}
   341  	//check student info
   342  	studentKey := STUDENT_PREFIX + studentAddr
   343  	studentJson, err := stub.GetState(studentKey)
   344  	if err != nil {
   345  		return shim.Error("Failed to get student info : " + err.Error())
   346  	} else if studentJson == nil {
   347  		return shim.Error("Student not exists")
   348  	}
   349  	//unmarshal student info
   350  	student := &Student{}
   351  	err = json.Unmarshal(studentJson, student)
   352  	if err != nil {
   353  		return shim.Error("Failed unmarshal student info : " + err.Error())
   354  	}
   355  	if student.CurrentSchool != "" {
   356  		return shim.Error("Student has study at another school")
   357  	}
   358  	//update school info
   359  	student.StudentNumber = studentNumber
   360  	student.CurrentSchool = schoolNumber
   361  	student.CurrentSchoolName = school.Name
   362  	student.AdmissionTime = admissionTime
   363  	student.Class = class
   364  	student.Grade = grade
   365  	student.CurrentLevel = level
   366  	studentJson, err = json.Marshal(student)
   367  	if err != nil {
   368  		return shim.Error("Marshal student info to json failed : " + err.Error())
   369  	}
   370  	//update student info
   371  	err = stub.PutState(studentKey, studentJson)
   372  	if err != nil {
   373  		return shim.Error("Failed to save student info : " + err.Error())
   374  	}
   375  	//save study log
   376  	err = s.saveStudyLog(stub, student, school, admissionTime)
   377  	if err != nil {
   378  		return shim.Error("Failed to save study log : " + err.Error())
   379  	}
   380  	return shim.Success(nil)
   381  }
   382  
   383  //student graduate
   384  func (s *StudentChaincode) graduate(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   385  	//get transaction time
   386  	timeStamp, err := stub.GetTxTimestamp()
   387  	if err != nil {
   388  		return shim.Error("Failed to get transaction timestamp : " + err.Error())
   389  	}
   390  	graduateTime := fmt.Sprintf("%d", timeStamp.GetSeconds())
   391  	//get sender
   392  	sender, err := stub.GetSender()
   393  	if err != nil {
   394  		return shim.Error("Failed to get sender info : " + err.Error())
   395  	}
   396  	//check args
   397  	schoolNumber := strings.TrimSpace(strings.ToLower(args[0]))
   398  	if len(schoolNumber) <= 0 {
   399  		return shim.Error("1st arg must be non-empty string")
   400  	}
   401  	studentAddr := strings.TrimSpace(strings.ToLower(args[1]))
   402  	if len(studentAddr) <= 0 {
   403  		return shim.Error("2st arg must be non-empty string")
   404  	}
   405  	description := strings.TrimSpace(strings.ToLower(args[2]))
   406  	if len(description) <= 0 {
   407  		return shim.Error("3st arg must be non-empty string")
   408  	}
   409  	creditNum := strings.TrimSpace(strings.ToLower(args[3]))
   410  	if len(creditNum) <= 0 {
   411  		return shim.Error("4st arg must be non-empty string")
   412  	}
   413  	credit, err := strconv.Atoi(args[4])
   414  	if err != nil {
   415  		return shim.Error("5st arg parse int failed : " + err.Error())
   416  	}
   417  	if credit <= 0 {
   418  		return shim.Error("5st arg must be more than 0")
   419  	}
   420  	//check school
   421  	schoolJson, err := stub.GetState(SCHOOL_PREFIX + schoolNumber)
   422  	if err != nil {
   423  		return shim.Error("Failed to get school info : " + err.Error())
   424  	}
   425  	school := &School{}
   426  	err = json.Unmarshal(schoolJson, school)
   427  	if err != nil {
   428  		return shim.Error("Unmarshal school info failed : " + err.Error())
   429  	}
   430  	//check sender
   431  	if school.Manager != sender {
   432  		return shim.Error("Authority failed")
   433  	}
   434  	//check student info
   435  	studentKey := STUDENT_PREFIX + studentAddr
   436  	studentJson, err := stub.GetState(studentKey)
   437  	if err != nil {
   438  		return shim.Error("Failed to get student info : " + err.Error())
   439  	}
   440  	student := &Student{}
   441  	err = json.Unmarshal(studentJson, student)
   442  	if err != nil {
   443  		return shim.Error("Unmarshal student info error : " + err.Error())
   444  	}
   445  	if student.CurrentSchool != school.Number {
   446  		return shim.Error("Student not study at your school")
   447  	}
   448  	//update student info
   449  	student.GraduationLevel = student.CurrentLevel
   450  	student.GraduationSchool = school.Number
   451  	student.GraduationSchoolName = school.Name
   452  	student.GraduationTime = graduateTime
   453  	student.Credit = credit
   454  	student.CurrentSchool = ""
   455  	studentJson, err = json.Marshal(student)
   456  	if err != nil {
   457  		return shim.Error("Student marshal to json failed : " + err.Error())
   458  	}
   459  	//update student state
   460  	err = stub.PutState(studentKey, studentJson)
   461  	if err != nil {
   462  		return shim.Error("Failed to update student info : " + err.Error())
   463  	}
   464  	//save graduate log
   465  	err = s.saveGraduateLog(stub, school, student, description, creditNum)
   466  	if err != nil {
   467  		return shim.Error("Failed to save graduate log : " + err.Error())
   468  	}
   469  	return shim.Success(nil)
   470  }
   471  
   472  //query school info
   473  func (s *StudentChaincode) querySchoolInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   474  	number := strings.TrimSpace(strings.ToLower(args[0]))
   475  	if len(number) <= 0 {
   476  		return shim.Error("1st arg must be non-empty string")
   477  	}
   478  	schoolKey := SCHOOL_PREFIX + number
   479  	schoolJson, err := stub.GetState(schoolKey)
   480  	if err != nil {
   481  		return shim.Error("Failed to get school info : " + err.Error())
   482  	} else if schoolJson == nil {
   483  		return shim.Error("School not exists")
   484  	}
   485  	return shim.Success(schoolJson)
   486  }
   487  
   488  //query student info
   489  func (s *StudentChaincode) queryStudentInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   490  	//check args
   491  	studentAddr := strings.TrimSpace(strings.ToLower(args[0]))
   492  	if len(studentAddr) <= 0 {
   493  		return shim.Error("1st arg must be non-empty string")
   494  	}
   495  	//get student info
   496  	studentKey := STUDENT_PREFIX + studentAddr
   497  	studentJson, err := stub.GetState(studentKey)
   498  	if err != nil {
   499  		return shim.Error("Failed to get student info : " + err.Error())
   500  	} else if studentJson == nil {
   501  		return shim.Error("Student not exists")
   502  	}
   503  	//unmarshal student info
   504  	student := &Student{}
   505  	err = json.Unmarshal(studentJson, student)
   506  	if err != nil {
   507  		return shim.Error("Failed to unmarshal student info : " + err.Error())
   508  	}
   509  	result := map[string]interface{}{
   510  		"studentInfo": student,
   511  	}
   512  	//get study log
   513  	studentStudyLog, err := s.getStudentStudyLog(stub, []string{student.StudentNumber})
   514  	if err == nil {
   515  		result["studyLog"] = studentStudyLog
   516  	}
   517  	//get graduation info log
   518  	graduationInfoLog, err := s.getStudentGraduationLog(stub, []string{student.StudentNumber})
   519  	if err == nil {
   520  		result["graduationLog"] = graduationInfoLog
   521  	}
   522  	//marshal result
   523  	resultJson, err := json.Marshal(result)
   524  	if err != nil {
   525  		return shim.Error("Failed to marshal result : " + err.Error())
   526  	}
   527  	return shim.Success(resultJson)
   528  }
   529  
   530  //query student study log
   531  func (s *StudentChaincode) queryStudentStudyLog(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   532  	studentAddr := strings.TrimSpace(strings.ToLower(args[0]))
   533  	if len(studentAddr) <= 0 {
   534  		return shim.Error("1st arg must be non-empty string")
   535  	}
   536  	studentJson, err := stub.GetState(STUDENT_PREFIX + studentAddr)
   537  	if err != nil {
   538  		return shim.Error("Failed to get student info : " + err.Error())
   539  	} else if studentJson == nil {
   540  		return shim.Error("Student not exists")
   541  	}
   542  	student := &Student{}
   543  	err = json.Unmarshal(studentJson, student)
   544  	if err != nil {
   545  		return shim.Error("Unmarshal student info failed : " + err.Error())
   546  	}
   547  	keyArg := []string{student.StudentNumber}
   548  
   549  	if len(args)>1 {
   550  		schoolNumber := strings.TrimSpace(strings.ToLower(args[1]))
   551  		if len(schoolNumber)>0{
   552  			keyArg = append(keyArg, schoolNumber)
   553  		}else{
   554  			return shim.Error("1st arg must be non-empty string")
   555  		}
   556  	}
   557  	result, err := s.getStudentStudyLog(stub, keyArg)
   558  	if err != nil {
   559  		return shim.Error("Failed to get student study log : " + err.Error())
   560  	}
   561  	resultJson, err := json.Marshal(result)
   562  	if err != nil {
   563  		return shim.Error("Failed to marshal result : " + err.Error())
   564  	}
   565  	return shim.Success(resultJson)
   566  }
   567  
   568  //query student graduation log
   569  func (s *StudentChaincode) queryStudentGraduationLog(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   570  	studentAddr := strings.TrimSpace(strings.ToLower(args[0]))
   571  	if len(studentAddr) <= 0 {
   572  		return shim.Error("1st arg must be non-empty string")
   573  	}
   574  	studentJson, err := stub.GetState(STUDENT_PREFIX + studentAddr)
   575  	if err != nil {
   576  		return shim.Error("Failed to get student info : " + err.Error())
   577  	} else if studentJson == nil {
   578  		return shim.Error("Student not exists")
   579  	}
   580  	student := &Student{}
   581  	err = json.Unmarshal(studentJson, student)
   582  	if err != nil {
   583  		return shim.Error("Unmarshal student info failed : " + err.Error())
   584  	}
   585  	keyArg := []string{student.StudentNumber}
   586  	if len(args)>1 {
   587  		schoolNumber := strings.TrimSpace(strings.ToLower(args[1]))
   588  		if len(schoolNumber)>0{
   589  			keyArg = append(keyArg, schoolNumber)
   590  		}else{
   591  			return shim.Error("1st arg must be non-empty string")
   592  		}
   593  	}
   594  	result, err := s.getStudentGraduationLog(stub, keyArg)
   595  	if err != nil {
   596  		return shim.Error("Failed to get student study log : " + err.Error())
   597  	}
   598  	resultJson, err := json.Marshal(result)
   599  	if err != nil {
   600  		return shim.Error("Failed to marshal result : " + err.Error())
   601  	}
   602  	return shim.Success(resultJson)
   603  }
   604  
   605  //get student study log
   606  func (s *StudentChaincode) getStudentStudyLog(stub shim.ChaincodeStubInterface, keyArg []string) ([]StudyLog, error) {
   607  	//get study log key
   608  	resultsIterator, err := stub.GetStateByPartialCompositeKey(STUDY_LOG_KEY, keyArg)
   609  	if err != nil {
   610  		return nil, err
   611  	}
   612  	defer resultsIterator.Close()
   613  	resultList := make([]StudyLog, 0)
   614  	for i := 0; resultsIterator.HasNext(); i++ {
   615  		responseRange, err := resultsIterator.Next()
   616  		if err != nil {
   617  			return nil, err
   618  		}
   619  		value := responseRange.Value
   620  		tmp := StudyLog{}
   621  		err = json.Unmarshal(value, &tmp)
   622  		if err == nil {
   623  			resultList = append(resultList, tmp)
   624  		}
   625  	}
   626  	return resultList, nil
   627  }
   628  
   629  //get student graduation log
   630  func (s *StudentChaincode) getStudentGraduationLog(stub shim.ChaincodeStubInterface, keyArg []string) ([]GraduationInfo, error) {
   631  	//get graduation key
   632  	resultsIterator, err := stub.GetStateByPartialCompositeKey(GRADUATION_LOG_KEY, keyArg)
   633  	if err != nil {
   634  		return nil, err
   635  	}
   636  	defer resultsIterator.Close()
   637  	resultList := make([]GraduationInfo, 0)
   638  	for i := 0; resultsIterator.HasNext(); i++ {
   639  		responseRange, err := resultsIterator.Next()
   640  		if err != nil {
   641  			return nil, err
   642  		}
   643  		value := responseRange.Value
   644  		tmp := GraduationInfo{}
   645  		err = json.Unmarshal(value, &tmp)
   646  		if err == nil {
   647  			resultList = append(resultList, tmp)
   648  		}
   649  	}
   650  	return resultList, nil
   651  }
   652  
   653  //save study log
   654  func (s *StudentChaincode) saveStudyLog(stub shim.ChaincodeStubInterface, student *Student, school *School, enrolTime string) error {
   655  	log := &StudyLog{
   656  		SchoolName:    school.Name,
   657  		SchoolNumber:  school.Number,
   658  		StudentName:   student.Name,
   659  		StudentNumber: student.StudentNumber,
   660  		Level:         student.CurrentLevel,
   661  		Class:         student.Class,
   662  		Grade:         student.Grade,
   663  		DateTime:      enrolTime,
   664  	}
   665  	//marshal study log data
   666  	logJson, err := json.Marshal(log)
   667  	if err != nil {
   668  		return err
   669  	}
   670  	//create composite key
   671  	compositeKey, err := stub.CreateCompositeKey(STUDY_LOG_KEY, []string{student.StudentNumber, school.Number, enrolTime})
   672  	if err != nil {
   673  		return err
   674  	}
   675  	err = stub.PutState(compositeKey, logJson)
   676  	if err != nil {
   677  		return err
   678  	}
   679  	return nil
   680  }
   681  
   682  //save graduate log
   683  func (s *StudentChaincode) saveGraduateLog(stub shim.ChaincodeStubInterface, school *School, student *Student, description string, creditNumber string) error {
   684  	graduateLog := &GraduationInfo{
   685  		SchoolNumber:    school.Number,
   686  		SchoolName:      school.Name,
   687  		StudentNumber:   student.StudentNumber,
   688  		StudentName:     student.Name,
   689  		GraduationLevel: student.CurrentLevel,
   690  		Description:     description,
   691  		Class:           student.Class,
   692  		Grade:           student.Grade,
   693  		Credit:          creditNumber,
   694  		DateTime:        student.GraduationTime,
   695  	}
   696  	//marshal graduate log data
   697  	graduationJson, err := json.Marshal(graduateLog)
   698  	if err != nil {
   699  		return err
   700  	}
   701  	//create composite key
   702  	compositeKey, err := stub.CreateCompositeKey(GRADUATION_LOG_KEY, []string{student.StudentNumber, school.Number, student.GraduationTime})
   703  	if err != nil {
   704  		return err
   705  	}
   706  	err = stub.PutState(compositeKey, graduationJson)
   707  	if err != nil {
   708  		return err
   709  	}
   710  	return nil
   711  }
   712  
   713  func main() {
   714  	err := shim.Start(new(StudentChaincode))
   715  	if err == nil {
   716  		fmt.Printf("Error starting WorkChaincode: %s", err)
   717  	}
   718  }