食品溯源合约 -- 智能合约实例

前提

Roles: 实现对用户地址的角色权限管控,添加、删除角色。

Producer: 生产商角色管控。

...

FoodInfoItem: 食品信息管控。生产商、中间商、超市添加食品信息。

Trace:食品溯源合约,主要负责对以上几个合约的统筹协作。

Roles

// SPDX-License-Identifier: MIT
pragma solidity >=0.4 <=0.9;

//角色库(管理所有角色地址)
// 1. 实现增加角色地址
// 2. 移除角色地址
// 3. 判断角色地址是否被授权
library Roles{
    struct Role{
        mapping (address =>bool) bearer;
    }
    // 在 Solidity 中,映射(mapping)不能在函数内部声明为局部变量,
    // 也不能在当前版本(0.8.0)中作为库(library)的成员变量。
   

    // 假如role 显示声明storage,那么算是合约中的状态变量,而且不能是memory
    function add(Role storage role,address account) public {   
        require(!has(role,account),"Roles:account already has role");
        role.bearer[account] = true;
    }

    function remove(Role storage role,address account) public {
        require(!has(role,account),"Roles:account has no role ");
        role.bearer[account] = false;
    }

    function has(Role storage role,address account) public  view returns(bool){
        require(account != address(0),"Roles: account cannot be zero address");
        return role.bearer[account];
    }

}

Producer

PS:这下面这三个都是代表角色,代码几乎一样的,看会这个,其他都会。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4 <=0.9;
import "./Roles.sol";
/**
*@title Producer
*@dev 
*/
contract Producer {
    using Roles for Roles.Role;

    event ProducerAdded(address indexed account);
    event ProducerRemoved(address indexed account);

    Roles.Role private _producers; // 使用这个相当于使用库

    constructor(address producer){ // 初始化给账户添加权限
        _addProducer(producer);
    }

    // 关于下面这几个函数为什么要拆分?提高代码可读性。


    modifier onlyProducer(){
        require(
            isProducer(msg.sender),
            "Producer:caller has no Producer role"
        );
        _;
    }

     // 用户是否拥有权限
      function isProducer(address account) public view returns (bool) {
        return _producers.has(account);
    }

    function addProducer(address account) public onlyProducer {
        _addProducer(account); // 不是很理解,为什么设置只有生产者角色才能为地址添加生产者角色权限
    }

    function removeRroducer(address account) public  {
        _removeProducer(account);
    }


     function _addProducer(address account) internal {
        _producers.add(account); 
        // 我们看向Roles的add()其实还有一个参数,但是我们 using for 了那个参数就相当于 _producers本身
        emit ProducerAdded(account);
    }

    function _removeProducer(address account) internal {
        _producers.remove(account);
        emit ProducerRemoved(account);
    }
    
}

Retailer

// SPDX-License-Identifier: MIT
pragma solidity >=0.4 <=0.9;
import "./Roles.sol";

// 超市
contract Retailer {
     using Roles for Roles.Role;

    event RetailerAdded(address indexed account);
    event RetailerRemoved(address indexed account);

    Roles.Role private _retailers; // 使用这个相当于使用库

    constructor(address retailer){ // 初始化给账户添加权限
        _addRetailer(retailer);
    }

    // 关于下面这几个函数为什么要拆分?提高代码可读性。


    modifier onlyRetailer(){
        require(
            isRetailer(msg.sender),
            "Retailer:caller has no Retailer role"
        );
        _;
    }

     // 用户是否拥有权限
      function isRetailer(address account) public view returns (bool) {
        return _retailers.has(account);
    }

    function addRetailer(address account) public onlyRetailer {
        _addRetailer(account); 
    }

    function removeRroducer(address account) public  {
        _removeRetailer(account);
    }


     function _addRetailer(address account) internal {
        _retailers.add(account); 
        // 我们看向Roles的add()其实还有一个参数,但是我们 using for 了那个参数就相当于 _Retailers本身
        emit RetailerAdded(account);
    }

    function _removeRetailer(address account) internal {
        _retailers.remove(account);
        emit RetailerRemoved(account);
    }
    
}

