主頁 > 區塊鏈 > FISCO-BCOS應用實戰:區塊鏈實戰應用開發分享

FISCO-BCOS應用實戰:區塊鏈實戰應用開發分享

2021-12-29 08:52:37 區塊鏈

政務通——區塊鏈助力政府辦公

1.專案簡介

? 區塊鏈具有不可篡改性以及可追溯性,因此對于一些重要資訊區塊鏈更能夠保障資訊的安全,基于區塊鏈的這兩大特點,本篇將介紹如何將區塊鏈應用于政府辦公,實作協同辦公資料脫敏上鏈,以及資料溯源打破資料孤島等功能,以小程式為載體,體現區塊鏈在實際生活中的具體作用,總體設計分為四個模塊,具體如表1.1所示,

? 表1.1 功能模塊分析

功能模塊技術特點
1. 用戶管理模塊注冊時候對用戶資訊進行資產數字化處理,用戶密碼等關鍵資訊脫敏上鏈,存盤的是通過sha256運算后的哈希值,保障了用戶的安全,用戶登錄時,輸入密碼進行一次哈希運算,與鏈上比對,即完成“確權”,驗證一致才可登陸,
2. 建言獻策模塊用戶留言內容記錄上鏈,同時對留言內容呼叫外部api,如果留言內容涉及敏感詞,則扣除用戶信用積分,打造一個好的社會信用生態
3巡檢模塊用戶打卡記錄上鏈,涉及“資料溯源”
4.政務合作模塊體現聯盟鏈的**“多方協作”**特點.

2 專案優勢

區塊鏈技術的去中心化、不可篡改、可信任、可溯源等特點,使得區塊鏈技術不僅在資料安全領域有所作為,在政務服務系統中也可大展身手,通過對大量資料資訊 的分析和快速處理,區塊鏈應用開發技術可以迅速將有效資訊傳遞至各部門,為扁平 化管理創造了條件,因此本專案有如下特點與優勢,

1、區塊鏈技術可在政府部門間構建起分布式對等網路,讓政府組織結構的資訊傳 遞更加直接高效,部門間可運用區塊鏈技術直接進行點對點資訊傳遞,

2、區塊鏈分布式的模式特點可以實作多部門間的資料同享,可使得政務管理層級 減少,部門與部門間、上級與下級間的溝通會更順暢,對于人員的需求也會相應減少, 政府部門可利用區塊鏈技術打造高效的行政系統,推動政府治理和公共服務模式創新,

3、每個在區塊鏈上獲取資料的主體是平等關系,需要共同承擔管理責任,資料的 變動和更改會同步在整個網路節點上更新,這種變動需要每個參與者確認,即使部分 資料庫系統出現失靈或錯誤,其他節點資料依然完整,資料庫系統依舊可以正常有效 運轉,

4、公共服務部門利用區塊鏈技術可以降低成本、保證資料安全、增加信任、透明 度和可靠性,區塊鏈的特性使得資料可以追根溯源,資料安全性提升且不能隨意變動, 有助于建立權威資料庫,進而建立更安全、開放、包容高效的公共服務平臺,

3.系統實作

? 區塊鏈部分基于FISCO BCOS 開發,FISCO BCOS 是由國內企業主導研發、對外開源、安全可控的企業級金融聯盟鏈底層平臺,另外通過微信小程式作為媒介,客戶端由小程式和后臺管理網站通過https 請求,經過 nginx 進行負載均衡,后臺采用 django,將用戶的 access_token等存盤在redis 快取服務器中,進行定時重繪, 前端采用小程式的原生框架,采用 WXML + WXSS + JS 進行原生開發與布局,

3.1用戶管理模塊

? 該模塊包含用戶注冊登陸以及管理員對用戶信用積分的管理,登錄功能是可確權登錄的操作手段,以此實作用戶的操作安全性,確保用戶的賬 號資料安全為用戶本人操作,用戶在正確登錄小程式后,后臺會獲取登錄用戶的身份, 根據身份給予該用戶不同的權限進行操作,

3.1.1 合約代碼

**1.功能說明:**本合約實作功能主要為:1.用戶注冊2.用戶登錄3.查看用戶資訊

  • activateUser(string memory _userid,string memory _username,string memory _userpassword, string memory _usertype):用戶實作注冊,傳入用戶的ID號、名字、密碼,用戶身份類別,
  • Login(string memory _userid,string memory _userpassword)用戶的ID、用戶的密碼
  • getUserRecordArray(string userid)用戶的ID
pragma solidity ^0.4.25;
pragma experimental ABIEncoderV2;
import "../lib/SafeMath.sol";
import "../utils/TimeUtil.sol";
import "../utils/StringUtil.sol";
import "../utils/TypeConvertUtil.sol";
import "./TableDefTools.sol";
contract UserControl is TableDefTools{
    /*******   引入庫  *******/
     using TimeUtil for *;
     using SafeMath for *;
     using TypeConvertUtil for *;
     using StringUtil for *;
   /*
    * 建構式,初始化使用到的表結構
    *
    * @param    無
    *
    * @return   無
    */
    constructor() public{
        //初始化需要用到的表,用戶資訊表
        initTableStruct(t_user_struct, TABLE_USER_NAME, TABLE_USER_PRIMARYKEY, TABLE_USER_FIELDS);
    }
    // 事件
    event REGISTER_USER_EVENT(string userid,string usertype,string activatetime); //注冊用戶事件.記錄注冊人身份,型別,注冊時間
    event DEL_CREDITPOINT_EVENT(string user_id,string grade,string time); //扣分時間,扣分人id、扣分數、扣分時間
    event ADD_CREDITPOINT_EVENT(string user_id,string grade,string time);
   /*
    * 1.用戶注冊
    *
    * @param _userid  用戶id
    * @param _fields 用戶資訊表各欄位值拼接成的字串(除最后三個欄位;用逗號分隔,最后三個欄位分別是注冊時間【根據注冊時間生成】,注冊狀態
    注冊后這個值默認為1,信用積分默認為100分),包括如下:
    *                   用戶ID(主鍵)
    *                   用戶名   
    *                   登陸密碼
    *                   注冊時間
    *                   注冊狀態[1代表已注冊,0代表為注冊]
    *                   信用積分                 
    *
    * @return 執行狀態碼
    *
    * 測驗舉例  引數一:"191867345212322"  
    * 引數二:"江會文","123456","個人"
    *注冊成功回傳SUCCESS否則回傳錯誤碼,錯誤碼對應的問題請參考DB
    */
    function activateUser(string memory _userid,string memory _username,string memory _userpassword, string memory _usertype) public  returns(int8){
                 // 獲得當前的時間
        string memory _passwordhash=TypeConvertUtil.bytes32ToString(sha256(abi.encode(_userid,_userpassword)));
        string memory nowDate = TimeUtil.getNowDate();
        string memory firstFiveParams=StringUtil.strConcat7(_username,',',_passwordhash,',',nowDate,',',_usertype);
        string memory lastTwoParams = "1,100";
        string memory storeFields = StringUtil.strConcat3(firstFiveParams,',',lastTwoParams);
        emit REGISTER_USER_EVENT(StringUtil.strConcat2("注冊人的ID為:",_userid),StringUtil.strConcat2("注冊人身份為:",_usertype),StringUtil.strConcat2("注冊時間為:",nowDate)); 
        return insertOneRecord(t_user_struct,_userid,storeFields,false);//最后的false代表主鍵下記錄不可重復
    }
    /*
    * 2.用戶登陸
    *
    * @param userid  用戶id
    * @param  userpassword  用戶密碼
    * @return 用戶所有資訊并以JSON格式回傳
    *
    * 測驗舉例  引數一:"191867345212322,123456"
    */
    function Login(string memory _userid,string memory _userpassword) public view returns (int8,string) {
         string memory _passwordhash=TypeConvertUtil.bytes32ToString(sha256(abi.encode(_userid,_userpassword)));
         return loginInToJson(t_user_struct,_userid,_passwordhash);
    }
    /*
    * 3.查詢用戶資訊并以字串陣列方式輸出
    *
    * @param _userid  用戶id
    *
    * @return 執行狀態碼
    * @return 該用戶資訊的字串陣列
    *
    * 測驗舉例  引數一:"191867345212322"
    */
    function getUserRecordArray(string userid) public view returns(int8, string[]){
        return selectOneRecordToArray(t_user_struct, userid, ["user_id",userid]);
    }
}

