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

时间:2013-03-07 13:47:59

标签: jira jira-plugin zendesk

我们正在考虑使用Jira进行错误跟踪,并将其与Git集成,以便将错误修复与版本处理相连接。

您是否也推荐Jira用于客户支持,或者我们是否应该为此找到另一个类似Zendesk的系统?我知道有可能以某种方式将Hipchat与Jira集成以实现与客户的聊天功能,但是Jira对于客户服务来说太复杂了吗?你有什么经历?

1 个答案:

答案 0 :(得分:16)

我们使用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小时通知

  • 创建了一个名为“Open since”的自定义字段 - “日期时间”字段,用于保存问题的打开时间。
  • 创建了一个名为“通知”的自定义字段 - 一个只读文本字段。
  • 使用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小时,发送提醒电子邮件,并将其标记为已通知,因此只会发送一种类型的提醒。< / LI>

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)