Distributor

// SPDX-License-Identifier: MIT
pragma solidity >=0.4 <=0.9;
import "./Roles.sol";
// 中间商
contract Distributor {
     using Roles for Roles.Role;

    event DistributorAdded(address indexed account);
    event DistributorRemoved(address indexed account);

    Roles.Role private _distributors; // 使用这个相当于使用库

    constructor(address distributor){ // 初始化给账户添加权限
        _addDistributor(distributor);
    }

    // 关于下面这几个函数为什么要拆分?提高代码可读性。


    modifier onlyDistributor(){
        require(
            isDistributor(msg.sender),
            "Distributor:caller has no Distributor role"
        );
        _;
    }

     // 用户是否拥有权限
      function isDistributor(address account) public view returns (bool) {
        return _distributors.has(account);
    }

    function addDistributor(address account) public onlyDistributor {
        _addDistributor(account); 
    }

    function removeRroducer(address account) public  {
        _removeDistributor(account);
    }


     function _addDistributor(address account) internal {
        _distributors.add(account); 
        // 我们看向Roles的add()其实还有一个参数,但是我们 using for 了那个参数就相当于 _Distributors本身
        emit DistributorAdded(account);
    }

    function _removeDistributor(address account) internal {
        _distributors.remove(account);
        emit DistributorRemoved(account);
    }
    
}

FoodInfoItem

// SPDX-License-Identifier: MIT
pragma solidity >=0.4 <=0.9;
pragma experimental ABIEncoderV2;

//食品信息管理合约
// 1.	保存食品基本信息:时间戳(流转过程中),用户名(流转过程中),用户地址信息(流转过程中),食品质量(流转过程中),食物名称,当前用户名称,质量,状态.
// 2.	对食品基本信息进行初始化
// 3.	实现两个方法:中间商添加食品信息;超市添加食品信息
// 4.	实现显示食品信息的方法

contract FoodInfoItem {
    uint[] _timestamp;     //保存食品流转过程中各个阶段的时间戳
    string[] _traceName;    //保存食品流转过程各个阶段的用户名
    address[] _traceAddress; //保存食品流转过程各个阶段的用户地址信息(和用户一一对应)
    uint8[] _traceQuality;  //保存食品流转过程中各个阶段的质量
    string _name;  //食品名称
    string _currentTraceName;  //当前用户名称
    uint8 _quality; //质量(0=优质 1=合格 2=不合格)
    uint8 _status; //状态(0:生产 1:分销 2:出售)
    address  _owner;

   // 初始化食品,创建者是生产商
    constructor(string memory name,string memory traceName,uint8 quality,address producer) public  {
        _name = name;
        _currentTraceName = traceName;
        _quality = quality;
        _owner = producer;
        _status = 0;
        // 不多说,看上面状态变量都能明白
        _timestamp.push(block.timestamp); 
        _traceName.push(traceName);
        _traceAddress.push(producer);
        _traceQuality.push(quality);
    }

   // 中间商添加食品信息
    function addTraceInfoByDistributor(
        string memory traceName,
        uint8 quality,
        address distributor
    ) public returns(bool){
        require(_status == 0, "FoddIndo: caller must be distributor");
        _currentTraceName = traceName;
        _quality = quality;
        _status = 1;

        _timestamp.push(block.timestamp); 
        _traceName.push(traceName);
        _traceAddress.push(distributor);
        _traceQuality.push(quality);

        return true;
    }


    // 超市添加食品信息
    function addTraceInfoByRetailer(
        string memory traceName,
        uint8 quality,
        address retailer
    )public returns(bool){
        require(_status == 1, "FoddIndo: caller must be retailer");
        _currentTraceName = traceName;
        _quality = quality;
        _status = 2;

        _timestamp.push(block.timestamp); 
        _traceName.push(traceName);
        _traceAddress.push(retailer);
        _traceQuality.push(quality);
        return true;
    }

    // 拿到食品流转的全部信息
    function getTraceInfo() public view  returns(uint[] memory,string[] memory,address[] memory,uint8[] memory){
        return (
            _timestamp,
            _traceName,
            _traceAddress,
            _traceQuality
        );
    }

    // 拿到生产商一开始添加食品信息
    function getFood() public view returns(uint,string memory,address,uint8,string memory){
        return (
            _timestamp[0],
            _traceName[0],
            _traceAddress[0],
            _traceQuality[0],
            _name
        );
    }

}


