写代码的套路

作为一个半道转行的人来说,干过运维、做过网工、当过讲师,也写过代码,代码写的少,非专业的。 不过往后我还是希望往运维开发方向发展。现在呢,每天都坚持写写代码。代码写的多了,自然就有感觉了。

现在看我之前写的一些代码啊,现在自己过来看我都觉得恶心,来给各位看官瞧一瞧,啥是烂代码,之前为了实现个自动化回复工单的程序,我就洋洋洒洒写了近1000行代码,其中有一个方法是用来检查是否有新工单的。他大概是这样的,判断工单是否有更新的情况下拿到标题去判断用户的问题,然后回复客户,老实讲这个程序当时就是随便写写的,写完跑一下,唉,可以用,于是就搞个服务器7*24小时挂着跑。一开始只是想着匹配几个标题去判断,就直接把要回复的内容都写代码里面了。后来加入的东西越来越多,我的妈呀,每次改个代码,看着这一堆中文夹在程序中,我就感到无比的恶心。而且,这个 if 语句判断也写的有问题。

    def check_ticket_new(self):
          try:
              tickets_id =  self.views_new_tickets()[0]
              organization_id = self.views_new_tickets()[1]
              if tickets_id is not None:
                  userlist = self.users_admin_list()
                  for id in tickets_id:
                      comments = self.list_comments(id)
                      auth_name = comments[2]
                      author_id = comments[0]
                      ticket_info = self.ticketid(id)[0]
                      content = comments[-2]
                      check_quota_value  = self.check_quota(auth_name)
                      self.kf5_info("check_quota {}".format(str(check_quota_value)))
                      if author_id in userlist:
                          pass
                      else:
                          for organization_id in organization_id:

                              if ticket_info == "确认退余额":
                                  pass
                              elif ticket_info is not None and ticket_info[0:4] == "面试作业" :
                                  self.put_new_comments(id, "open", """您好,请点击这里进行<a href="https://c.163yun.com/dashboard#/m/certify/" target="_blank"> 个人实名认证<a/>, 提交后将会在一个工作日内完成认证的审核以及代金券的发放。在此之前,请您仔细阅读以下内容:
                                                               <br>1.代金券只能创建按量付费模式的服务;
                                                               <br>2.如因网易云的问题导致的服务不可用可以提交工单反馈给我们,除此之外我们不解答您任何关于作业中的创建服务以及配置或连接访问等问题,请独立完成作业,遇到问题请自行解决;
                                                               <br>3.您需要根据作业要求在限定时间内完成并保留资源保证我们检查作业时是能够访问到您搭建的服务;
                                                               <br>4.如因代金券余额不足,可以提交工单再次申请代金券,注意工单标题为[面试人:XXX,代金券不足,需要再次申请代金券完成作业];
                                                               <br>5.该工单状态会设置为[受理中],待处理完毕后会回复该工单给您答复处理结果。在此期间,请勿回复该工单;
                                                               <br>
                                                               <br> 祝您面试顺利!网易云期待您的加入!

                                                           """)
                              elif ticket_info is not None and re.match(r'.{0,}(退款|提现|退余额|余额转出|充值错误|余额退款|钱可以退吗)', ticket_info):
                                  self.put_new_comments(id, "pending","""
                                                       您好,尊敬的用户,为更好的完善网易云基础服务用户体验和服务质量,并继续跟进您的退款请求,请完成以下问卷:
                                                       <br>1. 您是通过什么渠道知道网易云基础服务?
                                                       <br>2. 您是否使用过其他公司的云产品?分别有哪些?
                                                       <br>3. 您此次退款的原因?
                                                       <br>4. 您认为网易云基础服务需要优化的地方?
                                                       <br><br>感谢您对网易云基础服务的支持,期待您的回复,谢谢!
                                                       """)
                              elif ticket_info is not None and re.match(r'.{0,}(镜像仓库|推送|docker push|docker login|容器仓库|仓库)',ticket_info):
……

上面这段风骚的代码也能干活,但是他非常不美观 、整洁。超级恶心。我确定这是我写的垃圾代码。这段代码是出自于网易云的一个客服之手,希望各位轻喷。如果是个开发写的这么烂的代码,估计早就被开除了。

