{"content":{"title":"Dapp 数字积分 开发（一）","body":"学习以太坊一段时间，一直想想找个demo练习一下，看到趣链的一个数字积分项目，突然想重新写一边，增加自己对相关概念的理解，也积累一点项目经验。\r\n\r\n总体架构\r\n---\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/12/l8KPNbzD638db993419b1.png)\r\n\r\n\r\n合约开发\r\n---\r\n开发工具：vscode + hardhat + Ganache\r\n\r\n合约大约有两个文件，一个Utils.sol文件和Score.sol 文件\r\n\r\n```\r\n// SPDX-License-Identifier: SEE LICENSE IN LICENSE\r\npragma solidity ^0.8.9;\r\n\r\n/// @title  工具类\r\n/// @author arrom\r\ncontract Utils {\r\n    /// @notice string ->bytes32\r\n    function stringToBytes32(\r\n        string memory source\r\n    ) internal pure returns (bytes32 result) {\r\n        assembly {\r\n            result := mload(add(source, 32))\r\n        }\r\n    }\r\n\r\n    /// @notice bytes32 ->string\r\n    function bytes32ToString(bytes32 x) internal pure returns (string memory) {\r\n        bytes memory bytesString = new bytes(32);\r\n        uint charCount = 0;\r\n        for (uint j = 0; j < 32; j++) {\r\n            bytes1 char = bytes1(bytes32(uint(x) * 2 ** (8 * j)));\r\n            if (char != 0) {\r\n                bytesString[charCount] = char;\r\n                charCount++;\r\n            }\r\n        }\r\n        bytes memory bytesStringTrimmed = new bytes(charCount);\r\n\r\n        for (uint j = 0; j < charCount; j++) {\r\n            bytesStringTrimmed[j] = bytesString[j];\r\n        }\r\n        return string(bytesStringTrimmed);\r\n    }\r\n}\r\n\r\n```\r\n\r\nScore.sol \r\n```\r\n// SPDX-License-Identifier: SEE LICENSE IN LICENSE\r\npragma solidity ^0.8.9;\r\n\r\nimport \"./Utils.sol\";\r\n\r\n/// @title  积分\r\n/// @author arrom\r\ncontract Score is Utils {\r\n    /// @notice 合约的拥有者\r\n    address owner;\r\n    /// @notice 发行的积分总数\r\n    uint issuedScoreAmount;\r\n    /// @notice 已经清算的积分总数\r\n    uint settledScoreAmount;\r\n\r\n    struct Customer {\r\n        /// @notice 客户的address\r\n        address customerAddr;\r\n        /// @notice 客户密码\r\n        bytes32 password;\r\n        /// @notice 积分\r\n        uint scoreAmount;\r\n        /// @notice 兑换的商品\r\n        bytes32[] buyGoods;\r\n    }\r\n\r\n    struct Merchant {\r\n        /// @notice 商户address\r\n        address merchantAddr;\r\n        /// @notice 商户密码\r\n        bytes32 password;\r\n        /// @notice 积分余额\r\n        uint scoreAmount;\r\n        /// @notice 发布的商品数组\r\n        bytes32[] sellGoods;\r\n    }\r\n\r\n    struct Good {\r\n        /// @dev 商品Id\r\n        bytes32 goodId;\r\n        ///  @dev 商品价格\r\n        uint price;\r\n        ///  @dev 商品属于哪个商户\r\n        address belong;\r\n    }\r\n\r\n    mapping(address => Customer) customer;\r\n    mapping(address => Merchant) merchant;\r\n    /// @notice 根据商品ID查找该商品\r\n    mapping(bytes32 => Good) good;\r\n\r\n    /// @notice 已注册的客户\r\n    address[] customers;\r\n    /// @notice 已注册的商户数\r\n    address[] merchants;\r\n    /// @notice 商品\r\n    bytes32[] goods;\r\n\r\n    /// @dev 只有合约的创建者才能调用\r\n    modifier onlyOwner() {\r\n        if (msg.sender == owner) _;\r\n    }\r\n\r\n    constructor() {\r\n        owner = msg.sender;\r\n    }\r\n\r\n    /// @notice 返回合约调用者的地址\r\n    function getOwner() public view returns (address) {\r\n        return owner;\r\n    }\r\n\r\n    /// @notice 客户注册\r\n    event RegCustomer(address sender, bool isSuccess, string password);\r\n\r\n    /// @notice 客户注册\r\n    function regCustomer(\r\n        address _customerAddr,\r\n        string memory _password\r\n    ) public {\r\n        /// @dev 判断是否注册\r\n        if (!isCustomerAlreadyReg(_customerAddr)) {\r\n            /// @dev 未注册\r\n            customer[_customerAddr].customerAddr = _customerAddr;\r\n            customer[_customerAddr].password = stringToBytes32(_password);\r\n            customers.push(_customerAddr);\r\n            emit RegCustomer(msg.sender, true, _password);\r\n            return;\r\n        } else {\r\n            emit RegCustomer(msg.sender, false, _password);\r\n            return;\r\n        }\r\n    }\r\n\r\n    /// @notice 判断客户是否注册\r\n    function isCustomerAlreadyReg(\r\n        address _customerAddr\r\n    ) internal view returns (bool) {\r\n        for (uint i = 0; i < customers.length; i++) {\r\n            if (customers[i] == _customerAddr) return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    /// @notice 查询用户密码\r\n    function getCustomerPassword(\r\n        address _customerAddr\r\n    ) public view returns (bool, bytes32) {\r\n        /// @notice 先判断该用户注册\r\n        if (isCustomerAlreadyReg(_customerAddr)) {\r\n            return (true, customer[_customerAddr].password);\r\n        } else {\r\n            return (false, \"\");\r\n        }\r\n    }\r\n\r\n    /// @notice 查询商户密码\r\n    function getMerchantPassword(\r\n        address _merchantAddr\r\n    ) public view returns (bool, bytes32) {\r\n        if (isMerchantAlreadyReg(_merchantAddr)) {\r\n            return (true, merchant[_merchantAddr].password);\r\n        }\r\n        return (false, \"\");\r\n    }\r\n\r\n    /// @notice 商户注册\r\n    event RegMerchant(address sender, bool isSuccess, string message);\r\n\r\n    function regMerchant(\r\n        address _merchantAddr,\r\n        string memory _password\r\n    ) public {\r\n        if (!isMerchantAlreadyReg(_merchantAddr)) {\r\n            merchant[_merchantAddr].merchantAddr = _merchantAddr;\r\n            merchant[_merchantAddr].password = stringToBytes32(_password);\r\n            merchants.push(_merchantAddr);\r\n            emit RegMerchant(msg.sender, true, unicode\"注册\");\r\n\r\n            return;\r\n        } else {\r\n            emit RegMerchant(msg.sender, false, unicode\"该账户已经注册\");\r\n            return;\r\n        }\r\n    }\r\n\r\n    /// @notice 商户是否注册\r\n    function isMerchantAlreadyReg(\r\n        address _merchantAddr\r\n    ) internal view returns (bool) {\r\n        for (uint i = 0; i < customers.length; i++) {\r\n            if (customers[i] == _merchantAddr) {\r\n                return true;\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    /// @notice 发送积分给客户(智能银行调用)\r\n    event SendScoreToCustomer(address sender, string message);\r\n\r\n    function sendScoreToCustomer(\r\n        address _receiver,\r\n        uint _amount\r\n    ) public onlyOwner {\r\n        if (isCustomerAlreadyReg(_receiver)) {\r\n            /// @dev 已经注册\r\n            issuedScoreAmount += _amount;\r\n            customer[_receiver].scoreAmount += _amount;\r\n            emit SendScoreToCustomer(msg.sender, unicode\"发行积分成功\");\r\n            return;\r\n        }\r\n        emit SendScoreToCustomer(\r\n            msg.sender,\r\n            unicode\"该账户未注册，发行积分失败\"\r\n        );\r\n        return;\r\n    }\r\n\r\n    /// @notice 根据客户address查询余额\r\n    function getScoreWithCustomerAddr(\r\n        address customerAddr\r\n    ) public view returns (uint) {\r\n        return customer[customerAddr].scoreAmount;\r\n    }\r\n\r\n    /// @notice 根据商户address查找余额\r\n    function getScoreWithMerchantAddr(\r\n        address merchantAddr\r\n    ) public view returns (uint) {\r\n        return merchant[merchantAddr].scoreAmount;\r\n    }\r\n\r\n    /// @notice 转移积分\r\n    event TransferScoreToAnother(address sender, string message);\r\n\r\n    function transferScoreToAnother(\r\n        uint _senderType,\r\n        address _sender,\r\n        address _receiver,\r\n        uint _amount\r\n    ) public {\r\n        if (\r\n            !isCustomerAlreadyReg(_receiver) && !isMerchantAlreadyReg(_receiver)\r\n        ) {\r\n            emit TransferScoreToAnother(\r\n                msg.sender,\r\n                unicode\"账户不存在，请确认后再转移!\"\r\n            );\r\n            return;\r\n        }\r\n        if (_senderType == 0) {\r\n            //客户转移\r\n            if (customer[_sender].scoreAmount >= _amount) {\r\n                customer[_sender].scoreAmount -= _amount;\r\n                if (isCustomerAlreadyReg(_receiver)) {\r\n                    customer[_receiver].scoreAmount += _amount;\r\n                } else {\r\n                    merchant[_receiver].scoreAmount += _amount;\r\n                }\r\n                emit TransferScoreToAnother(msg.sender, unicode\"积分转让成功!\");\r\n                return;\r\n            }\r\n            emit TransferScoreToAnother(\r\n                msg.sender,\r\n                unicode\"你的积分不够,转让失败!\"\r\n            );\r\n            return;\r\n        } else {\r\n            /// @dev 商户转移\r\n            if (merchant[_sender].scoreAmount >= _amount) {\r\n                merchant[_sender].scoreAmount -= _amount;\r\n                if (isCustomerAlreadyReg(_receiver)) {\r\n                    customer[_receiver].scoreAmount += _amount;\r\n                } else {\r\n                    merchant[_receiver].scoreAmount += _amount;\r\n                }\r\n                emit TransferScoreToAnother(msg.sender, unicode\"积分转让成功!\");\r\n                return;\r\n            }\r\n            emit TransferScoreToAnother(\r\n                msg.sender,\r\n                unicode\"你的积分不够,转让失败!\"\r\n            );\r\n            return;\r\n        }\r\n    }\r\n\r\n    /// @notice 查询发行的积分总数\r\n    function getIssuedScoreAmount() public view returns (uint) {\r\n        return issuedScoreAmount;\r\n    }\r\n\r\n    /// @notice 清算的积分总数\r\n    function getSettleScoreAmount() public view returns (uint) {\r\n        return settledScoreAmount;\r\n    }\r\n\r\n    /// @notice 商户添加商品\r\n    event AddGood(address sender, bool isSuccess, string message);\r\n\r\n    function addGood(\r\n        address _merchantAddr,\r\n        string memory _goodId,\r\n        uint _price\r\n    ) public {\r\n        bytes32 tempId = stringToBytes32(_goodId);\r\n        /// @dev 判断是否存在\r\n        if (!isGoodAlreadyAdd(tempId)) {\r\n            good[tempId].goodId = tempId;\r\n            good[tempId].price = _price;\r\n            good[tempId].belong = _merchantAddr;\r\n            goods.push(tempId);\r\n            merchant[_merchantAddr].sellGoods.push(tempId);\r\n            emit AddGood(msg.sender, true, unicode\"创建商品成功!\");\r\n            return;\r\n        }\r\n        emit AddGood(msg.sender, false, unicode\"商品已经添加，请确认后操作!\");\r\n        return;\r\n    }\r\n\r\n    function isGoodAlreadyAdd(bytes32 _goodId) internal view returns (bool) {\r\n        for (uint i = 0; i < goods.length; i++) {\r\n            if (goods[i] == _goodId) {\r\n                return true;\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    /// @notice 积分购买商品\r\n    event BuyGood(address sender, bool isSuccess, string message);\r\n\r\n    function buyGood(address _customerAddr, string memory _goodId) public {\r\n        /// @dev 判断商品Id是否存在\r\n        bytes32 tempId = stringToBytes32(_goodId);\r\n        if (isGoodAlreadyAdd(tempId)) {\r\n            /// @dev 商品已经添加，可以购买\r\n            if (customer[_customerAddr].scoreAmount < good[tempId].price) {\r\n                emit BuyGood(\r\n                    msg.sender,\r\n                    false,\r\n                    unicode\"余额不足，购买商品失败!\"\r\n                );\r\n                return;\r\n            }\r\n            customer[_customerAddr].scoreAmount -= good[tempId].price;\r\n            merchant[good[tempId].belong].scoreAmount += good[tempId].price;\r\n            customer[_customerAddr].buyGoods.push(tempId);\r\n            emit BuyGood(msg.sender, true, unicode\"购买商品成功!\");\r\n            return;\r\n        } else {\r\n            emit BuyGood(\r\n                msg.sender,\r\n                false,\r\n                unicode\"输入商品Id不存在，请确认后购买!\"\r\n            );\r\n            return;\r\n        }\r\n    }\r\n\r\n    /// @notice 查询购买的商品\r\n    function getGoodsByCustomer(\r\n        address _customerAddr\r\n    ) public view returns (bytes32[] memory) {\r\n        return customer[_customerAddr].buyGoods;\r\n    }\r\n\r\n    /// @notice 商户查找自己的商品\r\n    function getGoodsByMerchant(\r\n        address _merchantAddr\r\n    ) public view returns (bytes32[] memory) {\r\n        return merchant[_merchantAddr].sellGoods;\r\n    }\r\n\r\n    /// @notice 商户和银行清算积分\r\n    event SettleScoreWithBank(address sender, string message);\r\n\r\n    function settleScoreWithBank(address _merchantAddr, uint _amount) public {\r\n        if (merchant[_merchantAddr].scoreAmount >= _amount) {\r\n            merchant[_merchantAddr].scoreAmount -= _amount;\r\n            settledScoreAmount += _amount;\r\n            emit SettleScoreWithBank(msg.sender, unicode\"积分清算成功!\");\r\n            return;\r\n        }\r\n        emit SettleScoreWithBank(\r\n            msg.sender,\r\n            unicode\"您的积分余额不足，清算失败!\"\r\n        );\r\n        return;\r\n    }\r\n}\r\n\r\n```"},"author":{"user":"https://learnblockchain.cn/people/5348","address":null},"history":null,"timestamp":1670238847,"version":1}