基于fisco-bcos的共享病例智能合约的设计与实现【3】

环境

fisco-bcos 2.8
ubuntu 20
solidity 0.4.25[版本有点老,也可以用0.6的版本 ]

前言

大家可以阅读上篇对于这个系统的介绍
上一篇 基于fisco-bcos的共享病例智能合约的设计与实现【2】
这篇将介绍共享病历系统的Roles库合约用RBAC 合约将 患者合约结合起来;

医生合约

pragma solidity^0.4.25;

// 医院信息管理智能合约
// 医生相关信息
contract Doctor{
    string public DoctorName;
    uint256 public Phone;
    string public ProfessionTitle;
    uint256   public Age;
    uint8 public Gendor; // 0 男,1 女
    uint256 public CreateTime;   
    constructor(string _dName,uint256 _phone,string _pTitle) public{
        DoctorName = _dName;
        Phone = _phone;
        ProfessionTitle = _pTitle;
        Age = 0;
        Gendor = 0;
        CreateTime = now;
    }    
    function getDoctorDetailByD() public view returns(string,uint256,string,uint256,uint8,uint256){
        return (DoctorName,Phone,ProfessionTitle,Age,Gendor,CreateTime);
    }
    function updateDoctorDetailByD(string _name,uint256 _phone,string _pTitle,uint256 _age,uint8 _gendor) public {
        if(keccak256(abi.encode(_name)) != keccak256(abi.encode(""))){
            DoctorName= _name;
        }
        
        if(_phone != 0){
            Phone = _phone;
        }
        if(keccak256(abi.encode(_pTitle)) != keccak256(abi.encode(""))){
            ProfessionTitle = _pTitle;
        }
        
        if(_age != 0){
            Age = _age;
        }
         Gendor = _gendor;
    }
    
}

这个医生合约 有名字,电话,职称,年龄,性别,创建时间 ,变量。在构造器的时候对其进行初始化,然后updateDoctorDetailByD()方法可以对其更新,getDoctorDetailByD() 方法可以查看医生的基础信息。

这种设计有点像软件工程的设计实体一样,我已java web为例子
我们每在数据库设计好一张表,如user 表,里面有 id,username,password 三个字段
然后我们使用java 往数据库存储数据的时候,我们是使用 类来映射数据库字段,所以创建了个entity(实体),> User类

public class User {
   private Integer id;
    
    private String  username;
    private String  password;
}

将这种设计应用在智能合约上,这个实体 就是医生合约。但是区块链就没有数据库这个层了,因为只要医生合约部署了或者在别的合约里 new 创建出来,就已经将一个医生信息给存储起来了。

病人合约

pragma solidity^0.4.25;


contract Patient{
    address public  patientId;
    string public patientName;
    uint256 public phone;
    string public allergy; //过敏史  默认:无
    uint32   public age;  //年龄   
    uint8  public  gender; //性别  默认:0,男    ; 1,女
    uint256 public  createTime;
    
    
    
    // 初始化一个病人信息
    constructor(address _amount,uint256 _phone,string _userName) public{
            patientId = _amount;
            patientName = _userName;
            createTime = now;
            phone = _phone;
            allergy = "无";
            gender = 0;
       
    }
    
    
    function updatePatientByP(string  _allergy,uint32 _age,uint8 _gender) public{
            if(keccak256(abi.encode(_allergy)) != keccak256(abi.encode(""))){
            allergy = _allergy;
            }
            if(_age != 0){
            age = _age;
            }
            
            _gender = _gender;
    }
    
    function getPatientDetailByP() public view returns(string,uint256,string,uint32,uint8,uint256){
         return (patientName,phone,allergy,age,gender,createTime);
    }
    
    
    
}

address public patientId;
string public patientName;
uint256 public phone;
string public allergy; //过敏史 默认:无
uint32 public age; //年龄
uint8 public gender; //性别 默认:0,男 ; 1,女
uint256 public createTime;

这个病人合约有病人id,姓名,电话,过敏史,年龄,性别和创建时间,基本与医生合约的变量没啥太大区别。

RBAC合约

这是我设计的一个桥梁合约,其作用就是用来连接Roles库合约和病人医生合约,将其绑定一起,来方便在后面的业务操作中执行相应 的角色功能和判断其角色权限

