Skip to content

Latest commit

 

History

History
558 lines (539 loc) · 22.6 KB

教程.md

File metadata and controls

558 lines (539 loc) · 22.6 KB

一、开发环境搭建

1、安装python

请在python官网下载根据机器选择下载python2.7.14。参考链接https://www.python.org/downloads/release/python-2714/。x86 64为机器下载地址(https://www.python.org/ftp/python/2.7.14/python-2.7.14.amd64.msi) 请将python目录安装在当前用户拥有可写目录下。windows系统用户建议装在除C盘外的其他盘。 将python的目录以及脚本目录添加至环境变量中。 例如:将python安装在E:\Python27 则将当前目录E:\Python27与脚本目录E:\Python27\Scripts添加至环境变量中。

2、安装ulord平台层SDK

git clone https://github.com/UlordChain/py-ulord-api.git
cd py-ulord-api
python setup.py install

二、SDK调用

1、角色确定

根据个人开发能力确定角色。SDK分为初级开发用户和中级开发用户两种。初级开发用户SDK帮助其创建了基本的数据库,涵盖用户表,资源表,标签表。开发者可以通过修改配置去修改数据库连接。由于使用flask-sqlalchemy所以支持大部分关系型数据库,包括mysql,sqlite,oracle,postgresql等等。默认使用sqlite。而中级开发者没有数据库创建的操作,他所拥有的功能仅仅是对平台层资源上链所需的http接口封装以及资源上传至UDFS的接口封装。 以初级开发者开发博客为例:

导入初级开发者类

from ulordapi.user import Junior

创建一个初级发开者实例,appkey与secret都是需要你在ulord平台注册用户并创建应用程序所生成的。如果你是ulord平台用户并且创建了一个应用,平台会返回给你一个appkey与secret。这是在ulord平台开发所必不可少的东西。

junior = Junior(appkey="8326648868ad11e8b894fa163e37b4c3", secret="8326648968ad11e8b894fa163e37b4c3")

2、配置修改

SDK支持配置文件直接修改以及通过接口进行修改。配置文件会在初次运行时在 以python接口修改为例,在创建初级开发者实例之后,可以根据自己的需要将配置写成一个字典传入config_edit方法中。 代码示例:

blog_config = {
    'baseconfig':{
        'config_file':'E:\ulord\ulord-blog-demo\config'
    },
    'logconfig':{
        'log_file_path': "E:\ulord\ulord-blog-demo\junior.log"
    }
}
junior.config_edit(blog_config)

3、创建数据库

在当前目录下创建数据库,初级开发者提供创建数据库接口,参数为数据库位置。 代码示例:

dbpath = os.getcwd()
junior.create_database(dbpath)

4、用户验证

用户验证采用token 的形式,用户登录注册都会生成一个有生效时限的token。用户在登出时会重新刷新token的是生效时限。所有接口都需要在head中传入token信息。 代码示例:

def auth_login_required():
    head_token = request.headers.get('token')
    if not head_token:
        return {
            'errcode':60103,
            'reason': "需要token"
        }
    login_user = User.query.filter_by(token=head_token).first()
    if not login_user:
        return return_result(60104)
    if int(login_user.timestamp) < time.time():
        return return_result(60104)
    return login_user

5、创建对应web接口

根据业务需求创建所需的web接口。博客案例使用flask模块开发。你也可以使用其他web开发框架进行开发。

5.1、获取公钥

信息加密采用RSA加密方式,后台SDK会在初次调用时生成一对密钥对,前端发送数据时先获取公钥对数据进行加密,然后输出加密信息,后台收到后用私钥进行解密。 注意1:相同数据用相同公钥加密会得到不同结果,用对应的私钥均可解密。 注意2:SDK加密需要加密之后通过base64编码进行编码。前端RSA加密工具jsencrypt已在内部实现编码,所以不要再进行编码。

形成一个get接口获取公钥,一个post接口验证加密信息。前端通过get接口回去公钥,用公钥加密后通过post接口验证是否加密成功。 代码示例:

@app.route('/user/password', methods=['GET', 'POST'])
def get_pubkey():
    log.info("start get password")
    if request.method == 'GET':
        return jsonify(return_result(0, result={"pubkey":junior.rsahelper.pubkeybytes}))
    elif request.method == 'POST':
        message = request.json.get("password")
        return jsonify(return_result(0, result={'password': junior.rsahelper._encry(message)}))

5.2、加密数据

实现该web接口便于开发阶段的测试。 代码示例:

@app.route('/user/encrypt',methods=['POST'])
def encrypt():
    messages = request.json.get("messages")
    result = {}
    for message in messages:
        message = message.encode('utf-8')
        result.update({
            message:junior.rsahelper._encry(message)
        })
    return jsonify(result)

