雲中漫步,作個公衆號方便生活、取悅本身

收錄待用,修改轉載已取得騰訊雲受權html


背景

筆者日常負責小組下午茶的組織(部門的小福利),每次購買點心後,須要先墊付費用並記錄下來,等到季度末的時候再彙總給接口人統一報銷。兩個季度下來,總感受一些地方須要改進:python

  • 不能隨手記錄(手機下單,不能及時把消費記錄存入電腦的excel上)mysql

  • 報銷記錄怕丟失(硬盤壞過一次,翻了不少app的歷史才把記錄找回)linux

  • 彙總統計不方便(各組記錄方式不同[xls?txt?],須要接口人逐個蒐集,再一一彙總統計)nginx

做爲一個搞自動化出生的技術控,面對這些問題簡直不能忍,在合併完上個季度的報銷費用後,我問本身:爲何不作個工具,既能解決問題,又能取悅本身呢?git

需求分析

對於小工具而言,主要需求以下:github

  1. 能夠隨手記錄web

  2. 能夠自動備份sql

  3. 能夠自動統計數據庫

自動備份和自動統計都比較容易,只要數據能錄入到後臺,這些都不是事兒;而要能隨手記錄,那就必須是移動端的應用(畢竟手機你們基本都是隨身帶,下單也可能是用手機),且安卓和蘋果都支持。通過這麼一分析,作個【微信公衆號】就是一個合適的輕量級解決方案啦 ^_^

固然,要做爲部門記帳報銷用的工具,還得加一些小需求:

  • 支持用戶註冊和鑑權 (萬一有其餘人也關注了公衆號,提幾個數據上來,那就悲劇了)

  • 支持多用戶報銷 (一個組可能有多人輪流組織,各自都要報銷)

系統設計

1.用戶場景

按照角色進行劃分以下:

  • 用戶:每一個小組的下午茶接口人,購買下午茶後錄入消費記錄,等待報銷

  • 管理員:中心報銷接口人,每季度彙總各小組消費統計數據,統一報銷

2.系統交互

  • 下午茶公衆號:從用戶場景看,用戶與公衆號的交互並不複雜,用基於文本交互的訂閱號(未認證)來實現,性價比是最高的。固然,若是有條件經過微信認證,使用菜單和界面等高級能力,交互體驗會更友好。

  • 公衆號web服務:web服務主要是處理微信公衆平臺轉發來的http消息,下文會詳細介紹如何基於騰訊雲來搭建公衆號web服務

3.基於騰訊雲的公衆號web服務

  • 服務器選型: 公衆號的用戶量並不大,業務場景也不復雜,因此最低配的CVM雲服務器(1核cpu+1G內存+20G雲硬盤+1M帶寬)徹底能夠知足需求。

  • 數據庫&緩存:騰訊雲自己提供了很是完善的數據庫雲服務(CDB和雲緩存),不過以本公衆號的數據量和訪問量來看,用專業的數據庫雲服務確實有點大材小用了,還增長成本。所以直接在CVM上部署mysql和memcached,實際運行下來,發現性能徹底沒有問題。

  • web服務:單從公衆號的交互方式來講,選Django彷佛有點‘重’,Tornado、Flask、web.py等輕框架更合適。不過,經過Django的後臺管理功能能夠對用戶、費用、組織關係等信息進行快速的維護,能省掉很多的工做量,後續有新需求也方便擴展,所以web框架選用Django。Web服務的搭建採用經典的Nginx+uwsgi+Django的方式,穩定性和性能也都能獲得保證。

  • 數據安全性:報銷記錄很是敏感,每一筆都是實打實的經費,數據的安全性格外的重要,所以,增長一層數據的保護很是有必要。高可用、高穩定、強安全的雲對象存儲服務COS就是一個很好的選擇,將數據每日備份到COS中,可讓數據的安全性獲得大大的提升。

說明:使用數據庫雲服務來提高數據安全性是最直接有效的,考慮到儘可能減小小工具的成本,這裏選用了COS存儲服務(COS每個月有50G存儲、10G流量的免費額度,徹底知足須要)

  • 服務可用性:做爲一個面向部門多個小組的記帳服務,可用性須獲得保證,當服務出現異常時,必需要立刻發現並修復。經過騰訊雲提供的雲監控服務,能夠在服務出現故障後第一時間獲得通知

系統實現

完成了需求分析和系統設計,實現起來就是水到渠成了。

1.新建雲服務器實例