2使用實體:

  • 用戶注冊:

通過呼叫activateUser傳入用戶的ID、姓名、密碼、身份類別完成注冊,

用戶注冊

  • 查詢用戶資訊:

    ? 因為區塊鏈上的資訊是公開透明的,因此不應當把密碼等隱私資料直接上鏈,而應當對隱私資料進行**“脫敏上鏈”**,在這里我們對用戶的密碼進行了hash處理,由于哈希函式具有單向性,因此即使該哈希值被他人讀取了也很難破解用戶的密碼,

    查詢用戶資訊

  • 用戶注冊日志記錄:

3.1.2 前端代碼

? 前端界面主要分為用戶注冊頁面和用戶登錄頁面.這里主要講一下注冊頁面.通過一個form表單讓用戶輸入關鍵的資訊,注冊時候,能自動獲取的資訊就不讓用戶手動再輸入一遍,增加用戶的使用體驗,自動獲取用戶的微信昵稱作為用戶的賬戶名,用戶需要手工輸入6-20位的密碼,再通過復選框cu-form-group選擇三類身份中的一種,最后點擊提交即可完成注冊,

    <view class="zan-dialog {{ showDialog ? 'zan-dialog--show' : '' }}">
      <view class="zan-dialog__mask" bindtap="toggleDialog" />
      <view class="zan-dialog__container">
        <view class="padding flex flex-direction">
          //用戶注冊,輸入需要的資訊
          <view class="text-center text-xl text-bold text-cyan margin-top">{{registeredStatus?'登陸后獲得發布和下載權限':'注冊后獲得發布和下載權限'}}</view>
          <view class="cu-form-group margin-top">
            <view class="title">用戶賬號</view>
            <input disabled="true" value="{{userNickName}}"></input>
          </view>
          <view class="cu-form-group">
            <view class="title">輸入密碼</view>
            <input placeholder="請輸入6~20位的密碼" password="true" maxlength="20" value="{{password}}" bindinput='getPassword'></input>
          </view>
          <view class="cu-form-group" wx:if="{{!(isLogin || registeredStatus)}}">
            <view class="title">確認密碼</view>
            <input placeholder="請重新輸入密碼" password="true" maxlength="20" value="{{passwordToCheck}}" bindinput='getPasswordToCheck'></input>
          </view>
					//用戶身份選擇可以用下拉框.分為三類個人、企業、政府
          <view class="cu-form-group" wx:if="{{!(isLogin || registeredStatus)}}">
          <view class="title">選擇用戶身份</view>
            <picker bindchange="changeUserType" value="{{userType}}" range="{{userTypeList}}">
              <view class="picker">
                {{userTypeList[userType]}}
              </view>
            </picker>
          </view>
          <button class="cu-btn bg-cyan margin-top lg" bindtap="checkPasswordAndPost" wx:if="{{!registeredStatus}}">提交</button>
          <button class="cu-btn bg-cyan margin-top lg" bindtap="login" wx:if="{{!isLogin&&registeredStatus}}">登陸</button>
        </view>
      </view>
    </view>

前端界面效果圖如下:

  • 注冊界面:

    注冊
  • 登陸界面:

3.1.3 后端代碼

? 后端根據用戶微信登錄后產生唯一的openId自動作為用戶的Id,作為用戶注冊的唯一主鍵,通過該主鍵獲取用戶的微信號、微信昵稱、密碼哈希、微信頭像、注冊狀態、信用積分、用戶身份類別、下載檔案記錄等資訊,

class User(models.Model):
    #自動生成openId作為用戶的唯一ID
    openId = models.CharField(db_index=True, max_length=100, default='0', null=True)  # 唯一表示微信用戶的id
    userWxName = models.CharField(verbose_name='用戶微信名', max_length=40, null=True)  # 用戶微信名
    userLoginName = models.CharField(verbose_name='用戶登錄名', max_length=10, null=True)
    userPassword = models.CharField(verbose_name='密碼通過sha256計算后的hash值', max_length=64, null=True)
    avatarUrl = models.URLField(max_length=255, null=True)  # 用戶頭像
    activateTime = models.DateTimeField(verbose_name='注冊時間', null=True)
    registeredStatus = models.BooleanField(verbose_name='注冊狀態', default=False)
    userCreditPoint = models.IntegerField(verbose_name='信用積分', default=100)#給定默認信用積分為100分
    userTypeChoices = [(0, '個人'), (1, '企業'), (2, '政府機構')] #通過用戶在前端界面選擇的序號確定身份類別
    userType = models.PositiveSmallIntegerField(verbose_name='用戶型別', choices=userTypeChoices, null=True, blank=True)
    downloadRecord = models.ManyToManyField("OfficeFile", blank=True, through='DownloadRecord',
                                            through_fields=('user', 'officeFile'), related_name='userDownloadRecord')
    #獲取用戶資訊
    def getUserInfo(self,request):
    obj = json.loads(request.body)
    user = get_user(obj)
    if not user:
        return HttpResponse('false')  # token過期
    else:
        if obj['funType'] == 0:
            user.userWxName = obj['name']
            user.avatarUrl = obj['avatarUrl']
            user.save()
        userTypeList = []
        choices = User.userTypeChoices
        for c in choices:
            userTypeList.append(c[1])
        data = {'userType': user.userType, 'userTypeList': userTypeList}
        if obj['funType'] == 1:
            data['avatarUrl'] = user.avatarUrl
            data['nickName'] = user.userWxName
        return JsonResponse(data, safe=False)

3.2 建言獻策模塊

? 每位用戶可在建言獻策界面的文本框內輸入建言標題和建言內容,確保內容無誤后點擊“提交建議”,建言資料將上傳到本地資料庫和區塊鏈上,后端呼叫 API 對用戶所發布的內容進行違規詞檢測替換,若內容有不文明用詞將扣除用戶 1 點信用值,且將建言標題內容和信用扣除記錄上傳到區塊鏈上(用戶違規事件記錄上鏈),每位用戶可在小程式首頁瀏覽所有用戶提交的建議,且每條 建議會顯示建議提交者 ID、提交時間以及是否違規,同時也可在個人中心的信用值記 錄查看自己提交的建言的詳細記錄

3.2.1 合約代碼

**1.功能說明:**本合約實作功能主要為:1.用戶留言 2.查看用戶留言記錄

  • suggest(string memory _proposeid,string memory _userid,string memory _title,string memory _content):用戶留言:傳入留言內容Id、用戶的ID號、留言標題、留言內容,
  • getSuggestRecordJson(string _proposeid):回傳留言內容:傳入留言內容ID號,以json形式回傳留言內容
