Jira 用于错误跟踪和客户支持?

标签 jira jira-plugin zendesk

我们正在考虑使用 Jira 进行错误跟踪,并将其与 Git 集成以将错误修复与版本处理联系起来。

您是否也推荐 Jira 用于客户支持,还是我们应该为此寻找另一个系统,例如 Zendesk?我知道有可能以某种方式将 Hipchat 与 Jira 集成以启用与客户的聊天功能,但 Jira 是否太复杂,客户服务无法处理?你的经验是什么?

最佳答案

我们使用 Jira 来提供客户支持,但我们发现 Jira 缺少为此所需的许多必备功能。这就是我们做出许多改变的原因。

总而言之,我们对自己的选择感到非常满意,并且通过使用 Jira 而不是其他解决方案,我们设法节省了大量资金。

这是我们所做的主要更改,这将向您展示缺少的内容,而另一方面向您展示通过一点点编程,Jira 可以做任何事情:)

注意:下面编写的脚本应附加到工作流转换。脚本是使用 Jython 编写的,所以需要安装才能使用。

通过电子邮件创建问题

Jira 仅向 Jira 用户发送电子邮件。由于我们不想为处理支持的每个人都创建一个用户,因此我们改用匿名用户,并使用脚本向他们发送电子邮件。

首先,将 Jira 设置为 create issues from emails .比,使用 Script Runner pluging将客户的电子邮件和姓名保存到自定义字段。 .代码:

from com.atlassian.jira import ComponentManager
import re
cfm = ComponentManager.getInstance().getCustomFieldManager()

# read issue description
description = issue.getDescription()
if (description is not None) and ('Created via e-mail received from' in description):
    # extract email and name:
    if ('<' in description) and ('>' in description):
        # pattern [Created via e-mail received from: name <email@company.com>]
        # split it to a list
        description_list = re.split('<|>|:',description)
        list_length = len(description_list)
        for index in range(list_length-1, -1, -1):
            if '@' in description_list[index]:
                customer_email = description_list[index]
                customer_name = description_list[index - 1]
                break
    else:
        # pattern [Created via e-mail received from: email@company.com]
        customer_name = "Sir or Madam"
        # split it to a list  
        description_list = re.split(': |]',description)
        list_length = len(description_list)
        for index in range(list_length-1, -1, -1):
            if '@' in description_list[index]:
                customer_email = description_list[index]
                break

    # if the name isn't in the right form, switch it's places:
    if (customer_name[0] == '"') and (customer_name[-1] == '"') and (',' in customer_name):
        customer_name = customer_name[1:-1]
        i =  customer_name.index(',')
        customer_name = customer_name[i+2:]+" "+customer_name[:i]

    # insert data to issue fields
    issue.setCustomFieldValue(cfm.getCustomFieldObject("customfield_10401"),customer_email)
    issue.setCustomFieldValue(cfm.getCustomFieldObject("customfield_10108"),customer_name)

发送客户 issue created通知

使用以下脚本发送邮件:
import smtplib,email
from smtplib import SMTP 
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
import os
import re
from com.atlassian.jira import ComponentManager

customFieldManager = ComponentManager.getInstance().getCustomFieldManager()
cfm = ComponentManager.getInstance().getCustomFieldManager()

# read needed fields from the issue
key = issue.getKey()
#status = issue.getStatusObject().name
summary = issue.getSummary()
project = issue.getProjectObject().name