在‘雲產品’中選擇‘雲服務器’,進入雲主機便可按提示建立CVM雲服務器

  • 鏡像這裏選擇的是‘CentOS 7.2 64位’

  • 硬盤建議選擇‘雲硬盤’,便於後續升級

  • 必定要選擇1M或以上的帶寬,並分配公網IP,不然沒法接收微信公衆平臺的消息

  • 若是選擇了額外的雲硬盤,在登陸後要先掛載才能使用

fdisk -l
fdisk /dev/vdb  -- 這裏有一系列交互式命令
mkfs.ext3 /dev/vdb1
mount /dev/vdb1 /data
cat /etc/fstab
echo '/dev/vdb1 /data ext3 defaults 0 0' >> /etc/fstab

2.軟件安裝

  • 經過yum,大部分須要的軟件均可以快速安裝
yum install nginx
yum install uwsgi
yum install uwsgi-plugin-python
yum install python-devel
yum install MySQL-python
yum install memcached
yum install Python-memcached
  • mysql和django須要本身手動安裝

  • 如何在linux下安裝和配置mysql,網上的文章很是全,這裏再也不贅述(本文采用5.7.16)

  • yum中的django版本較低,能夠自行下載須要的版原本安裝(文本採用1.8.4)

上傳安裝包到雲服務器,推薦使用FileZilla(上傳下載均可以),windows和mac都支持,界面友好,很是方便。

3.Nginx+uwsgi+Django配置

網上的資料雖然很多,但實際配置起來不免踩坑,這裏給出筆者的配置流程,供參考

  • 3.一、建立Django項目

本步驟主要是建立一個初始django項目,用於調試nginx+uwsgi+django,下午茶app的邏輯實現放在後面完成

// 此命令須要完成django安裝後才能使用
django-admin.py startproject wx_website
/* 
項目的目錄結構以下,其中
apps目錄存放下午茶對應的app工程
conf目錄存放項目相關的配置文件
lib目錄存放公共庫
media、static、templates目錄存放資源和模版文件
op目錄存放網站操做腳本
*/
ll wx_website
drwxr-xr-x 6 root root 4096 Jan 16 22:20 apps
drwxr-xr-x 2 root root 4096 Feb 23 21:13 conf
drwxr-xr-x 2 root root 4096 Jan  2 02:05 lib
-rwxr-xr-x 1 root root  253 Nov 19 14:15 manage.py
-rw-r--r-- 1 root root  418 Nov 26 08:20 manage.pyc
drwxr-xr-x 2 root root 4096 Nov 15 22:26 media
drwxr-xr-x 2 root root 4096 Nov 16 08:06 op
drwxr-xr-x 6 root root 4096 Feb 10 17:05 static
drwxr-xr-x 3 root root 4096 Dec 25 15:34 templates
drwxr-xr-x 2 root root 4096 Mar  4 10:14 wx_website
// 調試模式啓動,監聽分配的公網ip 14.249.22.158上的8000端口
python manage.py runserver 14.249.22.158:8000

訪問http://14.249.22.158:8000 看到Django歡迎頁面,說明此步驟成功

IP‘14.249.22.158爲示例,請替換爲CVM的公網IP

  • 3.二、nginx+uwsgi配置

網上有不少例子是先配置uwsgi+django,再配置nginx+uwsgi,實際操做時很容易埋坑;這裏直接給出完整的nginx+uwsgi配置,一次搞定

* 在conf目錄下建立如下3個文件,分別爲uwsgi和nginx的配置文件
ll conf
-rw-r--r-- 1 root root  253 Nov 19 14:15 uwsgi.ini
-rw-r--r-- 1 root root  664 Nov 19 14:15 uwsgi_params
-rw-r--r-- 1 root root 1157 Feb 23 21:13 wx_website_nginx.conf

uwsgi_params

uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;
uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;
uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

uwsgi.ini

[uwsgi]
socket = /var/run/uwsgi/uwsgi.sock
master = true
# 步驟1中建立的wx_website項目的絕對路徑
pythonpath = /data/website/wx_website
chdir = /data/website/wx_website
module = wx_website.wsgi
processes = 4
# 日誌所在的目錄必定要先建立好
daemonize = /var/log/uwsgi/uwsgi.log
plugins = python

wx_website_nginx.conf