pragma solidity ^0.4.25;
pragma experimental ABIEncoderV2;
import "../lib/SafeMath.sol";
import "../utils/TimeUtil.sol";
import "./TableDefTools.sol";
/*
*
* UserSuggest實作用戶留言功能,
* 首先查看用戶是否注冊,登陸,如果沒有則不能留言
* 操作的表為t_suggest.
*
*/
contract Suggest is TableDefTools{
    /*******   引入庫  *******/
     using TimeUtil for *;
     using SafeMath for *;
     event SUGGEST(string Proposeid,string  Userid,string _title,string SuggestContent,string time);//留言事件.留言人ID,留言標題,內容,時間
   /*
    * 建構式,初始化使用到的表結構
    *
    * @param    無
    *
    * @return   無
    */
    constructor() public{
        //初始化需要用到的表,建言獻策表
        initTableStruct(t_propose_struct, TABLE_PROPOSE_NAME, TABLE_PROPOSE_PRIMARYKEY, TABLE_PROPOSE_FIELDS);
    }
   /*
    * 1.用戶留言
    *
    * @param _proposeid      留言ID號  
    * @param _userid    留言人ID
    * @param _title    留言內容標題
    * @param _content   留言內容    
    *
    * @return 執行狀態碼
    *
   * 測驗舉例  引數一:"17846"  
    * 引數二:"191867345212322","關于提供退役軍人金融優惠措施的建議","尊敬的領導,本人通過網路知道了了去年事務部和十大銀行簽了優撫協議,其它省份落實的很好,希望江西省也能對接下江西銀行,江西農商銀行等"
    *注冊成功回傳SUCCESS,以及留言的句子,否則回傳錯誤碼,錯誤碼對應的問題請參考TableDefTools寫的
    */
   function suggest(string memory _proposeid,string memory _userid,string memory _title,string memory _content) public  returns(int8){
        //獲取時間
        string memory nowDate = TimeUtil.getNowDate();
        string memory suggestfields=StringUtil.strConcat4("建議標題為:",_title,"建議內容為:",_content);
        string memory storeFields = StringUtil.strConcat5(_userid,',',suggestfields,',',nowDate);
        emit SUGGEST(StringUtil.strConcat2("留言的ID號為:",_proposeid),StringUtil.strConcat2("留言人的ID為:",_userid),
        StringUtil.strConcat2("留言標題為:",_title),StringUtil.strConcat2("留言內容為:",_content),StringUtil.strConcat2("留言時間為:",nowDate)); 
        return (insertOneRecord(t_propose_struct,_proposeid,storeFields,false));
    }
    /*
    * 2.根據留言ID號查詢留言人ID號和留言內容并以Json字串方式輸出
    *
    * @param _proposeid  留言id
    *
    * @return 執行狀態碼 
    * @return 該用戶所有留言資訊的Json字串
    *
    * 測驗舉例  引數一:"17846"
    */
    function getSuggestRecordJson(string _proposeid) public view returns(int8, string){
        return selectOneRecordToJson(t_propose_struct, _proposeid);
    }

2使用實體:

  • 用戶留言:

    用戶留言

  • 用戶留言成功的回執資訊

  • 通過留言內容的Id號查看留言資訊

  • 用戶留言事件在區塊鏈端的日志記錄

  • 用戶違規發言的扣分記錄

3.2.2 前端代碼

  1. 用戶提交留言:用戶在表單中輸入留言標題和留言內容,通過placeholder給用戶提供輸入內容事例,防止用戶不知道該如何留言,表單設定容納最大大小為500字,防止內容過多,

    <!--pages/suggest/addSuggest/addSuggest.wxml-->
    <view class="cu-form-group">
      	<!--用戶輸入留言的標題和內容,后臺通過js請求生成留言Id號并獲取用戶Id-->
        <view class="title">建言標題</view>
        <input placeholder="如:審批流程問題建議" maxlength="50" value="{{suggestTitle}}" bindinput="getSuggestTitle"></input>
      </view>
      <view class="cu-form-group align-start" style="height: 500rpx;">
        <view class="title">建言內容</view>
        	<!--給出用戶示例輸入-->
        <textarea maxlength="-1" value="{{suggestContent}}" placeholder="請輸入政府辦公流程體制記憶體在的具體問題及有可能解決該問題的建議" bindinput="getSuggestContent" style="height: 450rpx;"></textarea>
      </view>
      <view class="btn-area padding-xl">
        <!--兩個按鈕.用戶確認無誤點擊提交,否則點擊重新填寫-->
        <button class="cu-btn block bg-cyan lg" style="width: 500rpx; height: 80rpx;" bindtap="postSuggest"><text
            class="cuIcon-upload" style="margin-right: 7rpx;"></text>提交建議</button>
        <button class="cu-btn block bg-grey margin-tb-sm lg" style="width: 500rpx; height: 80rpx;"
          bindtap="resetSuggest"><text class="cuIcon-refresh" style="margin-right: 7rpx;"></text>重新填寫</button>
      </view>
    
  2. 用戶留言內容展示:后端通過http請求將用戶留言的標題、內容、留言時間、上傳用戶的Id、留言內容是否合規等資訊展示到前端來,這里對用戶留言合規用綠色表示,如果不合格用紅色表示,起到醒目的作用,

<!--pages/suggest/suggestInfo/suggestInfo.wxml-->
<view class="cu-bar bg-white" style="margin: 0rpx 0 1rpx 0;">
  <view class="action">
    <text class="cuIcon-title text-green"></text>
    <text>建言標題</text>
  </view>
</view>
<view class="padding bg-white text-bold">{{title}}</view>
<view class="cu-bar bg-white" style="margin: 1rpx 0 1rpx 0;">
  <view class="action">
    <text class="cuIcon-title text-green"></text>
    <text>建言內容</text>
  </view>
</view>
<view class="padding bg-white" style="margin: 0rpx 0 2rpx 0;text-align: justify;">{{content}}</view>
<view class="cu-form-group">
  <view class="title">留言時間</view>
  <view>{{suggestTime}}</view>
</view>
<view class="cu-form-group">
  <view class="title">上傳用戶ID</view>
  <view class="text-right">{{userId}}</view>
</view>
<view class="cu-form-group">
  <view class="title">內容是否合規</view>
  <view class="text-{{isCompliance=='內容合規'?'green':'red'}}">{{isCompliance}}</view>
</view>
  • 用戶留言前端界面圖:

    用戶留言
  • 用戶查看自己的留言記錄圖:

  • 用戶瀏覽他人留言:

  • 用戶查看留言內容詳情:

3.2.3 后端代碼

后端對用戶留言的內容進行判斷,如果標題為慷訓者內容為空,則回傳error給用戶,提醒用戶輸入為空,對用戶留言的內容與從github搜集的違規詞語料庫進行匹配,若用戶的留言內容違規,那么將呼叫api對用戶的信用積分進行扣分處理,

def addSuggest(request):
    obj = json.loads(request.body)
    user = get_user(obj)
    if not user:
        return HttpResponse('false')  # token過期
    else:
        suggest = Suggest(user_id=user.id, suggestTitle=obj['suggestTitle'], suggestContent=obj['suggestContent'])
        suggest.save()
        addSuggestToChain(suggest.id, user.userLoginName, suggest.suggestTitle, suggest.suggestContent)
        content = getSuggestFromChain(suggest.id)[1]
        suggestFromChain = content[1]
        titleStartIndex = re.search('建議標題為:', suggestFromChain).span()[1]
        contentStartIndex = re.search('建議內容為:', suggestFromChain).span()[1]
        data1 = checkContent(suggestFromChain[titleStartIndex:contentStartIndex - 6])
        data2 = checkContent(suggestFromChain[contentStartIndex:])
        suggest.changeSuggestTitle = data1['text']
        suggest.changeSuggestContent = data2['text']
        if data1['num'] > 0 or data2['num'] > 0:
            suggest.isCompliance = False
            user.userCreditPoint = user.userCreditPoint - 1
            user.save()
        else:
            suggest.isCompliance = True
        suggest.save()
        if suggest.isCompliance is False:
            suggesterId = getSuggesterId(suggest.id)
            updateUserCredit(suggesterId, 1, 0)
    return HttpResponse("success")
  #判斷用戶的留言內容是否違規,從github上搜集違規詞存入keywords.txt檔案中,如果用戶留言內容在違規詞內則判定為違規
  def checkContent(content):
    gfw = DFAFilter()
    gfw.parse("keywords.txt")
    number = len(content.split("*"))-1
    text = gfw.filter(content, "*")
    data = {"num": len(text.split("*"))-1-number, "text": text}
    return data
  #獲取用戶留言細息,展示到前端頁面
  def returnSuggestList(request):
    obj = json.loads(request.body)
    pageSize = 10
    currentPage = obj['currentPage']
    startRow = (currentPage - 1) * pageSize
    endRow = currentPage * pageSize
    suggests = Suggest.objects.all().order_by('-id')[startRow:endRow]
    suggestList = []
    #遍歷所有的留言記錄,取出留言的Id號、留言標題、留言內容、是否違規、留言時間
    for s in suggests:
        obj = {"id": s.id, "userId": s.user.userLoginName, 'title': s.changeSuggestTitle,
               "content": s.changeSuggestContent,
               "isCompliance": s.isCompliance, "suggestTime": s.suggestTime.strftime("%Y.%m.%d")}
        suggestList.append(obj)
    return JsonResponse({"suggestList": suggestList})
  

3.3 巡檢模塊

? 該模塊模擬了在實際生活中,政府部門經常會有一些任務,要求在什么時候去哪些 地方巡查,也就涉及到用戶打卡,我們將用戶打卡記錄上鏈,避免了代打卡,甚至篡改資料庫等問題,同時領導可以通過下屬的用戶 ID查看他的打卡記錄資訊,

3.3.1 合約代碼

**1.功能說明:**本合約實作功能主要為:1.用戶打卡2.查看用戶打卡資訊記錄

  • ClockIn(string memory _userid,string memory _location,string memory _time)實作用戶打卡功能:傳入用戶Id號,打卡地點,打卡時間

  • getUserClockInfo(string _userid)查看用戶打卡記錄;傳入用戶Id

    pragma solidity ^0.4.25;
    import "../utils/TimeUtil.sol";
    import "../utils/StringUtil.sol";
    import "./TableDefTools.sol";
    pragma experimental ABIEncoderV2;
    contract Track is TableDefTools{
        /*******   引入庫  *******/
        using TimeUtil for *;
        using StringUtil for *;
    /*
    * Track實作用戶打卡巡檢合約
    * 主要包括:1.用戶打卡2.查看用戶打卡記錄回傳JSON陣列
    *
    */
        /*
        * 建構式,初始化使用到的表結構
        *
        * @param    無
        *
        * @return   無
        */
        constructor() public{
            //初始化需要用到的表,資料資源表
            initTableStruct(t_track_struct, TABLE_TRACK_NAME, TABLE_TRACK_PRIMARYKEY, TABLE_TRACK_FIELDS);
        }
          //定義事件日志資訊
       event TRACK_EVENT(string user_id,string user_location,string time);//用戶打卡記錄日志,誰在哪里什么時間打的卡
        /*
        * 1.用戶打卡
        *
        * @param _userid  用戶id
        * @param _location 地點名   
        * @param _time      打卡時間
        * @return 執行狀態碼
        *
        * 測驗舉例  引數一:"191867345212322"  
        * 引數二:"江西省人民政府","2020年1月19日15點58分29秒"
        *注冊成功回傳SUCCESS否則回傳錯誤碼,錯誤碼對應的問題請參考DB
        */
        function ClockIn(string memory _userid,string memory _location,string memory _time) public returns(int8){
            string memory storeFields=StringUtil.strConcat3(_location,',',_time);
            emit TRACK_EVENT(StringUtil.strConcat2("用戶ID:",_userid),StringUtil.strConcat2("打卡地點:",_location),StringUtil.strConcat2("到達時間:",_time));
            return insertOneRecord(t_track_struct,_userid,storeFields,true);
        }
        /*
        * 2.查詢用戶打卡記錄并以JSON方式輸出
        *
        * @param _userid  用戶id
        *
        * @return 執行狀態碼
        * @return 該用戶去過的地方的JSON陣列
        *
        * 測驗舉例  引數一:"191867345212322"
        */
        function getUserClockInfo(string _userid) public view returns(int8, string){
            return selectOneRecordToJson(t_track_struct,_userid);
        }
    }
    

    2 使用實體:

    • 用戶打卡:小程式自動獲取用戶的Id唯一主鍵、呼叫wx.getLocation(Object object)獲取用戶

    的打卡地點.

    用戶成功打卡完成后回傳資訊如下:

    • 通過輸入用戶的Id以json的形式回傳用戶的打卡資訊:

      查詢輸入:

    查詢成功回傳如下圖所示,查詢失敗回傳錯誤碼

用戶打卡資訊日志記錄:

3.3.2 前端代碼

? 用戶打卡界面以兩個按鈕為主,點擊授權獲取地址按鈕可以呼叫微信的獲取用戶地址的介面,得到當前所在位置的經緯度,查看無誤后,點擊打卡即可,

<!--用戶打卡的前端頁面-->
<view class="zan-dialog zan-dialog--show">
  <view class="zan-dialog__mask" />
  <view class="zan-dialog__container">
    <view class="padding flex flex-direction">
      <view class="text-center text-xl text-bold text-cyan margin-bottom">當前地址</view>

      <view class="text-center text-bold">{{localText}}</view>

      <button class="cu-btn bg-cyan margin-top lg" style="width: 500rpx; height: 80rpx;"
        bindtap="postAddressOnChain"><text class="cuIcon-check" style="margin-right: 7rpx;"></text>打卡</button>

      <button class="cu-btn bg-cyan margin-top lg" style="width: 500rpx; height: 80rpx;" open-type="openSetting" bindtap="locationAuthorization"><text class="cuIcon-unlock" style="margin-right: 7rpx;"></text>授權獲取地址</button>
    </view>
  </view>
</view>
  • 用戶打卡的前端界面圖如下:

  • 查詢用戶打卡資訊:

    查詢用戶打卡頁面在頁面上方設定了一個搜索框,用戶可以輸入查詢的用戶Id賬號,點擊搜索后呼叫合約介面,回傳用戶的打卡記錄,即用戶的打卡地點和打卡時間,

    <!--查詢用戶打卡資訊的前端頁面-->
    <view id="search">
    		<input id="input" placeholder="如:1611628666" placeholder-class="placeholderClass" value="{{searchValue}}" bindinput="getSearchValue"></input>
    		<view id="comfirm" bindtap="search">搜索</view>
    </view>
    <view class="cu-bar padding-top-sm" style="min-height: 80rpx; border-bottom: 2rpx solid #f1f1f1; margin-top: 100rpx;" wx:if="{{hadSearch}}">
      <view class="action">
        <text class="margin-left cuIcon-title text-cyan"></text>
        <text class="text-black text-bold">用戶打卡記錄</text>
      </view>
    </view>
    <view class="cu-card article" style="height: auto;" wx:for="{{trackList}}" wx:key='id' wx:if="{{hadSearch}}">
      <view class="cu-item shadow">
        <view class="title" style="line-height: 80rpx;">
          <view class="text-cut">{{item.fields.user_location}}</view>
        </view>
        <view class="content" style="height: auto;">
          <!-- <view class="desc"> -->
            <view class="cu-tag bg-white light sm round">{{item.fields.arrival_time}}</view>
          <!-- </view> -->
        </view>
      </view>
    </view>
    <view id="blankArea"
        style="width: 100%;position: absolute;top: 470rpx;display: flex;flex-direction: column;align-items: center;justify-content: center;"
        wx:if="{{trackList.length == 0 && hadSearch}}">
        <image id="blankImage" style="width: 114rpx;height: 100rpx;margin-bottom: 30rpx;" src="/images/blank.png"></image>
        <view id="blankText" style="font-size: 26rpx;color: #999999;text-align: center;white-space:pre-line;">該用戶無打卡記錄
        </view>
    </view>
    

3.3.3 后端代碼

? 后端呼叫wx.getLocation獲取用戶當前定位的經緯度.將該資訊以及用戶的Id通過合約api介面呼叫,存入區塊鏈上,若用戶未授權獲取定位點擊了打卡將會通過wx.showModel彈窗的形式給用戶提醒未能獲得您的定位,請點擊“授權獲得地址”按鈕以獲得定位

  //呼叫wx.getLocation獲取用戶當前定位的經緯度
  showLocal: function () {
    var that = this;
    wx.getLocation({
      type: 'gcj02',
      success: function (res) {
        let latitude = res.latitude//經度
        let longitude = res.longitude//緯度
        that.setData({
          latitude,
          longitude
        })
        that.getMapCity(latitude, longitude)
      }
    })
  },
      postAddressOnChain: function(){ 
    var that = this
    if(!that.data.canClockIn){
      wx.showModal({
        title: '溫馨提示',
        content: '未能獲得您的定位,請點擊“授權獲得地址”按鈕以獲得定位',
        showCancel: false,
        success (res) {
          if (res.confirm) {
            console.log('用戶點擊確定')
          } else if (res.cancel) {
            console.log('用戶點擊取消')
          }
        }
      })
      return
    } else {
      const app = getApp()
      //獲取時間
      var date = new Date()
      var year = date.getFullYear() + '年'
      var month = date.getMonth() + 1 + '月'
      var day = date.getDate() + '日'
      var hour = date.getHours() + '點'
      var minute = date.getMinutes() + '分'
      var second = date.getSeconds() + '秒'
      var dateText = year + month + day + hour + minute + second
      //將資訊上鏈
      var objToChain = {
        "groupId" :5,
        "signUserId": "fee97843cf0c45d683bade8fdebe724f",
        "contractAbi":[{"constant":true,"inputs":[{"name":"_userid","type":"string"}],"name":"getUserCityArray","outputs":[{"name":"","type":"int8"},{"name":"","type":"string[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_userid","type":"string"}],"name":"getUserCityJson","outputs":[{"name":"","type":"int8"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fields","type":"string[]"},{"name":"index","type":"uint256"},{"name":"values","type":"string"}],"name":"getChangeFieldsString","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_userid","type":"string"},{"name":"_location","type":"string"},{"name":"_time","type":"string"}],"name":"ClockIn","outputs":[{"name":"","type":"int8"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"user_id","type":"string"},{"indexed":false,"name":"user_location","type":"string"},{"indexed":false,"name":"time","type":"string"}],"name":"TRACK_EVENT","type":"event"}],
        "contractAddress":"0x8546174c5fe38243e1dcfb65e1347919fe0f45ba",
         "funcName":"ClockIn",
        "funcParam":[app.globalData.idNumber,that.data.localText,dateText],
        "useCns":false
      }
      //通過http請求與鏈上資料進行互動
      http.httpToChain(objToChain, function (res) {
        if(res.message == 'success' || res.message == 'Success'){
          wx.showToast({
            title: '打卡成功',
            icon: 'success',
            duration: 2000
          })
        }
      })
    }
  },

3.4 政務合作模塊

? 政府要發布某一條訊息往往不是某一個人決定的,而是多級領導審核后同意才會通過,在檔案傳輸程序中,如何保障資料不被篡改,區塊鏈就可以起到很大的作用,但是區塊鏈存盤資料對資源消耗特別大,因此我們決定對資料**“輕裝”上鏈**, 政府公文檔案的 pdf 放鏈下本地資料庫,檔案的哈希值上鏈,需要驗證的時候將 pdf 再做一次哈希運算與鏈上對比,一致則可以保證檔案的未被篡改,

3.4.1 合約代碼

**1.功能說明:**本合約實作功能主要為:1.科員提交材料2.查詢申請材料當前狀態 并以Json字串方式輸出3.領導簽字圖片資訊,審核結果資訊上鏈,4.將審核結果決定是否公示

  • applyForDocument(string memory _applicationid,string memory _userid,string memory _informationhash):傳入檔案的Id號、申請人Id、檔案的哈希值
  • getApplyJson(string memory _applicationid):傳入檔案的Id號,以Json形式回傳檔案資訊
  • checkApplyResearch(string memory _applicationid,string memory checkhash):檔案Id號、領導簽字圖片哈希值
  • GiveResultToUser(string _checkerid,string memory _applicationid,string memory checkresult):審核人Id號、材料Id、審核結果
pragma solidity ^0.4.25;
pragma experimental ABIEncoderV2;
import "../utils/TimeUtil.sol";
import "./TableDefTools.sol";
/*
* GovCooperation模擬政府辦公協作的流程
* 首先科員提交材料申請,等待上級審批是否通過,通過后公示給大眾
*
*/

contract GovCooperation is TableDefTools{
    /*******   引入庫  *******/
     using TimeUtil for *;
   /*
    * 建構式,初始化使用到的表結構
    *
    * @param    無
    *
    * @return   無
    */
    constructor() public{
        //初始化需要用到的表,檔案材料申請審核
        //公示政府檔案表
        initTableStruct(t_application_struct, TABLE_APPLICATION_NAME, TABLE_APPLICATION_PRIMARYKEY, TABLE_APPLICATION_FIELDS);
    }
    //
    event APPLY_DOCUMENT_EVENT(string userid,string application_id,string date);
    event Track_LeaderSignature(string applicationid,string signHash,string date);//記錄領導簽字
    event Tack_Final_Result(string applicationid,string checkerid,string result,string date);//記錄最后校驗部結果
   /*
    * 1.科員提交材料,具體材料放鏈下,鏈上存放申請材料哈希值,加密演算法采用的sha256目前最安全的一種演算法,暫時不可能逆推
    *
    * @param _applicationid  材料id唯一主鍵
    * @param _fields 用戶資訊表各欄位值拼接成的字串包括如下:
    *                   材料提交用戶ID
    *                   申請材料哈希值  
    *                   申請時間 【呼叫輪子自動生成】  
    *   【后三個欄位, 代表 領導簽字哈希值 , 審核結束時間,審核結果,最初默認值為“審批中,審批中,審批中”】后面修改        
    *
    * @return 執行狀態碼
    *
    * 測驗舉例  引數一:"1976945"  
    * 引數二:"1611408890,f44e39c1fc14dc05143eeba2065a921bbbc1bba5"
    *注冊成功回傳SUCCESS否則回傳錯誤碼,錯誤碼對應的問題請參考DB
    */
    function applyForDocument(string memory _applicationid,string memory _userid,string memory _informationhash) public returns(int8){
         // 獲得當前的日期
        string memory nowDate = TimeUtil.getNowDate();
        string memory firstTwoFields=StringUtil.strConcat5(_userid,',',_informationhash,',',nowDate);
        string memory lastFourParams = ",審核中,審核中,審核中";
        string memory storeFields = StringUtil.strConcat2(firstTwoFields,lastFourParams);
        emit APPLY_DOCUMENT_EVENT(StringUtil.strConcat2("申請人的ID號為:",_userid),StringUtil.strConcat2("材料的ID為:",_applicationid),StringUtil.strConcat2("申請時間為:",nowDate)); 
        return insertOneRecord(t_application_struct,_applicationid,storeFields,false);//最后的false代表主鍵下記錄不可重復
    }
    /*
    * 2.查詢申請材料當前狀態 并以Json字串方式輸出
    *
    * @param _applicationid  材料id唯一主鍵
    *
    * @return 執行狀態碼
    * @return 該用戶資訊的字串陣列
    *
    * 測驗舉例  引數一:"1976945"
    */
      function getApplyJson(string memory _applicationid) public view returns(int8, string){
        return selectOneRecordToJson(t_application_struct,_applicationid);
    }
    /*
    * 3.領導簽字圖片資訊,審核結果資訊上鏈,具體材料放鏈下,鏈上存放申請材料哈希值,加密演算法采用的sha256目前最安全的一種演算法,暫時不可能逆推
    * 具體操作為修改申請表的check_hash欄位
    * @param _applicationid  材料id唯一主鍵
    * @param checkhash 領導簽字圖片哈希值     
    *
    * @return 執行狀態碼
    *
    * 測驗舉例  引數一:"1976945"  
    * 引數二:"f88r46f6ki14dc05143eeba2065a921bbbc1bbqi5"
    *注冊成功回傳SUCCESS否則回傳錯誤碼,錯誤碼對應的問題請參考DB
    */
    function checkApplyResearch(string memory _applicationid,string memory checkhash) public returns(int8){
        // 獲得當前的日期
        string memory nowDate = TimeUtil.getNowDate();
          //查詢用戶申請資訊回傳狀態
        int8 queryRetCode; 
        //更新用戶申請保送表后回傳狀態
        int8 updateRetCode;
        // 資料表回傳資訊
        string[] memory retArray;
         // 查看該用戶申請審核資訊
        (queryRetCode, retArray) = selectOneRecordToArray(t_application_struct, _applicationid, ["application_id", _applicationid]);
          // 若存在該用戶記錄
        if(queryRetCode == SUCCESS_RETURN){
             string memory changedFieldsStr = getChangeFieldsString(retArray,3,checkhash);
             updateRetCode = (updateOneRecord(t_application_struct,_applicationid,changedFieldsStr));
            if(updateRetCode == SUCCESS_RETURN){
                //記錄日志
              emit Track_LeaderSignature(StringUtil.strConcat2("材料Id為:",_applicationid),
              StringUtil.strConcat2("領導簽字圖片的哈希為:",checkhash),StringUtil.strConcat2("申請時間為:",nowDate)); 
               return SUCCESS_RETURN;
            }
            else{
                return FAIL_RETURN;
            }
        }else{
            return FAIL_RETURN;
        }
    }
    /*
    * 4.校驗部驗證完以后,把是否公開結果上鏈,
    * @param _checkerid 校驗人員id
    * @param _applicationid  材料id唯一主鍵
    * @param checkresult 該檔案是否通過審批,公示給大眾,通過/不通過:      
    *
    * @return 執行狀態碼
    *
    * 測驗舉例  引數一:"186789,1976945,"通過""  

    *注冊成功回傳SUCCESS否則回傳錯誤碼,錯誤碼對應的問題請參考DB
    */
    function GiveResultToUser(string _checkerid,string memory _applicationid,string memory checkresult) public returns(int8){
        //查詢檔案資訊回傳狀態
        int8 queryRetCode; 
        //更新檔案后回傳狀態
        int8 updateRetCode;
        // 資料表回傳資訊
        string[] memory retArray;
        // 獲得當前的日期
        string memory nowDate = TimeUtil.getNowDate();
        // 查看該科員申請審核資訊
        (queryRetCode, retArray) = selectOneRecordToArray(t_application_struct, _applicationid, ["application_id", _applicationid]);
         // 若存在該用戶記錄
        if(queryRetCode == SUCCESS_RETURN){
            //修改科員申請表中的審核時間
             string memory changedFieldsStr = getChangeFieldsString(retArray, 5, nowDate);
            //修改
            updateRetCode = (updateOneRecord(t_application_struct, _applicationid,changedFieldsStr));
            if(updateRetCode == SUCCESS_RETURN){
            string memory changedFieldsStr2 = getChangeFieldsString(retArray, 4, checkresult);
            emit Tack_Final_Result(StringUtil.strConcat2("審核人Id為:",_checkerid),StringUtil.strConcat2("審核材料id為:",_applicationid),
            StringUtil.strConcat2("審核結果為:",checkresult),StringUtil.strConcat2("審核完成時間為:",nowDate)); 
            return (updateOneRecord(t_application_struct,_applicationid,changedFieldsStr2)); 
            }
        }
        else{
            // 若不存在該科員提交記錄
            return FAIL_RETURN;
        }
    }
}

2 使用實體:

  • 科員上傳檔案

  • 輸入檔案id號查詢檔案狀態

  • 領導簽字哈希上鏈:

?

成功回執資訊:

  • 審核員審批.校驗檔案沒被篡改、且領導意見為通過、領導簽名未被篡改后,提交最終審批結果如下:

    交易回執:

  • 科員提交檔案日志資訊:

  • 領導審批日志資訊:

  • 審核結果日志資訊:

3.4.2 前端代碼

? 前端以一個表單的形式讓用戶手工輸入檔案的名稱和內容,另外選擇檔案(支持doc和pdf等格式),選擇完后點擊上傳即可,領導審核的時候,將下載下來該檔案,并計算檔案的哈希值與區塊鏈上存盤的檔案哈希值作比對,一致則說明檔案沒發生篡改,才可以進行后面的步驟,否則將會報錯,

<!--pages/resourceView/resourceInfo/resourceInfo.wxml-->
<!--填寫檔案資訊-->
<view class="cu-bar bg-white padding-top-sm" style="min-height: 80rpx; border-bottom: 2rpx solid #f1f1f1;">
  <view class="action">
    <text class="cuIcon-title text-cyan"></text>
    <text class="text-black text-bold">檔案名稱</text>
  </view>
</view>
<view class="cu-bar bg-white" style="min-height: 80rpx; border-bottom: 2rpx solid #f1f1f1;">
  <view class="action" style=" margin:0 67rpx 0 67rpx;">
    <text>{{title}}</text>
  </view>
</view>
<view class="cu-bar bg-white" style="min-height: 80rpx; border-bottom: 2rpx solid #f1f1f1;">
  <view class="action">
    <text class="cuIcon-title text-cyan"></text>
    <text class="text-black text-bold">檔案介紹</text>
  </view>
</view>
<view class="cu-bar bg-white" style="min-height: 20rpx; border-bottom: 2rpx solid #f1f1f1;">
  <view class="action padding text-content" style=" margin:0 37rpx 0 37rpx;">
    <text>{{introduction}}</text>
  </view>
</view>
<view class="cu-bar bg-white" style="min-height: 80rpx; border-bottom: 2rpx solid #f1f1f1;">
  <view class="action">
    <text class="cuIcon-title text-cyan"></text>
    <text class="text-black text-bold">上傳時間:</text><text>{{uploadTime}}</text>
  </view>
</view>
<view class="cu-bar bg-white" style="min-height: 80rpx; border-bottom: 2rpx solid #f1f1f1;">
  <view class="action">
    <text class="cuIcon-title text-cyan"></text>
    <text class="text-black text-bold">資料提供者ID:</text><text>{{userId}}</text>
  </view>
</view>
<view class="cu-bar bg-white" style="min-height: 80rpx; border-bottom: 2rpx solid #f1f1f1;">
  <view class="action">
    <text class="cuIcon-title text-cyan"></text>
    <text class="text-black text-bold">資料總下載次數:</text><text>{{downloadNum}}</text>
  </view>
</view>
<!--通過把檔案的哈希值與該檔案的id對應的區塊鏈端的哈希值做對比保障檔案未被篡改-->
<view class="text-center margin-top text-gray">校驗可保證你下載的檔案并未被他人修改</view>

<view class="btn-area padding-xl">
  <button class="cu-btn block {{buttonBg}} lg" style="width: 550rpx; height: 80rpx;" bindtap="downloadDataAndCheck"
    disabled="{{loading}}"><text class="{{buttonIcon}}" style="margin-right: 7rpx;"></text>{{buttonText}}</button>
</view>
  • 科員提交檔案界面圖:

  • 科員查看檔案審核進度:

  • 科長簽字審核:

  • 處長審核:

  • 檔案公示:

3.4.3 后端代碼

? 后端代碼分為這幾個功能1.查看檔案的狀態;2.將領導審核的意見存盤上鏈3.將領導審核的簽名哈希上鏈;通過這些功能來保障資訊是沒有被篡改過的,做到協同辦公,最后將檔案審核結果公示給用戶,

# 查看檔案當前狀態
def returnApplyStatus(request):
    obj = json.loads(request.body)
    user = get_user(obj)
    if not user:
        return HttpResponse('false')  # token過期
    else:
      	#通過檔案的id查詢檔案的當前狀態
        a = OfficeFile.objects.get(id=obj['fileId'])
        data = {'uploadTime': a.uploadTime.strftime("%Y.%m.%d"),
                "informationLink": a.informationLink, "informationHash": a.informationHash,
                "boss1Opinion": a.boss1Opinion, "boss1SignLink": a.boss1SignLink, "boss1SignHash": a.boss1SignHash,
                "boss2Opinion": a.boss2Opinion, "boss2SignLink": a.boss2SignLink, "boss2SignHash": a.boss2SignHash,
                "checkHash": a.checkHash, "researchResult": a.researchResult, 'reviewResult': a.reviewResult
                }
        #以json形式回傳結果
        return JsonResponse(data)

#領導審核
def postApplicationListToAdminToSign(request):  # 領導簽字串列推送
    obj = json.loads(request.body)
    user = get_user(obj)
    if not user:
        return HttpResponse('false')  # token過期
    else:
        whichBoss = obj['identity']
        departmentId = obj['departmentId']
        pageSize = 10
        currentPage = obj['currentPage']
        startRow = (currentPage - 1) * pageSize
        endRow = currentPage * pageSize
        if whichBoss == 1:
            officeFiles = OfficeFile.objects.filter(workingStatus=False, boss1Opinion=None,
                                                    officeMember__department__id=departmentId).order_by('-id')[
                          startRow:endRow]
        else:
            officeFiles = OfficeFile.objects.filter(workingStatus=False, boss2Opinion=None,
                                                    officeMember__department__id=departmentId).exclude(
                boss1Opinion=None).order_by('-id')[startRow:endRow]
        officeFileList = []
        for o in officeFiles:
            data = {"fileId": o.id, 'uploadTime': o.uploadTime.strftime("%Y.%m.%d"),
                    "informationLink": o.informationLink, "informationHash": o.informationHash, 'title': o.dataTitle,
                    "introduction": o.dataIntroduction, 'userId': o.officeMember.user.userLoginName}
            officeFileList.append(data)
        return JsonResponse({"officeFileList": officeFileList})
#將領導簽字圖片的哈希值上鏈
def bossSign(request):
    obj = json.loads(request.body)
    user = get_user(obj)
    if not user:
        return HttpResponse('false')  # token過期
    else:
        whichBoss = obj['whichBoss']
        applicationForm = OfficeFile.objects.get(id=obj['id'])
        if whichBoss == 1:
            applicationForm.boss1Opinion = obj['boss1Opinion']
            applicationForm.boss1SignLink = obj['boss1SignLink']
            applicationForm.boss1SignHash = obj['boss1SignHash']
            if obj['boss1Opinion'] is False:
                applicationForm.researchResult = False
                applicationForm.workingStatus = False
        else:
            applicationForm.boss2Opinion = obj['boss2Opinion']
            applicationForm.boss2SignLink = obj['boss2SignLink']
            applicationForm.boss2SignHash = obj['boss2SignHash']
            if obj['boss2Opinion'] is False:
                applicationForm.researchResult = False
                applicationForm.workingStatus = False
        if applicationForm.boss1Opinion is not None and applicationForm.boss2Opinion is not None:
            s = hashlib.sha256()  # Get the hash algorithm.
            s.update((applicationForm.boss1SignHash + applicationForm.boss2SignHash).encode("utf8"))  # Hash the data.
            applicationForm.checkHash = s.hexdigest()  # Get he hash value.
            bossSignOnChain(obj['id'], s.hexdigest())
        applicationForm.save()
        return HttpResponse("success")
class OfficeFile(models.Model):
  	#
    dataTitle = models.CharField('檔案標題', max_length=50)
    dataIntroduction = models.CharField('檔案簡介', max_length=255)
    informationLink = models.CharField('檔案鏈接', max_length=255)
    informationHash = models.CharField('檔案哈希值', max_length=64)
    uploadTime = models.DateTimeField(verbose_name='上傳時間', auto_now_add=True)
    downloadsNum = models.IntegerField('下載次數', default=0)
    boss1SignLink = models.CharField('簽字圖片1下載鏈接', max_length=255, null=True)
    boss1SignHash = models.CharField('簽字圖片1哈希值', max_length=64, null=True)
    boss1Opinion = models.BooleanField('領導1意見', null=True)
    boss2SignLink = models.CharField('簽字圖片2下載鏈接', max_length=255, null=True)
    boss2SignHash = models.CharField('簽字圖片2哈希值', max_length=64, null=True)
    boss2Opinion = models.BooleanField('領導2意見', null=True)
    checkHash = models.CharField(verbose_name='審核結果哈希值', null=True, max_length=64)
    reviewResult = models.BooleanField(verbose_name='審核結果', null=True)
    researchResult = models.BooleanField(verbose_name='最終結果', null=True)  # 最終意見opinion
    workingStatus = models.BooleanField(verbose_name='作業已經完成', default=False, null=True)
    class Meta:
        verbose_name_plural = "辦公流程"
    def __str__(self):
        return self.dataTitle

4.后記

  • 該專案已獲得2020-2021騰訊舉辦的高校微信小程式比賽華中賽區三等獎
  • 所有相關代碼已經開源,運行有任何問題可以提issue,如專案對您有幫助,歡迎star支持!github地址點擊鏈接
  • 本人關注前沿知識,熱衷于開源,獲得Fisco Bcos 2021年度貢獻MVP
  • 目前在準備找Golong后端開發/區塊鏈開發相關實習,有一起的小伙伴可以滴滴.

轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/396281.html

標籤:區塊鏈

上一篇:GameFi賽道之一款基于區塊鏈的免費3D RPG 游戲Voxies(VOXEL)

下一篇:4EVERLAND 全球大使計劃,成為大使獲得豐富獎勵

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • JAVA使用 web3j 進行token轉賬

    最近新學習了下區塊鏈這方面的知識,所學不多,給大家分享下。 # 1. 關于web3j web3j是一個高度模塊化,反應性,型別安全的Java和Android庫,用于與智能合約配合并與以太坊網路上的客戶端(節點)集成。 # 2. 準備作業 jdk版本1.8 引入maven <dependency> < ......

    uj5u.com 2020-09-10 03:03:06 more
  • 以太坊智能合約開發框架Truffle

    前言 部署智能合約有多種方式,命令列的瀏覽器的渠道都有,但往往跟我們程式員的風格不太相符,因為我們習慣了在IDE里寫了代碼然后打包運行看效果。 雖然現在IDE中已經存在了Solidity插件,可以撰寫智能合約,但是部署智能合約卻要另走他路,沒辦法進行一個快捷的部署與測驗。 如果團隊管理的區塊節點多、 ......

    uj5u.com 2020-09-10 03:03:12 more
  • 谷歌二次驗證碼成為區塊鏈專用安全碼,你怎么看?

    前言 谷歌身份驗證器,前些年大家都比較陌生,但隨著國內互聯網安全的加強,它越來越多地出現在大家的視野中。 比較廣泛接觸的人群是國際3A游戲愛好者,游戲盜號現象嚴重+國外賬號安全應用廣泛,這類游戲一般都會要求用戶系結名為“兩步驗證”、“雙重驗證”等,平臺一般都推薦用谷歌身份驗證器。 后來區塊鏈業務風靡 ......

    uj5u.com 2020-09-10 03:03:17 more
  • 密碼學DAY1

    目錄 ##1.1 密碼學基本概念 密碼在我們的生活中有著重要的作用,那么密碼究竟來自何方,為何會產生呢? 密碼學是網路安全、資訊安全、區塊鏈等產品的基礎,常見的非對稱加密、對稱加密、散列函式等,都屬于密碼學范疇。 密碼學有數千年的歷史,從最開始的替換法到如今的非對稱加密演算法,經歷了古典密碼學,近代密 ......

    uj5u.com 2020-09-10 03:03:50 more
  • 密碼學DAY1_02

    目錄 ##1.1 ASCII編碼 ASCII(American Standard Code for Information Interchange,美國資訊交換標準代碼)是基于拉丁字母的一套電腦編碼系統,主要用于顯示現代英語和其他西歐語言。它是現今最通用的單位元組編碼系統,并等同于國際標準ISO/IE ......

    uj5u.com 2020-09-10 03:04:50 more
  • 密碼學DAY2

    ##1.1 加密模式 加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html ECB ECB : Electronic codebook, 電子密碼本. 需要加密的訊息按照塊密碼的塊大小被分為數個塊,并對每個塊進 ......

    uj5u.com 2020-09-10 03:05:42 more
  • NTP時鐘服務器的特點(京準電子)

    NTP時鐘服務器的特點(京準電子) NTP時鐘服務器的特點(京準電子) 京準電子官V——ahjzsz 首先對時間同步進行了背景介紹,然后討論了不同的時間同步網路技術,最后指出了建立全球或區域時間同步網存在的問題。 一、概 述 在通信領域,“同步”概念是指頻率的同步,即網路各個節點的時鐘頻率和相位同步 ......

    uj5u.com 2020-09-10 03:05:47 more
  • 標準化考場時鐘同步系統推進智能化校園建設

    標準化考場時鐘同步系統推進智能化校園建設 標準化考場時鐘同步系統推進智能化校園建設 安徽京準電子科技官微——ahjzsz 一、背景概述隨著教育事業的快速發展,學校建設如雨后春筍,隨之而來的學校教育、管理、安全方面的問題成了學校管理人員面臨的最大的挑戰,這些問題同時也是學生家長所擔心的。為了讓學生有更 ......

    uj5u.com 2020-09-10 03:05:51 more
  • 位元幣入門

    引言 位元幣基本結構 位元幣基礎知識 1)哈希演算法 2)非對稱加密技術 3)數字簽名 4)MerkleTree 5)哪有位元幣,有的是UTXO 6)位元幣挖礦與共識 7)區塊驗證(共識) 總結 引言 上一篇我們已經知道了什么是區塊鏈,此篇說一下區塊鏈的第一個應用——位元幣。其實先有位元幣,后有的區塊 ......

    uj5u.com 2020-09-10 03:06:15 more
  • 北斗對時服務器(北斗對時設備)電力系統應用

    北斗對時服務器(北斗對時設備)電力系統應用 北斗對時服務器(北斗對時設備)電力系統應用 京準電子科技官微(ahjzsz) 中國北斗衛星導航系統(英文名稱:BeiDou Navigation Satellite System,簡稱BDS),因為是目前世界范圍內唯一可以大面積提供免費定位服務的系統,所以 ......

    uj5u.com 2020-09-10 03:06:20 more