# read customer email address
toAddr = issue.getCustomFieldValue(cfm.getCustomFieldObject("customfield_10401"))
# send mail only if a valid email was entered
if (toAddr is not None) and (re.match('[A-Za-z0-9._%+-]+@(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,4}',toAddr)):
    # read customer name
    customerName = issue.getCustomFieldValue(cfm.getCustomFieldObject("customfield_10108"))
    # read template from the disk
    template_file = 'new_case.template'
    f = open(template_file, 'r')
    htmlBody = ""
    for line in f:
        line = line.replace('$$CUSTOMER_NAME',customerName)
        line = line.replace('$$KEY',key)
        line = line.replace('$$PROJECT',project)
        line = line.replace('$$SUMMARY',summary)
        htmlBody += line + '<BR>'


    smtpserver = 'smtpserver.com'
    to = [toAddr]
    fromAddr = 'jira@email.com'
    subject = "["+key+"] Thank You for Contacting Support team"
    mail_user = 'jira@email.com'
    mail_password = 'password'

    # create html email
    html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
    html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">'
    html +='<body style="font-size:12px;font-family:Verdana">'
    html +='<p align="center"><img src="http://path/to/company_logo.jpg" alt="logo"></p> '
    html +='<p>'+htmlBody+'</p>'
    html +='</body></html>'

    emailMsg = email.MIMEMultipart.MIMEMultipart('alternative')
    emailMsg['Subject'] = subject
    emailMsg['From'] = fromAddr
    emailMsg['To'] = ', '.join(to)
    emailMsg.attach(email.mime.text.MIMEText(html,'html'))

    # Send the email
    s = SMTP(smtpserver) # ip or domain name of smtp server
    s.login(mail_user, mail_password)   
    s.sendmail(fromAddr, [to], emailMsg.as_string())
    s.quit()

    # add sent mail to comments
    cm = ComponentManager.getInstance().getCommentManager()
    email_body = htmlBody.replace('<BR>','\n')
    cm.create(issue,'anonymous','Email was sent to the customer ; Subject: '+subject+'\n'+email_body,False)
new_case.template的内容:
Dear $$CUSTOMER_NAME,

Thank you for contacting support team.

We will address your case as soon as possible and respond with a solution very quickly.

Issue key $$KEY has been created as a reference for future correspondence.

If you need urgent support please refer to our Frequently Asked Questions page at http://www.example.com/faq.

Thank you,

Support Team


Issue key: $$KEY
Issue subject: $$PROJECT
Issue summary: $$SUMMARY