worker_processes  4;
# 日誌和pid所在的目錄必定要先建立好
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;
  log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
    # 日誌目錄要先建立好
  access_log   /var/log/nginx/access.log  main;
  sendfile     on;
  upstream django {
      # 對應uwsgi.ini中配置的socket文件路徑
    server unix:///var/run/uwsgi/uwsgi.sock;
    # server 127.0.0.1:8000;
  }
  server {
    listen      80;
    server_name 14.249.22.158;
    charset     utf-8;
    client_max_body_size 10M;
    location /media  {
        alias /data/website/wx_website/media;
    }
    location /static {
        alias /data/website/wx_website/static;
    }
    location / {
        uwsgi_pass  django;
        include     /data/website/wx_website/conf/uwsgi_params;
    }
  }
}
* 啓動nginx
# step1:驗證配置文件是否正確
nginx -t -c /data/website/wx_website/conf/wx_website_nginx.conf
# step2:步驟1顯示successful後,啓動nginx
nginx -t -c /data/website/wx_website/conf/wx_website_nginx.conf
# 後續修改nginx配置並驗證成功後,用此命令使新配置生效
nginx -s reload
  • 在op目錄下建立uwsgiserver.sh文件以下:
#!/bin/bash
# 這裏爲上文中uwsgi.ini的全路徑
uwsgi_ini_path=/data/website/wx_website/conf/uwsgi.ini
if [ ! -n "$1" ]
then
        echo "Usages: sh uwsgiserver.sh [start|stop|restart]"
        exit 0
fi
if [ $1 = start ]
then
        psid=`ps aux | grep "uwsgi" | grep -v "grep" | wc -l`
        echo "psid:"$psid
        if [ $psid -gt 4 ]
        then
                echo "uwsgi is running!"
                exit 0
        else
                uwsgi $uwsgi_ini_path
                echo "Start uwsgi service [OK]"
        fi
elif [ $1 = stop ];then
        killall -9 uwsgi
        echo "Stop uwsgi service [OK]"
elif [ $1 = restart ];then
        killall -9 uwsgi
        uwsgi $uwsgi_ini_path
        echo "Restart uwsgi service [OK]"
else
        echo "Usages: sh uwsgiserver.sh [start|stop|restart]"
fi

這個sh腳本用於uwsgi的啓停,後續開發過程當中,它的使用頻率會很是高

  • 修改django相關代碼後,須要重啓uwsgi使修改生效*

  • 啓動uwsgi

# 啓動uwsgi
sh uwsgiserver.sh start
# 重啓uwsgi
sh uwsgiserver.sh restart

最後,訪問http://14.249.22.158/ 出現django歡迎頁面,說明nginx+uwsgi+django配置成功

Tips:若是到最後一步,沒有出現django歡迎頁面,能夠查看如下幾個日誌文件定位問題

/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/uwsgi/uwsgi.log

4.公衆號django-app開發

下午茶消費如何記錄和報銷等邏輯(下圖灰色部分),不具有廣泛參考性,就不詳細介紹了,這裏主要介紹微信公衆號交互相關的內容。

  • 4.一、建立app

  • 在項目根目錄建立名字爲happytea的app

python manage.py startapp happytea
  • 將生成的happytea目錄移動到wx_website/apps/目錄下:
mv happytea/ apps/
ll /data/website/wx_website/apps/happytea
-rw-rw-r-- 1 root root     0 Jan 16 22:19 __init__.py
-rw-rw-r-- 1 root root   838 Feb 17 00:09 admin.py
-rw-rw-r-- 1 root root  1762 Feb 15 21:47 models.py
-rw-r--r-- 1 root root   162 Feb 12 13:25 urls.py
-rw-rw-r-- 1 root root 29199 Mar 24 22:10 views.py
  • wx_website/wx_website/settings.py 中添加app
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_crontab',
# 添加新建立的app
    'apps.happytea'
)
  • 4.二、修改url路由配置

  • 首先修改django的頂層url路由配置 wx_website/wx_website/urls.py

urlpatterns = [
    url(r'^$', views.web_root_index),
    url(r'^admin/', include(admin.site.urls)),
# 將/happytea/路徑下請求路由到app的url定義文件中
    url(r'^happytea/', include('apps.happytea.urls', namespace="happytea")),
]
  • 修改app的url路由配置 wx_website/apps/happytea/urls.py
urlpatterns = [
# 將/happytea/wxmp/路徑下的消息路由到 views中wxmp函數處理
    url(r'^wxmp/$', views.wxmp, name='wxmp'),
]
  • 4.三、引入wechat_sdk庫

微信公衆平臺相關的邏輯,主要有3個方面的內容(上圖紅框部分):