最新发布
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:46:47 more
  • Hyperledger Fabric 使用 CouchDB 和復雜智能合約開發

    在上個實驗中,我們已經實作了簡單智能合約實作及客戶端開發,但該實驗中智能合約只有基礎的增刪改查功能,且其中的資料管理功能與傳統 MySQL 比相差甚遠。本文將在前面實驗的基礎上,將 Hyperledger Fabric 的默認資料庫支持 LevelDB 改為 CouchDB 模式,以實作更復雜的資料... ......

    uj5u.com 2023-04-16 07:28:31 more
  • .NET Core 波場鏈離線簽名、廣播交易(發送 TRX和USDT)筆記

    Get Started NuGet You can run the following command to install the Tron.Wallet.Net in your project. PM> Install-Package Tron.Wallet.Net 配置 public reco ......

    uj5u.com 2023-04-14 08:08:00 more
  • DKP 黑客分析——不正確的代幣對比率計算

    概述: 2023 年 2 月 8 日,針對 DKP 協議的閃電貸攻擊導致該協議的用戶損失了 8 萬美元,因為 execute() 函式取決于 USDT-DKP 對中兩種代幣的余額比率。 智能合約黑客概述: 攻擊者的交易:0x0c850f,0x2d31 攻擊者地址:0xF38 利用合同:0xf34ad ......

    uj5u.com 2023-04-07 07:46:09 more
  • Defi開發簡介

    Defi開發簡介 介紹 Defi是去中心化金融的縮寫, 是一項旨在利用區塊鏈技術和智能合約創建更加開放,可訪問和透明的金融體系的運動. 這與傳統金融形成鮮明對比,傳統金融通常由少數大型銀行和金融機構控制 在Defi的世界里,用戶可以直接從他們的電腦或移動設備上訪問廣泛的金融服務,而不需要像銀行或者信 ......

    uj5u.com 2023-04-05 08:01:34 more
  • solidity簡單的ERC20代幣實作

    // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "hardhat/console.sol"; //ERC20 同質化代幣,每個代幣的本質或性質都是相同 //ETH 是原生代幣,它不是ERC20代幣, ......

    uj5u.com 2023-03-21 07:56:29 more
  • solidity 參考型別修飾符memory、calldata與storage 常量修飾符C

    在solidity語言中 參考型別修飾符(參考型別為存盤空間不固定的數值型別) memory、calldata與storage,它們只能修飾參考型別變數,比如字串、陣列、位元組等... memory 適用于方法傳參、返參或在方法體內使用,使用完就會清除掉,釋放記憶體 calldata 僅適用于方法傳參 ......

    uj5u.com 2023-03-08 07:57:54 more
  • solidity注解標簽

    在solidity語言中 注釋符為// 注解符為/* 內容*/ 或者 是 ///內容 注解中含有這幾個標簽給予我們使用 @title 一個應該描述合約/介面的標題 contract, library, interface @author 作者的名字 contract, library, interf ......

    uj5u.com 2023-03-08 07:57:49 more
  • 評價指標:相似度、GAS消耗

    【代碼注釋自動生成方法綜述】 這些評測指標主要來自機器翻譯和文本總結等研究領域,可以評估候選文本(即基于代碼注釋自動方法而生成)和參考文本(即基于手工方式而生成)的相似度. BLEU指標^[^?88^^?^]^:其全稱是bilingual evaluation understudy.該指標是最早用于 ......

    uj5u.com 2023-02-23 07:27:39 more
  • 基于NOSTR協議的“公有制”版本的Twitter,去中心化社交軟體Damus

    最近,一個幽靈,Web3的幽靈,在網路游蕩,它叫Damus,這玩意詮釋了什么叫做病毒式營銷,滑稽的是,一個Web3產品卻在Web2的產品鏈上瘋狂傳銷,各方大佬紛紛為其背書,到底發生了什么?Damus的葫蘆里,賣的是什么藥? 注冊和簡單實用 很少有什么產品在用戶注冊環節會有什么噱頭,但Damus確實出 ......

    uj5u.com 2023-02-05 06:48:39 more