一個簡單的小型項目開發(fā)(奔跑吧兄弟投票活動)

2018-11-21 21:33 更新

Give it some time, everything will be okay. -- 《那些年我們瘋狂的青春/青春洋溢色彩/This Youth is Crazy》

4.3.1 模擬的業(yè)務(wù)場景

假設(shè)我們需要為“奔跑吧兄弟”綜藝節(jié)目開發(fā)一套手機App的投票接口,以供用戶在觀看電視的同時,可以進行投票活動參與互動。

下面將以此模擬的業(yè)務(wù)場景,提供一個接口開發(fā)實戰(zhàn)的過程。

4.3.2 源代碼下載

PhalApi-Demo-Vote

4.3.3 接口總列表

以下是我們根據(jù)業(yè)務(wù)需求整理出來的接口:

  • 1、用戶可以通過微信、QQ、新浪微博等渠道進行第三方登錄
  • 2、用戶可以創(chuàng)建團隊進行參賽,但隊名不能重復(fù)
  • 3、用戶可以對已參賽的團隊進行投票,且每個用戶每天投票最多不能超過3次,支持可配置
  • 4、獲取已參賽團隊的得票排行榜

4.3.4 主要涉及技術(shù)功能點

  • 1、使用User擴展類庫實現(xiàn)第三方登錄操作
  • 2、使用緩存存放用戶每天投票的次數(shù)(為方便起見,使用文件緩存,不落地)
  • 3、對接口進行簽名驗證(為方便起見,固定sign簽名)
  • 4、數(shù)據(jù)庫的基本操作
  • 5、自動化腳本的使用

4.3.4 快速開發(fā)流程

(題外音:整個示例的開發(fā),我個人在單元測試驅(qū)動開發(fā)下,只用了兩個多小時,其中還包括對模板場景的業(yè)務(wù)構(gòu)思、建表、編寫單元測試代碼等)。

(1)創(chuàng)建項目和部署環(huán)境

把PhalApi最新的框架代碼下載后,并將User擴展類庫按文檔說明配置后,將項目部署到了以下接口域名:

http://api.vote.phalapi.com

測試一下:

http://api.vote.phalapi.com/vote/?sign=phalapi

//返回
{
    "ret": 200,
    "data": {
        "title": "Hello World!",
        "content": "PHPer您好,歡迎使用PhalApi!",
        "version": "1.1.4",
        "time": 1431796924
    },
    "msg": ""
}

Good! 下面是簡明的開發(fā)過程。

(2)單元測試驅(qū)動開發(fā)

在定好接口后:

<?php
class Api_Act extends PhalApi_Api {

    public function joinIn() {
    }

    public function showList() {
    }

    public function vote() {
    }
}

便可使用腳本,快速生成單元測試的骨架代碼:

$ cd ./Vote/Tests/Api
$ phalapi_buildtests ../../Api/Act.php Api_Act ../test_env.php  > Api_Act_Test.php

(3)快速開發(fā)

開發(fā)過程此處略,但在單元測試驅(qū)動的引導(dǎo)下,很快就產(chǎn)出了以下高質(zhì)量的代碼:

.
├── Api
│   ├── Act.php
│   └── Default.php
├── Common
│   └── SignFilter.php
├── Domain
│   ├── Team.php
│   └── Vote.php
├── Model
│   ├── Team.php
│   ├── UserVoteRecord.php
│   └── Vote.php
└── Tests
    ├── Api
    │   ├── Api_Act_Test.php
    │   └── Api_Default_Test.php
    ├── Domain
    ├── Model
    ├── phpunit.xml
    └── test_env.php

(4)單元測試全部通過了!

為了方便大家查看,已省略了部分的調(diào)試內(nèi)容,但保留了測試過程中全部執(zhí)行的SQL語句,如下:

$ phpunit ./Api_Act_Test.php 
PHPUnit 4.3.4 by Sebastian Bergmann.