1\. 公衆號token的維護與更新

2\. 驗證消息是否鑑權經過

3\. 微信xml消息解析與封裝

經過微信公衆平臺的 開發文檔 能夠理解這些概念和協議,從而實現對應的處理邏輯。
不過從開發效率上看,若是引入開源的wechat_sdk庫,能夠將咱們從這些非主幹業務的開發工做中解放出來,節省大量的開發工做;並且開源庫通過大量實際項目的運行,可靠性也很是高

這裏給你們推薦wechat-python-sdk文檔清晰易懂,接口調用簡單,按照示例能快速上手。以本項目爲例:

* 定義用於存儲/獲取的「token」和「時間戳」的函數
*
# 保存新的token和過時時間,覆蓋原來保存的
def set_wx_token_func(newtoken, expires_at):
    # 直接使用django的模型保存
    token.update_token(newtoken, expires_at)
#
# 獲取當前保存的token和過時時間(以前經過set_wx_token_func中保存的)
def get_wx_token_func():
    last_token = token.get_token_by_wxid(wx_id)
    return (str(last_token.token), last_token.expires_at)
  • 獲得WechatBasic類實例,後面的公衆平臺相關操做都經過此實例完成
# 公衆號的配置信息
# access_token_getfunc和access_token_setfunc爲前一步定義的兩個函數
conf = WechatConf(token=apptoken, appid=appid, appsecret=appsecret,
                  encrypt_mode='normal', encoding_aes_key=appaeskey,
                  access_token_getfunc=get_wx_token_func,
                  access_token_setfunc=set_wx_token_func,
                  access_token_refreshfunc=None)
wechat = WechatBasic(conf=conf)
  • 驗證微信消息
# 1,解析3個請求鑑權參數
    signature = request.GET.get('signature')
    timestamp = request.GET.get('timestamp')
    nonce = request.GET.get('nonce')

    # 二、經過sdk提供的check_signature接口驗證消息是否鑑權經過
    if not wechat.check_signature(signature, timestamp, nonce):
        logger.info('check_signature fail')
        return HttpResponseForbidden()

    # 三、若是是http-get方法,說明是平臺配置驗證,返回echostr參數的值便可;
    #    若是是http-post,則說明微信公衆平臺轉發的用戶命令消息,進一步處理
    if request.method == 'GET':
        echostr = request.GET.get('echostr')
        logger.info('wx reg succ')
        return HttpResponse(echostr)
    elif request.method == 'POST':
        return handler_wxmp_req(request.body)
* 解析微信轉發的xml請求,提取發送用戶id、消息類型、消息內容
# 一、解析xml消息
    try:
        wechat.parse_data(body)
    except ParseError:
        logger.error('parse wx_msg fail, msg: ' + body)
        return HttpResponseBadRequest()

    # 二、獲得發送消息的用戶id、消息類型、消息內容
    userid = wechat.message.source # 用戶id
    msg = wechat.message 
    if isinstance(msg, TextMessage): # 消息類型
        txt = wechat.message.content   # 消息文本
* 將要回復的文本消息封裝爲微信公衆號xml響應
# 解析命令文本,判斷格式是否正確
   (ret, expinfo) = parse_add_expense(txt)
   if not ret:
        content = '您輸入的格式有誤'
                # 將返回的文本內容(content)封裝爲xml響應文本
                # response_text方法返回的是一個xml文本
                # 將此文本做爲http-body返回給微信公衆平臺便可
        return wechat.response_text(content=content)

按照上面的方法,就能夠很方便的完成與微信公衆平臺相關的邏輯處理了。

  • 4.四、下午茶邏輯處理
  • 如何進行django-app的開發,能夠參考官方文檔,很是全面,這也是選擇django框架的優勢之一。

這裏僅僅截取一段簡單‘用戶狀態查詢’邏輯,便於理解下文的公衆號操做示例

# 獲得用戶發送的文本(已完成微信xml請求的解析)
    txt = wechat.message.content.strip()
    # 根據請求中的用戶id查詢用戶信息
    user = User.get_user_by_openid(wechat.message.source)
    # 若是命令文本是‘2’,說明是要查詢用戶狀態
    if txt == '2':
        # 獲得用戶的狀態信息文本
        content = gen_user_info(user)
                # 將要返回給用戶的文本結果封裝爲xml響應文本
        rspxml = wechat.response_text(content=content)
                # 將xml響應文本做爲http響應的body返回給公衆平臺
                return HttpResponse(rspxml)