5.3、用户注册

通过调用SDK的注册功能完成。初级开发者调用user_regist接口,中级开发者调用regist接口完成用户注册。 代码示例:

@app.route('/user/regist',methods=['POST'])
def regist():
    username = request.json.get('username')
    password = request.json.get('password')
    cellphone = request.json.get('cellphone')
    email = request.json.get('email')
    if username is None or password is None:
        return jsonify(return_result(60100))
    args = junior.decrypt([username, password, cellphone, email])
    if args:
        result = junior.user_regist(username=args[0],password=args[1],cellphone=args[2],email=args[3])
        return jsonify(result)
    else:
        return jsonify(return_result(60100))

5.4、优惠活动

新注册的用户通过开发者赠送其一定量的ulord。该接口调用成功前提为开发者拥有一定数量的ulord。为每位新注册的用户赠送指定数量的ulord(默认为10,可以修改配置文件中的webconfig的amount,同时activity字段控制是否开启赠送活动。)初级开发者调用user_activity接口实现,中级开发者调用paytouser接口实现。 代码示例:

@app.route('/user/activity', methods=['GET'])
def activity():
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    return jsonify(junior.user_activity(current_user.token))

5.5、用户登录

用户登录验证用户名密码,成功之后刷新token的有效时限。初级开发者调用user_login接口,中级开发者无此接口。 代码示例:

@app.route('/user/login',methods=['POST'])
def login():
    username = request.json.get('username')
    password = request.json.get('password')
    if username is None or password is None:
        return jsonify({
            'errcode': 60100,
            'reason': "缺少参数"
        })
    username = junior.decrypt(username)
    return jsonify(junior.user_login(username=username, password=password,encrypted=True))

5.6、用户登出

用户登出将token的过期时间设置为过去的一秒。即失效。初级开发者调用user_logout接口,中级开发者无此接口。 代码示例:

@app.route('/user/logout',methods=['POST','GET'])
def logout():
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    return jsonify(junior.user_logout(current_user.token))

5.7、博客发布

用户将博客发布到链上,内容将上传至UDFS上,获得一个UDFS的哈希值,ulord链存储博客的简介以及费用说明等相关信息。初级开发者调用udfs_upload接口resource_publish接口,中级开发者调用udfs_upload接口与publish接口。udfs_uplord负责将内容上传至UDFS接口,获得的哈希值组成发布字段传入发布接口中。 示例代码如下:

@app.route('/blog/publish',methods=['POST'])
def blog_publish():
    current_user = auth_login_required() # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    title = request.json.get('title')
    body = request.json.get('body')
    amount = request.json.get('amount')
    tags = request.json.get('tag')
    description = request.json.get('description')
    body_hash = junior.udfs_upload([body])
    if body_hash and body_hash.get(body):
        return jsonify(junior.resource_publish(title=title, udfshash=body_hash.get(body),amount=amount,tags=tags,des=description,
                                           usercondition={'usertoken':current_user.token}))
    else:
        return jsonify(return_result(errcode=60400))

5.8、博客更新

用户将博客信息进行更新,如果更新内容则需要重新将新内容上传至UDFS平台获取哈希值再更新链上字段值。所需参数为用户密码与博客在平台数据库中的ID值,以及所需更新的值。初级开发者使用resource_update接口,中级开发者直接调用update接口。 代码示例:

def blog_update():
    current_user = auth_login_required() # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    id = request.json.get('id')
    if id:
        try:
            id = str(id)
        except:
            return return_result(60100)
    pay_password = request.json.get('password')
    if not id and not pay_password:
        return  jsonify(return_result(60100))
    title = request.json.get('title')
    body = request.json.get('body')
    amount = request.json.get('amount')
    tags = request.json.get('tag')
    description = request.json.get('description')

    return jsonify(junior.resource_update(id=id,pay_password=pay_password,encrypted=True,title=title,body=body,price=amount,tags=tags,des=description))

5.9、博客删除

只是将链上的数据进行重定向。初级开发者与中级开发者均调用delete接口进行博客的删除,所需参数为博客的平台数据ID和用户密码。 示例代码如下:

@app.route('/blog/delete', methods=['POST'])
def blog_delete():
    current_user = auth_login_required() # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    id = request.json.get('id')
    password = request.json.get('password')
    if not id and not password:
        return return_result(60100)
    password = junior.decrypt(password)
    if not current_user.verify_password(password):
        return jsonify(return_result(60003))
    return jsonify(junior.delete(id, current_user.pay_password))

5.10、获取博客列表