[1 - 0.06911s]DELETE FROM phalapi_team WHERE (team_name = 'test team name');<br>
[2 - 0.06487s]SELECT expires_time FROM phalapi_user_session_1 WHERE (user_id = '1') AND (token = '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731');<br>
[3 - 0.06553s]SELECT COUNT(id) FROM phalapi_team WHERE (team_name = 'test team name');<br>
[4 - 0.0653s]INSERT INTO phalapi_team (team_name) VALUES ('test team name');<br>
[5 - 0.0699s]SELECT expires_time FROM phalapi_user_session_1 WHERE (user_id = '1') AND (token = '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731');<br>
[6 - 0.06555s]SELECT COUNT(id) FROM phalapi_team WHERE (team_name = 'test team name');<br>
[9 - 0.06778s]SELECT expires_time FROM phalapi_user_session_1 WHERE (user_id = '1') AND (token = '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731');<br>
[10 - 0.06603s]SELECT COUNT(id) FROM phalapi_team WHERE (id = 3);<br>
[11 - 0.06825s]SELECT vote_num FROM phalapi_vote WHERE (team_id = 3) LIMIT 1;<br>
[12 - 0.07374s]UPDATE phalapi_vote SET team_id = 3, vote_num = 22 WHERE (team_id = 3);<br>
[13 - 0.07012s]SELECT expires_time FROM phalapi_user_session_1 WHERE (user_id = '1') AND (token = '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731');<br>
[14 - 0.06682s]SELECT COUNT(id) FROM phalapi_team WHERE (id = 3);<br>
[15 - 0.07433s]SELECT vote_num FROM phalapi_vote WHERE (team_id = 3) LIMIT 1;<br>
[16 - 0.07283s]UPDATE phalapi_vote SET team_id = 3, vote_num = 23 WHERE (team_id = 3);<br>
[17 - 0.07307s]SELECT expires_time FROM phalapi_user_session_1 WHERE (user_id = '1') AND (token = '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731');<br>
[18 - 0.07501s]SELECT COUNT(id) FROM phalapi_team WHERE (id = 3);<br>
[19 - 0.07135s]SELECT vote_num FROM phalapi_vote WHERE (team_id = 3) LIMIT 1;<br>
[20 - 0.07653s]UPDATE phalapi_vote SET team_id = 3, vote_num = 24 WHERE (team_id = 3);<br>
[21 - 0.07215s]SELECT expires_time FROM phalapi_user_session_1 WHERE (user_id = '1') AND (token = '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731');<br>
[22 - 0.06722s]SELECT COUNT(id) FROM phalapi_team WHERE (id = 3);<br>
[23 - 0.06506s]SELECT expires_time FROM phalapi_user_session_1 WHERE (user_id = '1') AND (token = '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731');<br>
[24 - 0.06732s]SELECT COUNT(id) FROM phalapi_team WHERE (id = 404);<br>

Time: 1.97 seconds, Memory: 7.00Mb

OK (4 tests, 42 assertions)

(5)運行效果 - Part 1

在我們通過第三方登錄后,我們就可以這樣進行接口操作了。

首先,讓我們添加兩個參賽團隊:

//奔跑吧兄弟(藍隊)
http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.JoinIn&team_name=%E5%A5%94%E8%B7%91%E5%90%A7%E5%85%84%E5%BC%9F%EF%BC%88%E8%93%9D%E9%98%9F%EF%BC%89&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{
    "ret": 200,
    "data": {
        "code": 0,
        "team_id": "5"
    },
    "msg": ""
}

//奔跑吧兄弟(紅隊)
http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.JoinIn&team_name=%E5%A5%94%E8%B7%91%E5%90%A7%E5%85%84%E5%BC%9F%EF%BC%88%E7%BA%A2%E9%98%9F%EF%BC%89&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{
    "ret": 200,
    "data": {
        "code": 0,
        "team_id": "6"
    },
    "msg": ""
}

然后,讓我們進行瘋狂地投票:

//第一次投票
http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.Vote&team_id=5&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{"ret":200,"data":{"code":0,"vote_num":1},"msg":""}

//第二次投票
http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.Vote&team_id=5&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{"ret":200,"data":{"code":0,"vote_num":2},"msg":""}

//第三次投票
http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.Vote&team_id=5&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{"ret":200,"data":{"code":0,"vote_num":3},"msg":""}

//第四次投票
http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.Vote&team_id=5&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回 - 注意此返回!
{"ret":200,"data":{"code":2,"vote_num":0},"msg":""}

最后,看一下排行榜:

http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.ShowList&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{
    "ret": 200,
    "data": {
        "code": 0,
        "teams": [
            {
                "id": 5,
                "team_name": "奔跑吧兄弟(藍隊)",
                "vote_num": 3
            },
            {
                "id": 6,
                "team_name": "奔跑吧兄弟(紅隊)",
                "vote_num": 0
            }
        ]
    },
    "msg": ""
}

至此,我們已經(jīng)可以把接口交付給客戶端同學(xué)使用啦!
當(dāng)然,我們還需要稍微整理輸出WIKI文檔~~~
部分接口返回的結(jié)果可能與你實際看到的不一樣,因為數(shù)據(jù)會在變化而且有單元測試的測試數(shù)據(jù)。

(6)運行效果 - Part 2

接口服務(wù),不僅僅需要提供正常的業(yè)務(wù)功能,還需要考慮到各種客戶端使用的情況,包括非法的請求,或者不合的調(diào)用,比如防刷票。

這一部分,主要展示接口在各種異常情況下的響應(yīng)能力。

簽名失敗

http://api.vote.phalapi.com/vote/?sign=XXX&service=Act.JoinIn&team_name=%E5%A5%94%E8%B7%91%E5%90%A7%E5%85%84%E5%BC%9F%EF%BC%88%E7%BA%A2%E9%98%9F%EF%BC%89&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{
    "ret": 400,
    "data": [
    ],
    "msg": "非法請求:wrong sign"
}

無登錄態(tài)

http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.JoinIn&team_name=%E5%A5%94%E8%B7%91%E5%90%A7%E5%85%84%E5%BC%9F%EF%BC%88%E7%BA%A2%E9%98%9F%EF%BC%89&token=XXX&user_id=1
//返回
{
    "ret": 401,
    "data": [
    ],
    "msg": "非法請求:user need to login again"
}

重復(fù)參賽

http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.JoinIn&team_name=%E5%A5%94%E8%B7%91%E5%90%A7%E5%85%84%E5%BC%9F%EF%BC%88%E7%BA%A2%E9%98%9F%EF%BC%89&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{
    "ret": 200,
    "data": {
        "code": 1,
        "team_id": 0
    },
    "msg": ""
}

當(dāng)天投票次數(shù)已達最大

//第四次投票后
http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.Vote&team_id=5&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回 - 注意此返回!
{
    "ret": 200,
    "data": {
        "code": 2,
        "vote_num": 0
    },
    "msg": ""
}

投票的團隊不存在

http://api.vote.phalapi.com/vote/?sign=phalapi&service=Act.Vote&team_id=404&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731&user_id=1
//返回
{
    "ret": 200,
    "data": {
        "code": 1,
        "vote_num": 0
    },
    "msg": ""
}

在進行上面的非法請求后,我們可以同時關(guān)注后臺的日志:

$ tailf ./Runtime/log/201505/20150517.log 
2015-05-17 01:35:32|DEBUG|user not login|{"userId":null,"token":null}
2015-05-17 01:36:01|DEBUG|user can not vote today|{"userId":1,"teamId":5}
2015-05-17 01:37:52|DEBUG|user can not vote today|{"userId":1,"teamId":5}
2015-05-17 01:45:25|DEBUG|user need to login again|{"expiresTime":0,"userId":"1","token":"XXX"}
2015-05-17 01:48:13|DEBUG|user can not vote today|{"userId":1,"teamId":5}

(7)需要的數(shù)據(jù)庫表

以下為關(guān)鍵的表,其他表,可以通過腳本自動生成,然后導(dǎo)入。

-- ----------------------------
-- Table structure for `phalapi_team`
-- ----------------------------
DROP TABLE IF EXISTS `phalapi_team`;
CREATE TABLE `phalapi_team` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `team_name` varchar(100) DEFAULT '' COMMENT '隊名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of phalapi_team
-- ----------------------------
INSERT INTO `phalapi_team` VALUES ('3', 'egg team');
INSERT INTO `phalapi_team` VALUES ('5', '奔跑吧兄弟(藍隊)');
INSERT INTO `phalapi_team` VALUES ('6', '奔跑吧兄弟(紅隊)');
INSERT INTO `phalapi_team` VALUES ('17', 'test team name');

INSERT INTO `phalapi_user` VALUES ('1', 'wx_edebc877070133c65161d00799e00544', 'weixinName', '******', '4CHqOhe1Jxi3X9HmRfPOXygDnU267eCA', '1431790647', 'phpunit.png');