Trace

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
pragma experimental ABIEncoderV2; // 开启实验性功能,包括对map,数组的编码和解码等
import "./FoodInfoItem.sol";
import "./Distributor.sol";
import "./Producer.sol";
import "./Retailer.sol";

//食品溯源合约(负责具体食品溯源信息的生成)
//  1.实现生产食品的方法(新建食品信息)
//  2.实现食品分销过程中增加溯源信息的接口
//  3.实现食品出售过程中增加溯源信息的接口
//  4.实现获取食品溯源信息接口

contract Trace is Distributor,Producer,Retailer {
    mapping(uint256 => address) foods; //食品溯源id到 -> 具体食品溯源合约的映射表
    uint[] foodList; // 食品溯源id数组

    //构造函数初始化,一起把父类角色初始化
    constructor(
        address producer,
        address distributor,
        address retailer
    )    Producer(producer) Distributor(distributor) Retailer(retailer) {}

    // 生产商调用,调用添加食品信息
    function newFood(
        string memory name,
        uint256 traceNumber, // 溯源id
        string memory traceName,
        uint8 quality
    ) public onlyProducer returns (address) {
        require(foods[traceNumber] == address(0), "Trace:traceNumber already exist"); // 检测溯源id对应食品合约是否已经存在,已存在id不能用。
        FoodInfoItem food = new FoodInfoItem( // 初始化食品合约
            name,
            traceName,
            quality,
            msg.sender
        );
        foods[traceNumber] = address(food); // 往映射表添加地址
        foodList.push(traceNumber); // 往食品溯源数组添加溯源id
        return address(food);
    }

   // 中间商调用,添加食品信息
    function addTraceInfoByDistributor(
        uint256 traceNumber,
        string memory traceName,
        uint8 quality
    ) public onlyDistributor returns (bool) {
        require(foods[traceNumber] != address(0), "Trace:traceNumber does not exist"); // id 对应食品合约没存在,代表没食品
        return
            FoodInfoItem(foods[traceNumber]).addTraceInfoByDistributor(traceName,quality,msg.sender); // 调用对应的食品合约方法
    }

   // 超市调用,添加食品信息
    function addTraceInfoByRetailer(
        uint256 traceNumber,
        string memory traceName,
        uint8 quality
    ) public onlyRetailer returns (bool) {
        require(foods[traceNumber] != address(0), "Trace:traceNumber does not exist");
        return
            FoodInfoItem(foods[traceNumber]).addTraceInfoByRetailer(traceName,quality,msg.sender);
    }

    // 拿到所有食品信息
    function getTraceInfo(
        uint256 traceNumber
    ) public view returns (uint[] memory,string[] memory,address[] memory,uint8[] memory) {
        require(foods[traceNumber] != address(0), "Trace:traceNumber does not exist");
        return FoodInfoItem(foods[traceNumber]).getTraceInfo();
    }

    //拿到单条食品信息
    function getFood(
        uint256 traceNumber
    ) public view returns (uint,string memory,address,uint8,string memory) {
        require(foods[traceNumber] != address(0), "Trace:traceNumber does not exist");
        return FoodInfoItem(foods[traceNumber]).getFood();
    }

    // 拿到全部食品的溯源id数组
    function getAllFood() public view returns (uint[] memory) {
        return foodList;
    }

}

你可能感兴趣的:(区块链开发,智能合约,区块链)