\chapter{API设计}
在开始构建UI之前，我们必须先设计API。


\section{验证}
\noindent 这是一个简单的API，供潜在用户了解它是否是服务器中的真实用户。

\begin{lstlisting}
@app.route('/api/v1/verify', methods=['POST'])
async def verify(request):
    data = request.json
    result = {"status": "wrong, you have to sign up or log in"}
    if data:
        username = data.get("username")
        temp_token = data.get("temp_token")
        if username and temp_token:
            the_user_we_found = users.find_one({"username": username, "temp_token": temp_token})
            if the_user_we_found != None:
                result["status"] = "ok"
    return response.json(result)
\end{lstlisting}

\noindent 如果我们的客户的状态为“错误”，则他可能需要重新登录


\section{获取联系人}
\noindent 我们使用此API来获取可以与之交谈的人员的列表。

\noindent 这里的逻辑很简单，我们在服务器中获取所有用户的姓名，但要求该列表的用户除外。

\begin{lstlisting}
@app.route('/api/v1/contactlist', methods=['POST'])
async def get_contact_list(request):
    data = request.json
    result = {"status": "wrong"}
    if data:
        username = data.get("username")
        temp_token = data.get("temp_token")
        if username and temp_token:
            the_user_we_found = users.find_one({"username": username, "temp_token": temp_token})
            if the_user_we_found == None:
                result["status"] = "wrong: user doesn't exist"
            else:
                userlist = users.find({"_id": {"$nin": [the_user_we_found.get("_id")]}})
                #userlist = users.find({"username": {"$nin": [username]}})
                name_list = []
                for user in userlist:
                    name_list.append(user.get("username"))
                result["result"] = {"name_list": name_list}
                result["status"] = "ok"
    return response.json(result)
\end{lstlisting}

\section{发送消息}
我们需要创建一个API来处理消息发送。

\subsection{步骤 1}
我想让这个API具有3个参数：
\begin{table}[h!]
\centering
\begin{tabular}{ll}
\hline
参数名 & 描述 \\ \hline
from & 此消息来自哪里               \\ 
to   & 我们需要将此消息发送给哪个目标用户       \\
text & 我们要发送的消息
\end{tabular}
\end{table}

Function that handles "/api/v1/send":
\begin{lstlisting}
     username = data.get("username")
     temp_token = data.get("temp_token")
     body = data.get("body")
     if username and temp_token and body:
         the_user_we_found = users.find_one({"username": username, "temp_token": temp_token})
         if the_user_we_found != None:
             from_ = username
             to = body.get("to")
             text = body.get("text")
             if to and text:
                 result["status"] = "ok"
                 update_an_conversation(from_, to, text)
\end{lstlisting}


\subsection{步骤 2}
另外，我们需要创建一个新的数据结构来保存聊天记录。

如果from=a-user，to=b-user，文本=“hi”，则数据结构为：
\begin{lstlisting}
     [
         {
             "conversation_name": "a-user_b-user",
             "history": [
                 {
                     "date": "2020.3.29",
                     "from": "a-user",
                     "to": "b-user",
                     "text": "hi",
                 },
             ]
         }
     ]
\end{lstlisting}

这样，我们可以基于两个用户的姓名、日期，获得一系列聊天记录。

有了这些信息，建立聊天视图就不会困难了。

这是处理此过程的函数：
\begin{lstlisting}
     def update_an_conversation(from_, to_, text):
         date = datetime.datetime.utcnow()
         conversation_name = "_".join(list(sorted([from_, to_])))
         conversation = conversations.find_one({"conversation_name": conversation_name})
         if conversation == None:
             create_an_conversation(from_, to_, text, date)
         else:
             conversations.find_one_and_update({"_id": conversation.get("_id")}, {
                 "$addToSet": {
                     "history": {
                         "date": date,
                         "from": from_,
                         "to": to_,
                         "text": text,
                     }
                 }
             })
     
     def create_an_conversation(from_, to_, text, date):
         conversation_name = "_".join(list(sorted([from_, to_])))
         conversation = {
             "conversation_name": conversation_name,
             "history": [
                 {
                     "date": date,
                     "from": from_,
                     "to": to_,
                     "text": text,
                 }
             ]
         }
         conversations.insert_one(conversation)
\end{lstlisting}


\section{接收信息}
我们需要定义一个API，以允许用户获取其聊天记录。

通过分析聊天记录，用户还可以知道他们是否收到其他人的新消息。

对于客户端而言，这非常简单，因为如果新的聊天记录与旧的聊天记录不同，则意味着它收到了新消息。

\subsection{步骤 1}
我想让这个API有1个参数：target

当目标==“空字符串”时，我们返回与此用户相关的所有聊天记录。

当目标==“另一个用户”时，我们仅返回另一个用户与该用户之间发生的聊天记录。

我用到的代码是：
\begin{lstlisting}
    target = body.get("target")
    if target == "" or target:
        got = get_conversation(username, target)
        if got == None:
            result["status"] = f"wrong: no record with {username}_{target}"
        else:
            result["status"] = "ok"
            result["result"] = got
\end{lstlisting}

\subsection{步骤 2}
此过程全都涉及查找与此用户相关的历史聊天记录。

如果用户提供了目标用户，我们可以得到一个唯一的字符串，表示存储在数据库中的会话之一。

或者，如果用户未向我们提供特定的目标用户，我们仍然可以使用正则表达式来获取包括该用户的所有对话。

我用到的代码是：
\begin{lstlisting}
    def get_conversation(username, target):
        data = None
        if target == "" or target:
            if target != "":
                conversation_name = "_".join(list(sorted([username, target])))
                conversation = conversations.find_one({"conversation_name": conversation_name})
                if conversation != None:
                    data = [conversation]
    
            else:
                regex = "(" + username+"_.*)|(.*_"+username+")"
                many_conversations = conversations.find({"conversation_name": {"$regex": regex}})
                if many_conversations != None:
                    result = []
                    for conversation in many_conversations:
                        result.append(conversation)
                    data = result
        return data
\end{lstlisting}