问题提醒 - 24/36/48 小时通知
  • 创建了一个名为“自打开时间”的自定义字段 - 一个“日期时间”字段,用于保存问题已打开的时间。
  • 创建了一个名为“通知”的自定义字段 - 一个只读文本字段。
  • 使用 Script Runner pluging ,我创建了一个后置函数并将其放置在每个进入“打开”状态的转换上。这是为了保持问题开放时间。

  • 编码:
    from com.atlassian.jira import ComponentManager
    from datetime import datetime
    
    opend_since_field = "customfield_10001"
    
    # get opened since custom field:
    cfm = ComponentManager.getInstance().getCustomFieldManager()
    # get current time
    currentTime = datetime.today()
    # save current time
    issue.setCustomFieldValue(cfm.getCustomFieldObject(opend_since_field),currentTime)
    
  • 我创建了一个新过滤器来获取超过 24 小时开放的问题列表:

  • JQL:
    project = XXX AND status= Open ORDER BY updated ASC, key DESC
    
  • 最后 - 我使用了 Jira remote API - XML-RPC编写计划每 5 分钟运行一次的 Python 脚本的方法。剧本
    读取过滤器发出的所有信息,拉取所有超过 24 小时/36 小时/48 小时的“打开”状态,发送提醒电子邮件,并将它们标记为已通知,因此每种类型只会发送一个提醒。

  • python 代码:
    #!/usr/bin/python
    
    # Refer to the XML-RPC Javadoc to see what calls are available:
    # http://docs.atlassian.com/software/jira/docs/api/rpc-jira-plugin/latest/com/atlassian/jira/rpc/xmlrpc/XmlRpcService.html
    # /home/issues_reminder.py
    
    import xmlrpclib
    import time
    from time import mktime
    from datetime import datetime
    from datetime import timedelta
    import smtplib,email
    from smtplib import SMTP 
    from email.MIMEMultipart import MIMEMultipart
    from email.MIMEBase import MIMEBase
    from email.MIMEText import MIMEText
    from email import Encoders
    
    # Jira connction info
    server = 'https://your.jira.com/rpc/xmlrpc'
    user = 'user'
    password = 'password'
    filter = '10302' # Filter ID
    # Email definitions 
    smtpserver = 'mail.server.com'
    fromAddr = 'support@your.jira.com'
    mail_user = 'jira_admin@your.domain.com'
    mail_password = 'password'
    toAddr = 'support@your.domain.com'
    mysubject = "hrs Issue notification!!!"
    opend_since_field = "customfield_10101"
    
    
    COMMASPACE = ', '
    def email_issue(issue,esc_time):
        # create html email
        subject = '['+issue+'] '+esc_time+mysubject
        html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
        html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">'
        html +='<body style="font-size:12px;font-family:Verdana">'
        html +='<p align="center"><img src="your_logo.jpg" alt="logo" height="43" width="198"></p> '
        html +='<p> The issue ['+issue+'] is open for over '+esc_time+' hours.</p>'
        html +='<p> A link to view the issue: https://your.jira.com/browse/'+issue+'.</p>'
        html +='<BR><p> This is an automated email sent from Jira.</p>'
        html +='</body></html>'
        emailMsg = email.MIMEMultipart.MIMEMultipart('alternative')
        emailMsg['Subject'] = subject
        emailMsg['From'] = fromAddr
        emailMsg['To'] = toAddr
        emailMsg.attach(MIMEText(html, 'html'))
        # Send the email
        emailserver = SMTP(smtpserver) # ip or domain name of smtp server
        emailserver.login(mail_user, mail_password)
        emailserver.sendmail(fromAddr, [toAddr], emailMsg.as_string())
        emailserver.quit()
        return
    
    
    s = xmlrpclib.ServerProxy(server)
    auth = s.jira1.login(user, password)
    
    esc12List = []
    esc24List = []
    esc48List = []
    
    
    issues = s.jira1.getIssuesFromFilter(auth, filter)
    print "Modifying issue..."
    for issue in issues:
            creation = 0;
            # get open since time
            for customFields in issue['customFieldValues']:
                    if customFields['customfieldId'] == opend_since_field :
                            print "found field!"+  customFields['values']
                            creation = customFields['values']
            if (creation == 0):
                    creation = issue['created']
                    print "field not found"
        creationTime = datetime.fromtimestamp(mktime(time.strptime(creation, '%d/%b/%y %I:%M %p')))
        currentTime = datetime.fromtimestamp(mktime(time.gmtime()))
        delta = currentTime - creationTime
        esc12 = timedelta(hours=12)
        esc24 = timedelta(hours=24)
        esc48 = timedelta(hours=48)
        print "\nchecking issue "+issue['key']
        if (delta < esc12):
            print "less than 12 hours"
            print "not updating"
            continue
        if (delta < esc24):
            print "less than 24 hours"
            for customFields in issue['customFieldValues']:
                if customFields['customfieldId'] == 'customfield_10412':
                    if customFields['values'] == '12h':
                        print "not updating"
                        break
                    else:
                        print "updating !!!"
                        s.jira1.updateIssue(auth, issue['key'], {"customfield_10412": ["12h"]})
                        esc12List.append(issue['key'])
                        break
            continue
        if (delta < esc48):
            print "less than 48 hours"
            for customFields in issue['customFieldValues']:
                if customFields['customfieldId'] == 'customfield_10412':
                    if customFields['values'] == '24h':
                        print "not updating"
                        break
                    else:
                        print "updating !!!"
                        s.jira1.updateIssue(auth, issue['key'], {"customfield_10412": ["24h"]})
                        esc24List.append(issue['key'])
                        break
            continue
        print "more than 48 hours"
        for customFields in issue['customFieldValues']:
            if customFields['customfieldId'] == 'customfield_10412':
                if customFields['values'] == '48h':
                    print "not updating"
                    break
                else:
                    print "updating !!!"
                    s.jira1.updateIssue(auth, issue['key'], {"customfield_10412": ["48h"]})
                    esc48List.append(issue['key'])
                    break
    
    for key in esc12List:
        email_issue(key,'12')
    for key in esc24List:
        email_issue(key,'24')
    for key in esc48List:
        email_issue(key,'48')
    

    这种方法的主要优点是高度可定制,并且通过将数据保存到自定义字段,可以轻松创建过滤器和报告以显示已打开很长时间的问题。

    上报给开发团队

    创建新的过渡 - Escalate .这将为开发团队创建一个问题,并将新问题链接到支持问题。添加以下帖子功能:
    from com.atlassian.jira.util import ImportUtils
    from com.atlassian.jira import ManagerFactory
    from com.atlassian.jira.issue import MutableIssue
    from com.atlassian.jira import ComponentManager
    from com.atlassian.jira.issue.link import DefaultIssueLinkManager
    from org.ofbiz.core.entity import GenericValue;
    
    # get issue objects
    issueManager = ComponentManager.getInstance().getIssueManager()
    issueFactory = ComponentManager.getInstance().getIssueFactory()
    authenticationContext = ComponentManager.getInstance().getJiraAuthenticationContext()
    issueLinkManager = ComponentManager.getInstance().getIssueLinkManager()
    customFieldManager = ComponentManager.getInstance().getCustomFieldManager()
    userUtil = ComponentManager.getInstance().getUserUtil()
    projectMgr = ComponentManager.getInstance().getProjectManager()
    customer_name = customFieldManager.getCustomFieldObjectByName("Customer Name")
    customer_email = customFieldManager.getCustomFieldObjectByName("Customer Email")
    escalate = customFieldManager.getCustomFieldObjectByName("Escalate to Development")
    
    if issue.getCustomFieldValue(escalate) is not None:
        # define issue
        issueObject = issueFactory.getIssue()
        issueObject.setProject(projectMgr.getProject(10000))
        issueObject.setIssueTypeId("1") # bug
        # set subtask attributes
        issueObject.setSummary("[Escalated from support] "+issue.getSummary())
        issueObject.setAssignee(userUtil.getUserObject("nadav"))
        issueObject.setReporter(issue.getAssignee())
        issueObject.setDescription(issue.getDescription())
        issueObject.setCustomFieldValue(customer_name, issue.getCustomFieldValue(customer_name)+" "+issue.getCustomFieldValue(customer_email))
        issueObject.setComponents(issue.getComponents())
        # Create subtask 
        subTask = issueManager.createIssue(authenticationContext.getUser(), issueObject)
        # Link parent issue to subtask
        issueLinkManager.createIssueLink(issueObject.getId(),issue.getId(),10003,1,authenticationContext.getUser())
        # Update search indexes
        ImportUtils.setIndexIssues(True);
        ComponentManager.getInstance().getIndexManager().reIndex(subTask)
        ImportUtils.setIndexIssues(False)
    

    搬到销售部

    创造新的转变 - Move to sales .许多支持电话最终都是销售电话,这会将问题转移到销售团队。添加以下帖子功能:
    from com.atlassian.jira.util import ImportUtils
    from com.atlassian.jira.issue import MutableIssue
    from com.atlassian.jira import ComponentManager
    
    customFieldManager = ComponentManager.getInstance().getCustomFieldManager()
    userUtil = ComponentManager.getInstance().getUserUtil()
    
    issue.setStatusId("1");
    issue.setAssignee(userUtil.getUserObject("John"))
    issue.setSummary("[Moved from support] "+issue.getSummary())
    issue.setProjectId(10201);
    issue.setIssueTypeId("35");
    ImportUtils.setIndexIssues(True);
    ComponentManager.getInstance().getIndexManager().reIndex(issue)
    ImportUtils.setIndexIssues(False)
    
    
    # add to comments
    from time import gmtime, strftime
    time = strftime("%d-%m-%Y %H:%M:%S", gmtime())
    cm = ComponentManager.getInstance().getCommentManager()
    currentUser = ComponentManager.getInstance().getJiraAuthenticationContext().getUser().toString()
    cm.create(issue,currentUser,'Email was moved to Sales at '+time,False)
    

    关于Jira 用于错误跟踪和客户支持?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15272864/

    相关文章:

    jira - 修改 Jira Cloud 实例的 Jira Server 附加组件

    jira - 如何在 GreenHopper 看板上的卡片上显示故事点?

    javascript - 在不使用额外助手的情况下限制 Handlebars #each 中的结果

    tfs - TFS与VSTS的 future

    javascript - 用javascript附加&lt;script&gt;内容(zendesk的登录)

    jira - 如何在 Jira 注释中的代码中添加 JSON 突出显示?

    javascript - Jira 小工具 - 在配置屏幕上重新加载 AJAX

    JIRA rest api 来获取事件流

    java - 自定义 Jira 插件 : Cannot Update an issue

    java - 使用 servlet 时如何使用 system.out.println