INSERT INTO `phalapi_user_login_weixin` VALUES ('1', 'wx_122348561111', 'ASDF', '130000000', '1');

INSERT INTO `phalapi_user_session_1` VALUES ('1', '1', '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731', '', '1', '1431790647', '1934382647', null);

4.3.4 腳本的使用

(1)創(chuàng)建項目腳本 - phalapi_buildapp

此腳本可用于快速創(chuàng)建項目,其使用如下:

$ ./PhalApi/phalapi-buildapp 
Usage: ./PhalApi/phalapi-buildapp <app>

此腳本會根據(jù)Demo示例,快速生成一個新的項目。

(2)自動生成單元測試骨架腳本 - phalapi_buildtest

這是一個用來自動生成單元測試骨架的腳本,其使用如下:

$ phalapi-buildtest 

Usage: 
        php /usr/bin/phalapi-buildtest <file_path> <class_name> [bootstrap] [author = dogstar]

Demo:
        php ./build_phpunit_test_tpl.php ./Demo.php Demo > Demo_Test.php

然后,我們就可以使用輔助的PhalApiTestRunner::go()進行單元測試:

    public function testShowList()
    {
        //Step 1. 構(gòu)建請求URL
        $url = 'service=Act.ShowList&sign=phalapi&user_id=1&token=193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731';

        //Step 2. 執(zhí)行請求  
        $rs = PhalApiTestRunner::go($url);
        //var_dump($rs);

        //Step 3. 驗證
        $this->assertNotEmpty($rs);
        $this->assertArrayHasKey('code', $rs);
        $this->assertArrayHasKey('teams', $rs);
        $this->assertEquals(0, $rs['code']);

        $this->assertNotEmpty($rs['teams']);
        foreach ($rs['teams'] as $team) {
            $this->assertArrayHasKey('id', $team);
            $this->assertArrayHasKey('team_name', $team);
            $this->assertArrayHasKey('vote_num', $team);

            $this->assertGreaterThanOrEqual(0, $team['vote_num']);
        }
    }

(3)自動生成分表建表SQL語句 - phalapi_buildsqls

此腳本用于根據(jù)dbs.php配置和./Data/*.sql文件生成完整的建表語句,其使用如下:

$ ./PhalApi/phalapi-buildsqls 
Usage: ./PhalApi/phalapi-buildsqls <dbs.php> <table> [engine=InnoDB]

假設(shè)我們需要對vote進行分表,拆分成3個表,可以這樣配置:

//$ vim ./Config/dbs.php

        'vote' => array(
            'prefix' => 'phalapi_',
            'key' => 'id',
            'map' => array(
                array('start' => 0, 'end' => 2, 'db' => 'db_vote'),
            ),
        ),

然后準(zhǔn)備基本的建表語句:

//$vim ./Data/vote.sql

  `team_id` bigint(20) DEFAULT '0',
  `vote_num` int(11) DEFAULT '0',

最后,執(zhí)行生成腳本:

$ phalapi-buildsqls ./Config/dbs.php vote

/**
 * DB: phalapi_vote
 */

CREATE TABLE `phalapi_vote_0` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `team_id` bigint(20) DEFAULT '0',
  `vote_num` int(11) DEFAULT '0',
    `ext_data` text COMMENT 'json data here',
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `phalapi_vote_1` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `team_id` bigint(20) DEFAULT '0',
  `vote_num` int(11) DEFAULT '0',
    `ext_data` text COMMENT 'json data here',
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `phalapi_vote_2` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `team_id` bigint(20) DEFAULT '0',
  `vote_num` int(11) DEFAULT '0',
    `ext_data` text COMMENT 'json data here',
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4.3.5 在線接口參數(shù)查看

可以通過以下鏈接,在線實時查看接口參數(shù):

//參賽接口
http://api.vote.phalapi.com/vote/checkApiParams.php?service=Act.JoinIn

//投票接口
http://api.vote.phalapi.com/vote/checkApiParams.php?service=Act.Vote

//排行榜接口
http://api.vote.phalapi.com/vote/checkApiParams.php?service=Act.ShowList

其中,投票接口的參數(shù)如下:
a pic


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號