在程序设计中有个东西叫 MVC,就是模型 视图 和控制,就和你写网页一样,html 单独写个文件,css 单独写个文件,js 单独写个文件,这样的好处是,容易维护,而且代码看着清晰。你要都整一块,就像我上面这段烂代码一样。

  • 控制器(Controller)- 负责转发请求,对请求进行处理。
  • 视图(View) - 界面设计人员进行图形界面设计。
  • 模型(Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

后来实在看不下去这个代码了,本身我也不是专业写代码的,就是觉得工作中有些活可以做成自动化,于是就自己学自己实现了。也在不断学习中吧。而且感觉写代码的是件非常有意思的事情,我讨厌枯燥无味的重复而又单调的工作。

于是重新写了一遍,我把要回复的内容,组成一个json 文件,类似这样

{
  "reply":[
      {
    "id":1,
    "status":"new",
    "source": "ticket",
    "rule":"^面试作业",
    "reply_status":"open",
    "msg":"您好,请点击这里进行<a href='https://c.163yun.com/dashboard#/m/certify/' target='_blank'> 个人实名认证<a/>, 提交后将会在一个工作日内完成认证的审核以及代金券的发放。在此之前,请您仔细阅读以下内容: <br>1.代金券只能创建按量付费模式的服务; <br>2.如因网易云的问题导致的服务不可用可以提交工单反馈给我们,除此之外我们不解答您任何关于作业中的创建服务以及配置或连接访问等问题,请独立完成作业,遇到问题请自行解决; <br>3.您需要根据作业要求在限定时间内完成并保留资源保证我们检查作业时是能够访问到您搭建的服务; <br>4.如因代金券余额不足,可以提交工单再次申请代金券,注意工单标题为[面试人:XXX,代金券不足,需要再次申请代金券完成作业]; <br>5.该工单状态会设置为[受理中],待处理完毕后会回复该工单给您答复处理结果。在此期间,请勿回复该工单; <br> <br> 祝您面试顺利!网易云期待您的加入!"
      },
    {
      "id":2,
      "status":"new",
      "source": "ticket",
      "reply_status":"pending",
      "rule":".{0,}(发票|开票)",
      "msg":"您好,根据您的问题,猜您可能想了解:<br> <strong>1.发票提交后多久寄出</strong><br>答:发票一般从您提交到开出需要5-7个工作日。<br><strong>2.为什么我刚充的钱发票管理里面不显示?</strong><br>答: 发票的金额需要是已经消费的金额才可以申请,并且本月的(预付费和后付费)消费要到次月2号才可以申请发票。<br><strong>3.开发票有最低金额限制吗?寄送发票免快递费吗?</strong><br>答: 开具发票目前暂无金额限制,寄送发票我们都是免费的。<br><strong>4.在哪里开发票?</strong><br>答:您可以在控制台找到业务支持-业务消费-发票管理,点击申请发票,或直接点击跳转到<a href='https://c.163yun.com/dashboard#/m/account/expense/invoice/' target='_blank'>发票管理</a> 页面。<br><strong>5.怎么开具增值税专用发票?</strong><br>答:通过企业认证和专票信息审核的用户可以开具增值税专用发票;<br><strong>6. 为什么我不能开具发票?</strong><br>答:可能出现的情况:1.账户欠费情况下不能申请发票。2.本月消费需要次月初才能开具发票。3.您未使用网易云基础服务的产品,例如使用的是云信的服务,则需联系云信申请发票。<br><strong>7.开具发票需求注意什么?</strong><br>答:应国家税务总局要求,自2017年7月1日起,若开具增值税普通发票,企业类型必须具备税号,否则发票将无法用于企业报销。"
  },

   {
      "id":3,
      "status":"new",
      "source": "ticket",
      "reply_status":"pending",
      "rule":".{0,}(修改|更改)安全手机",
      "msg":"您好,因安全手机是用于云计算基础服务进行资源关键操作的安全验证的,如无法自助修改安全手机,请根据如下情况提交对应的资料。审核通过后可以协助您修改,我们会在 1-2 个工作日之内给您处理: <br><strong>对于个人用户,请提交以下资料:</strong><br> 1.请工单提交注册时的身份证反面照片(有国徽的那面)以及手持身份证正面(含个人照片的那面),并提供原安全手机号和需要修改的安全手机号<br> <strong>对于企业用户,需要您完善并打印以下模板内容,并加盖公司章拍照上传给我们:</strong>                 <br> 公司名称:xxx公司                         <br> 问题描述:因 xxx 原因无法自助修改安全手机,需要网易云协助修改安全手机。<br> 修改内容:<br> 原安全手机:xxx   修改为:xxx"
  },

然后加个方法

# 读取回复内容
    def read_json_config(self):
        with open('/opt/kf5/robot.json', 'r') as f:
            data = json.load(f)
            status = [(d['status']) for d in data['reply']]
            source = [(d['source']) for d in data['reply']]
            rule = [(d['rule']) for d in data['reply']]
            reply_status = [(d['reply_status']) for d in data['reply']]
            msg = [(d['msg']) for d in data['reply']]
            return [status,source,rule,reply_status,msg]

然后重写 check_ticket_new 方法,大概是下面这个样子。对比下看看,是不是好看多了。

# 判断工单是否是客服回复以及是否超过规定时间, SLA 5分钟响应,4小时客户未回复要发送消息通知用户回复工单,24小时客户未回复关闭工单!
    # 判断新工单是否有人回复,如果没有受理则自动回复用户一条信息,确保 SLA 不超时
    def check_ticket_new(self):
        tickets_id = self.views_new_tickets()[0]
        organization_id = self.views_new_tickets()[1]
        read_config = self.read_json_config()
        status = read_config[0]
        source = read_config[1]
        rule = read_config[2]
        reply_status = read_config[3]
        msg = read_config[4]
        if tickets_id is  None and organization_id is None:
            return
        userlist = self.users_admin_list()
        if userlist is None:
            return
        for id in tickets_id:
            comments = self.list_comments(id)
            auth_name = comments[2]
            author_id = comments[0]
            ticket_info = self.ticketid(id)[0]
            content = comments[-2]
            check_quota_value = self.check_quota(auth_name)
            if comments and auth_name and author_id and ticket_info and content is None:
                return
            if author_id in userlist:
                return
            for i in range(len(organization_id)):
                organization_id = organization_id[i]
                print(organization_id)
                print(ticket_info)
                if ticket_info == "确认退余额":
                    pass
                if organization_id is None:
                    organization_id = "NOAUTH"
                if check_quota_value == "LAJI" and re.match(r'.{0,}(配额)', ticket_info):
                    check_quota_value = "LAJI"
                for info in range(len(status)):
                    print(info)
                    if check_quota_value == rule[info]:
                        self.put_new_comments(id, reply_status[info],msg[info])
                        return
                    if re.search(rule[info],ticket_info):
                        self.put_new_comments(id, reply_status[info],msg[info])
                        return
                    if organization_id == rule[info]:
                        self.put_new_comments(id, reply_status[info], msg[info])
                        return
                self.put_new_comments(id, "open", "您好,您的问题已收到,我们正在处理中!")

写代码其实有很多套路的。比如

  • 变量名:要么驼峰式命名,类似这样 agentStatus,要么下划线 agent_status
  • 变量名要有意义,尽量使用英文。
  • 要有注释。
  • 如果层级特别多,把他提炼出一个函数或方法
  • 一个方法或函数只做一件事。
  • 多去 github 读一读优秀的代码。
  • 避免写多层嵌套,例如 下面这段

代码

def do_offline(self,headers=headers):

       online_offline_url = "https://yun163.kf5.com/apiv2/kchat/agent/availabilities.json"
       chats_url = "https://yun163.kf5.com/apiv2/kchat/monitor/chats.json"
       querystring = {"agent_id": "2775056", "status": "offline"}
       while 1:
           chats_response = requests.request("GET", chats_url, headers=headers)
           if chats_response.status_code == 200:
               ch_json = chats_response.json()
               if len(ch_json['chats']) == 0:
                       offline_time = str(time.strftime("%H%M", time.localtime()))
                       print(offline_time)
                       offline_area = [1130, 1730, 2000]
                       if int(offline_time) in offline_area:
                           offline_response = requests.request("PUT", online_offline_url, headers=headers,params=querystring)
                           if offline_response.status_code == 200:
                               offline_json = offline_response.json()
                               offline_name = offline_json['agent']['name']
                               if offline_json['agent']['webStatus'] == "offline" and offline_json['agent']['appStatus'] == "offline":
                                   self.send_message_wchat("通知",offline_name + " Web 端和 APP 端都已下线")
                               if offline_json['agent']['webStatus'] == "offline" and offline_json['agent']['appStatus'] != "offline":
                                   self.send_message_wchat("通知",offline_name + " Web 端已下线但是 APP 端未下线,请在手机 APP 操作")
           time.sleep(10)

看,都斜歪到姥姥家了,这种代码就特别难看,可以多使用逆向思维,比如判断状态是不是200 ,你直接判断状态不是200 直接就 return 掉。这样就不用 else 了,上面的代码修改下

# 设置IM 下线时间
    def do_offline(self, headers=headers):
        online_offline_url = "https://yun163.kf5.com/apiv2/kchat/agent/availabilities.json"
        chats_url = "https://yun163.kf5.com/apiv2/kchat/monitor/chats.json"
        querystring = {"agent_id": "2775056", "status": "offline"}
        while 1:
            chats_response = requests.request("GET", chats_url, headers=headers)
            if chats_response.status_code != 200:
                return
            ch_json = chats_response.json()
            if len(ch_json['chats']) != 0:
                return
            offline_time = str(time.strftime("%H%M", time.localtime()))
            print(offline_time)
            offline_area = [1130, 1720, 2000]
            if int(offline_time) in offline_area is False:
                return
            offline_response = requests.request("PUT", online_offline_url, headers=headers,params=querystring)
            if offline_response.status_code != 200:
                return
            offline_json = offline_response.json()
            offline_name = offline_json['agent']['name']
            web_status =  offline_json['agent']['webStatus']
            app_status = offline_json['agent']['appStatus']
            if web_status == "offline" and  app_status == "offline":
                self.send_message_wchat("通知", offline_name + " Web 端和 APP 端都已下线")
            if web_status == "offline" and app_status == "offline":
                return
            if self.offline_all() == True:
                self.send_message_wchat("通知", offline_name + " 所有端已全部下线!")
                return
            self.send_message_wchat("通知", offline_name + " Web 端已下线但是 APP 端未下线,请在手机 APP 操作")
            time.sleep(30)

嗯,任重而道远,需要学习的地方还很多,最近在看《代码整洁之道》

先把套路学会了,后面在学下 python web 开发。