iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 14
1
Blockchain

區塊練起來-智能合約與DApp開發系列 第 14

[區塊練起來-智能合約與DApp開發] DAY 14 - 實戰智能合約!區塊鏈履歷(2)

  • 分享至 

  • xImage
  •  

貼心小語

上一篇我們已經初步完成 ResumeBase 合約,能夠提供 Resume 合約該有的狀態變數、結構等,那麼今天就繼續努力把 Resume 合約完成吧!


主合約 - Resume

這個合約為我們主要操作的合約,提供外部操作合約的多種函式,那麼就先來建立合約吧!

pragma solidity ^0.5.0;

import { ResumeBase } from "./ResumeBase.sol";

contract Resume is ResumeBase {
}

有注意到這次的 import 方法跟之前寫的不一樣嗎?這樣的寫法跟 ES6 的 import 方法有異曲同工之妙!我個人比較偏好這樣的 import 方式,不過這部分就是看個人習慣,看得懂最重要~

我們在父合約有使用 constructor 來設定初始資料,那麼要怎麼樣在部署 Resume 合約時,把參數帶入 ResumeBase 的 constructor 中呢?其實很簡單:

constructor(string memory name, address account, uint8 age, Gender gender) public ResumeBase(name, account, age, gender) {}

我們前面有提到 Solidity 預設是無法回傳陣列型別的,所以要完整取得一個陣列中的所有元素,就必須要知道陣列有多少個元素,再從外部一一取出來,這表示我們需要先提供外部取得陣列長度的函式。取出 educationsexperiences 以及 skills 很容易,因為只需要取出一層的陣列,但 courses 以及 licenses 是在 educations 裡面的,每一筆學歷資料裡面都含有這兩個陣列,所以需要指定是哪一筆學歷資料,才能夠取得 courseslicenses 的陣列長度,並要用 IndexValidator 來確認給定的 index 是不是合法的:

function getEducationCount() public view returns(uint) {
  return educations.length;
}

function getExperienceCount() public view returns(uint) {
  return experiences.length;
}

function getSkillCount() public view returns(uint) {
  return skills.length;
}

function getCourseCount(uint index) public view IndexValidator(index, getEducationCount()) returns(uint) {
  return educations[index].courses.length;
}

function getLicenseCount(uint index) public view IndexValidator(index, getEducationCount()) returns(uint) {
  return educations[index].licenses.length;
}

能夠取得陣列長度後,在 DApp 裡面就可以知道資料筆數進而用迴圈去取得資料,那麼要怎麼樣取得陣列中的資料呢?答案就是繼續寫函式!(被打)。不過在寫的時候需要注意, Solidity 不能夠回傳陣列, 也不能回傳結構 ,所以在回傳時需要個別回傳:

function getEducation(uint index) public view IndexValidator(index, getEducationCount())
returns(string memory, EducationStatus, string memory) {
  Education memory edu = educations[index];
  return(edu.school.name, edu.status, edu.major);
}

function getExperience(uint index) public view IndexValidator(index, getExperienceCount())
returns(string memory, string memory, uint, uint) {
  Job memory exp = experiences[index];
  return(exp.company.name, exp.position, exp.startDate, exp.endDate);
}

function getSkill(uint index) public view IndexValidator(index, getSkillCount())
returns(string memory, string memory) {
  Skill memory skill = skills[index];
  return(skill.class, skill.name);
}

function getCourse(uint eduIndex, uint index) public view
IndexValidator(eduIndex, getEducationCount())
IndexValidator(index, getCourseCount(eduIndex))
returns(string memory, string memory, string memory, uint8) {
  Course memory course = educations[eduIndex].courses[index];
  return(course.name, course.content, course.comment, course.grade);
}

function getLicense(uint eduIndex, uint index) public view
IndexValidator(eduIndex, getEducationCount())
IndexValidator(index, getCourseCount(eduIndex))
returns(string memory, string memory) {
  License memory license = educations[eduIndex].licenses[index];
  return(license.name, license.content);
}

接著來寫比較複雜的 設置 用的函式,因為設置用的函式會改變狀態變數,所以會發起交易,也就表示需要挖礦時間,故用 event 寫 log ,好讓 DApp 可以監聽交易進度並在交易上留下操作紀錄。第一個就是最基礎的,由政府設置機構單位權限的函式:

function setPermission(address account, string memory name, OrganizationType property, bool permission) public onlyGov {
  organizations[account] = Organization({
    name: name,
    property: property,
    account: account,
    permission: permission
  });
  emit done(DoneCode.setPermission, "Set Permission");
}

設置工作經歷與離職日的函式:

function setExperience(string memory position, uint startDate) public onlyCompany {
  Job memory info = Job({
    company: organizations[msg.sender],
    position: position,
    startDate: startDate,
    endDate: 0
  });
  experiences.push(info);
  emit done(DoneCode.setExperience, "Set Experience");
}