获取所有博客信息,该信息为应用所有资源数据列表。返回资源的基本信息列表。初级开发者与中级开发者使用queryresource接口实现。 示例代码如下:

@app.route('/blog/all/list',methods=['POST'])
def blog_list():
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    try:
        page = request.json.get('page')
        num = request.json.get('num')
    except:
        page = 1
        num = 10
    if not page:
        page = 1
    if not num:
        num = 10
    return jsonify(junior.queryresource(page, num))

5.11、根据ID查询博客信息

根据ID去查询博客的相关信息。开发者使用query_resource_by_ID去实现。 示例代码如下:

@app.route('/blog/condition/id',methods=['POST'])
def blog_list_by_ID():
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    ids = request.json.get('ids')
    if not ids and not isinstance(ids, list):
        return jsonify(return_result())
    return jsonify(junior.query_resourc_by_ID(ids))

5.11、查询资源是否已经购买

查询当前用户是否已经购买过该资源。通过调用checkisbought接口实现。 示例代码如下:

@app.route('/blog/isbought',methods=['POST'])
def check_bought():
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    claim_ids = request.json.get('claim_ids')
    if claim_ids is None:
        return jsonify({
            'errcode': 60100,
            'reason': '缺少参数'
        })
    return jsonify(junior.checkisbought(current_user.wallet, claim_ids))

5.12、添加博客浏览量

仅支持初级开发者。初级开发者调用resource_views接口实现。前端调用该接口为对应的数据库访问添加1。

示例代码如下:

@app.route('/blog/views',methods=['POST'])
def add_views():
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    title = request.json.get('title')
    if title is None:
        return jsonify({
            'errcode': 60100,
            'reason': '缺少参数'
        })
    # add blog views
    return jsonify(junior.resouce_views(title))

5.13、用户支付博客

用户支付博客。用户根据博客信息支付对应的价格,购买成功后返回给用户哈希值,初级开发者通过调用pay_resources接口实现,中级开发者通过调用transaction接口实现。

@app.route('/pay/blogs',methods=['POST'])
def pay_blogs():
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    password = request.json.get('password')
    claim_id = request.json.get('claim_id')
    if password is None or claim_id is None:
        return jsonify({
            'errcode':60100,
            'reason':"缺少参数"
        })
    return jsonify(junior.pay_resources(current_user, claim_id, password, encrypted=True))

5.14、用户获得广告收益

用户浏览广告会的到广告费用,广告由用户通过发布接口发布,价格为负数即为广告,广告主需要支付给浏览者一定数量的ulord。初级开发者通过调用pay_ads接口实现。中级开发者通过调用transaction接口实现。 示例代码如下:

@app.route('/pay/ads',methods=['POST'])
def pay_ads():
    """
    get ulord from ads

    :return: ads hash
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    claim_id = request.json.get('claim_id')
    authorname = request.json.get('author')
    if claim_id is None or authorname is None:
        return jsonify({
            'errcode': 60100,
            'reason': "缺少参数"
        })
    return jsonify(junior.pay_ads(current_user.wallet, claim_id, authorname))

5.15、用户获得账号信息

此功能仅为初级开发者才支持实现,通过调用user_info_query接口实现。 示例代码如下:

@app.route('/user/info',methods=['GET'])
def get_userinfo():
    """
    get user infor

    :return: dict.User information
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    return jsonify(junior.user_info_query(token=current_user.token))

5.16、用户获得余额信息

获取用户的余额信息,在区块链中,由于区块是需要被确认的,所以余额拥有总余额,已确认余额,未确认余额,未成熟的余额四个部分,讲以字典的形式返回。开发者可以通过调用querybalance接口实现。 示例代码如下:

@app.route('/user/balance',methods=['GET'])
def get_userbalance():
    """
    get user balance

    :return: user balance
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)

    return jsonify(junior.querybalance(payer=current_user.wallet, pay_password=current_user.pay_password))

5.16、用户获取发布过的内容

用户获取自己所发布过的内容列表。开发者调用queryuserpublished接口实现。 示例代码如下:

@app.route('/user/published',methods=['POST'])
def get_userpublished():
    """
    get blog list the user has published
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    try:
        page = request.json.get('page')
        num = request.json.get('num')
        category = request.json.get('category')
    except:
        page = 1
        num = 10
    if not page:
        page = 1
    if not num:
        num = 10
    return jsonify(junior.queryuserpublished(current_user.wallet, page, num))

5.17、用户获取发布过的内容总数

用户获取发布过的内容总数。开发者调用ulord_published_num实现。 示例代码如下:

@app.route('/user/published/num',methods=['GET'])
def get_userpublishednum():
    """
    get the num of the blogs that user has published
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    return jsonify(junior.ulord_published_num(current_user.wallet))

5.18、用户获取账单

用户获取账单信息,涵盖用户的两种角色的总计四种收支情况,涵盖作为消费者的支出(浏览收费资源)与收入(浏览广告),作为发布者的支出(发布广告)与收入(发布收费资源)四种情况。开发者可以通过调用querybillings接口实现。 示例代码如下:

@app.route('/user/billings',methods=['POST'])
def get_billings():
    """
    query user's billing
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    sdate = request.json.get('sdate')
    edate = request.json.get('edate')
    if not sdate or not edate:
        return jsonify(return_result(60100))
    return jsonify(junior.querybillings(current_user.wallet, sdate, edate))

5.19、用户获取账单详情

用户获取收支账单详情。列出用户的每条消费记录,用户作为发布者(即消费记录中auther为当前用户),价格为正时表示资源收入,价格为负时表示广告支出。与此相反,如果用户作为消费者(即消费记录中auther不是当前用户),价格为正时表示资源支出,价格为负时表示广告收入。这一部分逻辑需要前端整理清楚方便展示。开发者通过调用querybillingsdetail接口实现功能。 示例代码如下:

@app.route('/user/billings/details',methods=['POST'])
def get_billingsdetail():
    """
    query user billing detail
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    try:
        page = request.json.get('page')
        num = request.json.get('num')
    except:
        page = 1
        num = 10
    if not page:
        page = 1
    if not num:
        num = 10
    return jsonify(junior.querybillingsdetail(current_user.wallet, page, num))

5.20、用户获取收入账单

用户获取收入的账单详情,列出用户作为消费者点击广告的收入以及作为发布者他人浏览收费资源收到的费用。开发者通过调用queryincomebillings接口实现这部分功能。 示例代码如下:

@app.route('/user/billings/income',methods=['POST'])
def get_incomebillings():
    """
    get user income billing information
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    try:
        page = request.json.get('page')
    except:
        page = 1
    try:
        num = request.json.get('num')
    except:
        num = 10
    try:
        category = request.json.get('category')
    except:
        category = 2
    sdate = request.json.get('sdate')
    edate = request.json.get('edate')
    if not page:
        page = 1
    if not num:
        num = 10
    if not category:
        category = 2
    if not sdate or not edate:
        return jsonify(return_result(60100))
    return jsonify(junior.queryincomebillings(current_user.wallet, sdate, edate, page, num, category=category))

5.21、用户获取支出账单

与用户获取收入账单相似,此功能为用户获取支出账单。包含用户作为消费者点击收费资源的支出以及作为发布者发布广告的支出。(注意:用户作为发布者发布广告时定价之后浏览者点击后会从发布者账户扣除对应费用转到浏览者账户中)。开发者通过调用queryoutgobillings接口实现功能。 示例代码如下:

@app.route('/user/billings/outgo',methods=['POST'])
def get_expensebillings():
    """
    get user expense billing information
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    try:
        page = request.json.get('page')
    except:
        page = 1
    try:
        num = request.json.get('num')
    except:
        num = 10
    try:
        category = request.json.get('category')
    except:
        category = 2
    sdate = request.json.get('sdate')
    edate = request.json.get('edate')
    if not page:
        page = 1
    if not num:
        num = 10
    if not category:
        category = 2
    if not sdate or not edate:
        return jsonify(return_result(60100))
    return jsonify(junior.queryoutgobillings(current_user.wallet, sdate, edate ,page, num, category=category))

5.22、用户修改信息

用户修改个人信息,包括用户密码,用户手机号与邮箱。该功能仅支持初级开发者用户,初级开发者通过调用user_infor_modify实现功能。这部分操作主要是修改本地数据库内容,所以中级开发者需要自己实现。

@app.route('/user/modify',methods=['POST'])
def modify_userinfo():
    """
    Delete modify username.It will make publish error
    """
    current_user = auth_login_required()  # check token
    if type(current_user) is dict:
        return jsonify(current_user)
    # change demand: cann't change username
    # username = request.json.get('username')
    password = request.json.get('password')
    cellphone = request.json.get('cellphone')
    email = request.json.get('email')
    new_password = request.json.get('new_password')
    if not password:
        # missing arguments
        return jsonify({
            'errcode': 60100,
            'reason': '缺少参数'
        })
    if cellphone:
        cellphone = junior.decrypt(cellphone)
    if email:
        email = junior.decrypt(email)
    return jsonify(junior.user_infor_modify(username=current_user.username, encrypted=True, password=password,cellphone=cellphone,email=email,new_password=new_password))