构造器

    // 角色库使用
    using Roles for Roles.Role;
    mapping(string=>Roles.Role)  roles;
    
    // 管理员
    address admin;
    
    // 构造器
    constructor(address amount) public{
        admin = amount;
   }

构造器的用处是设置一个管理员,最后默认是部署合约的那个人
然后 将Roles 附加到Roles.Role 类型上,这样Roles的函数将接收到调用它们的对象作为它们的第一个参数。

– 在这还设置了一个mapping roles,用来做角色区分,将病人和医生的role区分开。

基础方法

   // ----------------------------------基础方法---------------------------------------------------
    function addRole(address _operater,string memory _role,address _roleDetail) private{
        roles[_role].addRole(_operater,_roleDetail);
        
    }    
    function hasRole(address _operater,string memory _role) public view returns(bool){
        return roles[_role].hasRole(_operater);
    }   

    function removeRole(address _operater,string memory _role) public{
        roles[_role].removeRole(_operater);
    }
    
    function getRoleList(string memory _role) public view returns(address[]){
        return roles[_role].getRoleList();
    }

这几个方法可以方便的为我们处理Role,封装起来,后续使用

病人相关方法

    // 病人用户进行注册
    
    event EventPatientEnroll(address userName,address patient);
    function patientEnroll(uint256   _phone,string    _userName) public returns(bool){
        // address _amount,uint256 _phone,string _userName
        Patient   patient = new  Patient(msg.sender,_phone,_userName);
        addRole(msg.sender,"PA",address(patient));
        emit EventPatientEnroll(msg.sender,address(patient));
        return true;
    }

这里就需要new一个Patient的合约,这个合约在上一篇有讲解,
然后使用addRole() 方法,加入病人Role Mapping, 这里可以看到第一个参数是msg.sender是用户地址,第二个参数是角色标识,第三个参数是病人合约地址。并且调用注册病人事件。

    
    // 病人用户进行信息更新
    function updateByPatient(string memory _role,string memory  _allergy,uint32 _age,uint8 _gender) onlyPatient(msg.sender) public returns(bool){
        address paticentAddress = roles[_role].getRoleDetail(msg.sender);
        Patient patient = Patient(paticentAddress);
        patient.updatePatientByP(_allergy,_age,_gender);
        return true;
    }
    
    // 查看病人的详细信息
    function getPatientDetail(string memory _role,address amount) public  view onlyPatient(amount)  returns(string,uint256,string,uint32,uint8,uint256){
        address paticentAddress = roles[_role].getRoleDetail(amount);
        Patient patient = Patient(paticentAddress);
        return patient.getPatientDetailByP();
    }
    
    // 病人删除
    function removePatient(string memory _role) public  onlyPatient(msg.sender) returns(bool){
        removeRole(msg.sender,_role);
        return true;
        
    }
    
    // 工具类,获取该病人的合约地址

    function getPatientAddress2(address amount) public view returns(address){
        return roles["PA"].getRoleDetail(amount);
    }

这些方法就不作讲解,都是简单的更改查看删除操作,调用的都是之前写好的底层方法。

– 最后一个方法,这里我有个经验,分享给大家

    
    // 工具类,获取该病人的合约地址
    function getPatientAddress() public view onlyPatient(tx.origin) returns(address){
          return roles["PA"].getRoleDetail(tx.origin);
    }

这是我之前写的,用到的是tx.origin ,但是这是有问题的,这会产生钓鱼问题,举个例子,病人A直接调用这个方法没问题,若有恶人B,写了一个方法C[方法里面其实是调用getPatientAddress()],病人A被诱导去调用这个方法C, 这样getPatientAddress 识别的tx.origin 还是病人A,这个方法就通过了权限modify ,恶人B 也相当于形式了病人A的权限。所以避免这个钓鱼漏洞,可以将其改成msg.sender,若真的需要调用tx.origin ,可以加一条tx.origin == msg.sender, 但我推荐第一个方法预防,因为后者会拒绝所有来自其他合约的调用。

结语

这篇介绍了病人合约和医院合约和RBAC合约对病人合约的用法,下一篇将介绍如何用RBAC合约结合医生合约,并且还需要介绍医院 和科室合约【因为医生需要和医院和科室进行绑定】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

已久依依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值