到這裏,咱們已經完成了服務器環境的搭建和公衆號後臺服務的開發。接下來就能夠配置公衆號,讓搭建的後臺服務來處理用戶發送的命令了。

5.微信公衆號配置

  • url填入在django-app開發中配置的url,確保對應的view邏輯能夠處理微信公衆平臺轉發的消息

  • token須要與實例化wechat-python-sdk的WechatConf時傳入的token參數相同

  • EncodingAESKey也是要與實例化WechatConf時傳入的參數相同

  • 加密方式選擇‘明文模式’,便於調試

  • 點擊「提交」,公衆號後臺服務將收到來自公衆平臺的http-get請求。按照4.3中的處理邏輯,若是校驗成功並返回了echostr,則公衆號配置成功,後續用戶在公衆號中發送的消息,都會轉發給咱們的後臺服務處理。

  • 出現下圖說明配置成功

  • 在公衆號中發送文本‘2’,驗證功能是否正確

TIPS:若是提交公衆號的基礎配置未成功 或 發送命令後未返回結果,請檢查django邏輯處理的日誌來定位問題

6.使用COS雲儲存

  • 6.二、建立一個Bucket,這裏取名爲‘happytea’

    • 要使用cos雲存儲,必選先建立bucket

    • 最終存儲的文件,必須在某一個bucket下

    • 能夠認爲bucket就是根目錄,下面能夠存放文件或者建立目錄

    • bucket的所屬地域,最好與CVM的機房所在地域一致,以得到最快的上傳、下載速度

  • 6.三、參考COS-PYTHON-SDK文檔進行安裝

推薦pip方式

pip install qcloud_cos_v4
  • 6.四、在雲API密鑰中查詢appid、secretid、secretkey信息,調用cos-sdk的時候須要用到

  • 6.五、在代碼中調用sdk,完成文件的雲存儲(上傳)

# 導入cos-sdk
from qcloud_cos import CosClient
from qcloud_cos import UploadFileRequest
# 經過appid、secretid、secretkey,cos-region(bucket的地域)
# 建立一個CosClient實例
os_client = CosClient(settings.COS_APPID, settings.COS_SECRET_ID,
                               settings.COS_SECRET_KEY, settings.COS_REGION)
# 建立一個上傳文件請求,參數爲:bucket名稱、路徑、文件路徑
# 注意參數的類型爲unicode,不是string
cos_upload_req = UploadFileRequest(
            u'happytea', u'/' + filename.decode('utf8'), file_path.decode('utf8'))
# 設置上傳時若是存在同名同路徑文件,是否容許覆蓋
cos_upload_req.set_insert_only(0)
# 經過client完成上傳,並獲得上傳響應對象
cos_upload_rsp = cos_client.upload_file(cos_upload_req)
# 判斷響應(json對象)的‘code’值是否等於0
# 0:上傳cos成功,非0:失敗
if cos_upload_rsp['code'] != 0:
    raise Exception('cos upload fail, info:{}'.format(cos_upload_rsp['message']))
  • 6.六、添加一個定時任務,每日調用腳本上傳關鍵數據的備份文件到cos中

7.添加雲監控

  • 7.一、進入雲監控-告警策略管理,添加‘告警策略’

  • 7.二、添加告警觸發條件(要監控的內容),如cpu、內存、ping不可達、磁盤只讀等等

  • 7.三、關聯告警對象,這裏勾選咱們要監控的cvm服務器

  • 7.四、設置告警接收組,這裏選擇接收告警(郵件、短信)的用戶分組

這樣,當cvm服務器出現咱們監控的問題時,就能夠經過短信、郵件立刻獲得通知了。

公衆號實現效果

到這裏,基於雲服務的公衆號開發就完成了,使用時效果以下

  • 添加一筆下午茶報銷費用

  • 查看小組未報銷費用

  • 查看中心未報銷費用

END

回想幾年前,有朋友開發一個小型的業務系統,卻因服務器購買、託管、網絡等問題耗費了大量時間和精力,等系統開發完成,已錯過了最佳的上線時間,實在讓人唏噓。

現在,不管是服務器、網絡、雲存儲仍是CDN,雲服務的生態已經很是成熟,門檻低且成本小,咱們何不放飛本身的夢想,在雲中世界裏盡情的飛翔呢!


原文連接:https://www.qcloud.com/community/article/704748001488448345

相關文章
相關標籤/搜索