function setJobEndDate(uint endDate) public onlyCompany {
  uint index = uint(findOrganization(msg.sender, "experience"));
  experiences[index].endDate = endDate;
  emit done(DoneCode.setJobEndDate, "Set JobEndDate");
}

設置學歷的方式較為特別,因為學歷的結構中還有其他結構的陣列,而且是無法忽略的,表示在新增的同時就需要帶入 courseslicenses ,然而 Solidity 無法在 memory 的結構變數中利用 push 新增結構中的結構陣列(嗯...好繞口令XD),簡單來說就是我們不能先宣告一個 memoryEducation 變數並用 push 的方式新增 courseslicenses ,需要有一個 storageEducation 才能夠 push 進去,但今天我們 educations 本身就是該被新增元素的陣列,不可能做一個狀態變數專門處理這種事情,一定是要用 educations 這個陣列來處理,那我們要怎樣才能這麼做呢?其實我們可以先擴充 educations 的長度,然後用擴充的這個來做處理,這也是 Solidity 留的後路(啊不就還好動態陣列可以這樣擴充/images/emoticon/emoticon03.gif):

function setEducation(EducationStatus status, string memory major) public onlySchool {
  educations.length++;
  Education storage edu = educations[educations.length - 1];
  Course memory course = Course({ name: "", content: "", comment: "", grade: 0 });
  License memory license = License({ name: "", content: "" });
  edu.school = organizations[msg.sender];
  edu.status = status;
  edu.major = major;
  edu.courses.push(course);
  edu.licenses.push(license);
  emit done(DoneCode.setEducation, "Set Education");
}

接著就是設置專業證照與修課證明,會需要先找到該教育單位位於陣列的哪個位置,再針對該元素更新:

function setLicense(string memory name, string memory content) public onlySchool {
  uint index = uint(findOrganization(msg.sender, "education"));
  Education storage edu = educations[index];
  edu.licenses.push(License({ name: name, content: content }));
  emit done(DoneCode.setLicense, "Set License");
}

function setCourse(string memory name, string memory content, string memory comment, uint8 grade) public onlySchool {
  uint index = uint(findOrganization(msg.sender, "education"));
  Education storage edu = educations[index];
  edu.courses.push(Course({ name: name, content: content, comment: comment, grade: grade }));
  emit done(DoneCode.setCourse, "Set Course");
}

還有設置自傳、專業技術與聯絡方式的函式:

function setAutobiography(string memory text) public onlyHost {
  profile.autobiography = text;
  emit done(DoneCode.setAutobiography, "Set Autobiography");
}

function setSkill(string memory class, string memory name) public onlyHost {
  skills.push(Skill({ class: class, name: name }));
  emit done(DoneCode.setSkill, "Set Skill");
}

function setContact(string memory contact) public onlyHost {
  profile.contact = contact;
  emit done(DoneCode.setContact, "Set Contact");
}

移除 的部分就是移除權限與專業技能:

function removePermission(address account) public onlyGov {
  Organization storage org = organizations[account];
  org.permission = false;
  emit done(DoneCode.removePermission, "Remove Permission");
}

function removeSkill(string memory class, string memory name) public onlyHost {
  uint index = 0;
  for (uint i = 0; i < skills.length; i++) {
    if (skills[i].name.compare(name) && skills[i].class.compare(class)) {
      index = i;
      break;
    }
  }
  for (uint i = index; index < skills.length - 1; i++) {
    skills[i] = skills[i + 1];
  }
  delete skills[skills.length - 1];
  skills.length--;

  emit done(DoneCode.removeSkill, "Remove Skill");
}

恭喜完成簡單的履歷合約了!


今日小結

今天完成了履歷智能合約,預計明天的內容會是做驗證,實際操作一次讓各位更了解這份智能合約要如何使用!


上一篇
[區塊練起來-智能合約與DApp開發] DAY 13 - 實戰智能合約!區塊鏈履歷(1)
下一篇
[區塊練起來-智能合約與DApp開發] DAY 15 - 實戰智能合約!區塊鏈履歷(3)
系列文
區塊練起來-智能合約與DApp開發31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
tytom2003
iT邦新手 5 級 ‧ 2021-02-28 21:59:50

請問"emit done(DoneCode.removeSkill, "Remove Skill");"這句是否會立刻寫入event log,還是將Remove Skill字串儲存DoneCode裏的removeSkill裏,之後才寫入event log?

HAO iT邦研究生 1 級 ‧ 2021-03-02 15:13:43 檢舉

你好,DoneCode 只是 enum,並不會有任何資料被寫入 enum,另外,event log 是在交易處理完後才能夠查詢到。

我要留言

立即登入留言