mirror of https://github.com/jumpserver/jumpserver
feat(ticket): 工单关闭生成 Comment
parent
c3c5801d2e
commit
f6a4253936
|
@ -139,7 +139,7 @@ class AuthMixin:
|
|||
|
||||
def get_ticket_or_create(self, confirm_setting):
|
||||
ticket = self.get_ticket()
|
||||
if not ticket or ticket.status == ticket.STATUS_CLOSED:
|
||||
if not ticket or ticket.status == ticket.STATUS.CLOSED:
|
||||
ticket = confirm_setting.create_confirm_ticket(self.request)
|
||||
self.request.session['auth_ticket_id'] = str(ticket.id)
|
||||
return ticket
|
||||
|
@ -148,12 +148,12 @@ class AuthMixin:
|
|||
ticket = self.get_ticket()
|
||||
if not ticket:
|
||||
raise errors.LoginConfirmOtherError('', "Not found")
|
||||
if ticket.status == ticket.STATUS_OPEN:
|
||||
if ticket.status == ticket.STATUS.OPEN:
|
||||
raise errors.LoginConfirmWaitError(ticket.id)
|
||||
elif ticket.action == ticket.ACTION_APPROVE:
|
||||
elif ticket.action == ticket.ACTION.APPROVE:
|
||||
self.request.session["auth_confirm"] = "1"
|
||||
return
|
||||
elif ticket.action == ticket.ACTION_REJECT:
|
||||
elif ticket.action == ticket.ACTION.REJECT:
|
||||
raise errors.LoginConfirmOtherError(
|
||||
ticket.id, ticket.get_action_display()
|
||||
)
|
||||
|
|
|
@ -71,7 +71,7 @@ class LoginConfirmSetting(CommonModelMixin):
|
|||
reviewer = self.reviewers.all()
|
||||
ticket = Ticket.objects.create(
|
||||
user=self.user, title=title, body=body,
|
||||
type=Ticket.TYPE_LOGIN_CONFIRM,
|
||||
type=Ticket.TYPE.LOGIN_CONFIRM,
|
||||
)
|
||||
ticket.assignees.set(reviewer)
|
||||
return ticket
|
||||
|
|
|
@ -52,7 +52,7 @@ class ChoiceSetType(type):
|
|||
return self._choices_dict.__getitem__(item)
|
||||
|
||||
def get(self, item, default=None):
|
||||
return self._choices_dict.get(item, default=None)
|
||||
return self._choices_dict.get(item, default)
|
||||
|
||||
@property
|
||||
def choices(self):
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-07-31 19:20+0800\n"
|
||||
"POT-Creation-Date: 2020-08-04 15:33+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||
|
@ -47,7 +47,7 @@ msgid "Name"
|
|||
msgstr "名称"
|
||||
|
||||
#: applications/models/database_app.py:22 assets/models/cmd_filter.py:52
|
||||
#: terminal/models.py:376 terminal/models.py:413 tickets/models/ticket.py:46
|
||||
#: terminal/models.py:376 terminal/models.py:413 tickets/models/ticket.py:40
|
||||
#: users/templates/users/user_granted_database_app.html:35
|
||||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
@ -131,8 +131,8 @@ msgstr "参数"
|
|||
#: applications/models/remote_app.py:39 assets/models/asset.py:224
|
||||
#: assets/models/base.py:240 assets/models/cluster.py:28
|
||||
#: assets/models/cmd_filter.py:26 assets/models/cmd_filter.py:60
|
||||
#: assets/models/group.py:21 common/db/models.py:66 common/mixins/models.py:49
|
||||
#: orgs/models.py:23 orgs/models.py:316 perms/models/base.py:54
|
||||
#: assets/models/group.py:21 common/db/models.py:67 common/mixins/models.py:49
|
||||
#: orgs/models.py:23 orgs/models.py:326 perms/models/base.py:54
|
||||
#: users/models/user.py:530 users/serializers/group.py:35
|
||||
#: users/templates/users/user_detail.html:97
|
||||
#: xpack/plugins/change_auth_plan/models.py:81 xpack/plugins/cloud/models.py:56
|
||||
|
@ -145,9 +145,9 @@ msgstr "创建者"
|
|||
#: applications/models/remote_app.py:42 assets/models/asset.py:225
|
||||
#: assets/models/base.py:238 assets/models/cluster.py:26
|
||||
#: assets/models/domain.py:23 assets/models/gathered_user.py:19
|
||||
#: assets/models/group.py:22 assets/models/label.py:25 common/db/models.py:68
|
||||
#: assets/models/group.py:22 assets/models/label.py:25 common/db/models.py:69
|
||||
#: common/mixins/models.py:50 ops/models/adhoc.py:38 ops/models/command.py:27
|
||||
#: orgs/models.py:24 orgs/models.py:314 perms/models/base.py:55
|
||||
#: orgs/models.py:24 orgs/models.py:324 perms/models/base.py:55
|
||||
#: users/models/group.py:18 users/templates/users/user_group_detail.html:58
|
||||
#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:149
|
||||
msgid "Date created"
|
||||
|
@ -190,7 +190,7 @@ msgstr "基础"
|
|||
msgid "Charset"
|
||||
msgstr "编码"
|
||||
|
||||
#: assets/models/asset.py:148 tickets/models/ticket.py:41
|
||||
#: assets/models/asset.py:148 tickets/models/ticket.py:35
|
||||
msgid "Meta"
|
||||
msgstr "元数据"
|
||||
|
||||
|
@ -380,8 +380,8 @@ msgid "SSH public key"
|
|||
msgstr "SSH公钥"
|
||||
|
||||
#: assets/models/base.py:239 assets/models/gathered_user.py:20
|
||||
#: common/db/models.py:69 common/mixins/models.py:51 ops/models/adhoc.py:39
|
||||
#: orgs/models.py:315
|
||||
#: common/db/models.py:70 common/mixins/models.py:51 ops/models/adhoc.py:39
|
||||
#: orgs/models.py:325
|
||||
msgid "Date updated"
|
||||
msgstr "更新日期"
|
||||
|
||||
|
@ -488,7 +488,7 @@ msgstr "每行一个命令"
|
|||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/forms/asset_permission.py:20
|
||||
#: tickets/serializers/request_asset_perm.py:60
|
||||
#: tickets/serializers/ticket.py:26
|
||||
#: tickets/serializers/ticket.py:30
|
||||
#: users/templates/users/_granted_assets.html:29
|
||||
#: users/templates/users/user_asset_permission.html:44
|
||||
#: users/templates/users/user_asset_permission.html:79
|
||||
|
@ -537,14 +537,14 @@ msgstr "默认资产组"
|
|||
|
||||
#: assets/models/label.py:15 audits/models.py:36 audits/models.py:56
|
||||
#: audits/models.py:69 audits/serializers.py:77 authentication/models.py:46
|
||||
#: authentication/models.py:90 orgs/models.py:16 orgs/models.py:312
|
||||
#: authentication/models.py:90 orgs/models.py:16 orgs/models.py:322
|
||||
#: perms/forms/asset_permission.py:83 perms/forms/database_app_permission.py:38
|
||||
#: perms/forms/remote_app_permission.py:40 perms/models/base.py:49
|
||||
#: templates/index.html:78 terminal/backends/command/models.py:18
|
||||
#: terminal/backends/command/serializers.py:12 terminal/models.py:185
|
||||
#: tickets/models/ticket.py:36 tickets/models/ticket.py:135
|
||||
#: tickets/models/ticket.py:30 tickets/models/ticket.py:137
|
||||
#: tickets/serializers/request_asset_perm.py:61
|
||||
#: tickets/serializers/ticket.py:27 users/forms/group.py:15
|
||||
#: tickets/serializers/ticket.py:31 users/forms/group.py:15
|
||||
#: users/models/user.py:157 users/models/user.py:643
|
||||
#: users/serializers/group.py:20
|
||||
#: users/templates/users/user_asset_permission.html:38
|
||||
|
@ -692,7 +692,7 @@ msgstr "协议重复: {}"
|
|||
msgid "Hardware info"
|
||||
msgstr "硬件信息"
|
||||
|
||||
#: assets/serializers/asset.py:112 orgs/mixins/serializers.py:27
|
||||
#: assets/serializers/asset.py:112 orgs/mixins/serializers.py:26
|
||||
msgid "Org name"
|
||||
msgstr "组织名称"
|
||||
|
||||
|
@ -1014,7 +1014,7 @@ msgid "Reason"
|
|||
msgstr "原因"
|
||||
|
||||
#: audits/models.py:106 tickets/serializers/request_asset_perm.py:59
|
||||
#: tickets/serializers/ticket.py:25 xpack/plugins/cloud/models.py:211
|
||||
#: tickets/serializers/ticket.py:29 xpack/plugins/cloud/models.py:211
|
||||
#: xpack/plugins/cloud/models.py:269
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
@ -1199,7 +1199,7 @@ msgstr "SSH密钥"
|
|||
msgid "Reviewers"
|
||||
msgstr "审批人"
|
||||
|
||||
#: authentication/models.py:56 tickets/models/ticket.py:27
|
||||
#: authentication/models.py:56 tickets/models/ticket.py:23
|
||||
#: users/templates/users/user_detail.html:250
|
||||
msgid "Login confirm"
|
||||
msgstr "登录复核"
|
||||
|
@ -1263,7 +1263,7 @@ msgstr "删除成功"
|
|||
|
||||
#: authentication/templates/authentication/_access_key_modal.html:155
|
||||
#: authentication/templates/authentication/_mfa_confirm_modal.html:53
|
||||
#: templates/_modal.html:22 tickets/models/ticket.py:73
|
||||
#: templates/_modal.html:22 tickets/models/ticket.py:67
|
||||
msgid "Close"
|
||||
msgstr "关闭"
|
||||
|
||||
|
@ -1393,7 +1393,7 @@ msgstr "%(name)s 创建成功"
|
|||
msgid "%(name)s was updated successfully"
|
||||
msgstr "%(name)s 更新成功"
|
||||
|
||||
#: common/db/models.py:67
|
||||
#: common/db/models.py:68
|
||||
msgid "Updated by"
|
||||
msgstr "更新人"
|
||||
|
||||
|
@ -1649,16 +1649,16 @@ msgstr "更新任务内容: {}"
|
|||
msgid "Disk used more than 80%: {} => {}"
|
||||
msgstr "磁盘使用率超过 80%: {} => {}"
|
||||
|
||||
#: orgs/api.py:54
|
||||
#: orgs/api.py:58
|
||||
msgid "Organization contains undeleted resources"
|
||||
msgstr ""
|
||||
|
||||
#: orgs/api.py:58
|
||||
#: orgs/api.py:62
|
||||
msgid "The current organization cannot be deleted"
|
||||
msgstr ""
|
||||
|
||||
#: orgs/mixins/models.py:56 orgs/mixins/serializers.py:26 orgs/models.py:40
|
||||
#: orgs/models.py:311
|
||||
#: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:40
|
||||
#: orgs/models.py:321
|
||||
msgid "Organization"
|
||||
msgstr "组织"
|
||||
|
||||
|
@ -1670,7 +1670,7 @@ msgstr "组织管理员"
|
|||
msgid "Organization auditor"
|
||||
msgstr "组织审计员"
|
||||
|
||||
#: orgs/models.py:313 users/forms/user.py:27 users/models/user.py:499
|
||||
#: orgs/models.py:323 users/forms/user.py:27 users/models/user.py:499
|
||||
#: users/templates/users/_select_user_modal.html:15
|
||||
#: users/templates/users/user_detail.html:73
|
||||
#: users/templates/users/user_list.html:16
|
||||
|
@ -2506,100 +2506,108 @@ msgstr "结束日期"
|
|||
msgid "Args"
|
||||
msgstr "参数"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:42
|
||||
msgid "Ticket closed"
|
||||
msgstr "工单已关闭"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:45
|
||||
#: tickets/api/request_asset_perm.py:44
|
||||
#, python-format
|
||||
msgid "Ticket has %s"
|
||||
msgstr "工单已%s"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:90
|
||||
#: tickets/api/request_asset_perm.py:89
|
||||
msgid "Confirm assets first"
|
||||
msgstr "请先确认资产"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:93
|
||||
#: tickets/api/request_asset_perm.py:92
|
||||
msgid "Confirmed assets changed"
|
||||
msgstr "确认的资产变更了"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:97
|
||||
#: tickets/api/request_asset_perm.py:96
|
||||
msgid "Confirm system-user first"
|
||||
msgstr "请先确认系统用户"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:101
|
||||
#: tickets/api/request_asset_perm.py:100
|
||||
msgid "Confirmed system-user changed"
|
||||
msgstr "确认的系统用户变更了"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:104 xpack/plugins/cloud/models.py:202
|
||||
#: tickets/api/request_asset_perm.py:103 xpack/plugins/cloud/models.py:202
|
||||
msgid "Succeed"
|
||||
msgstr "成功"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:111
|
||||
#: tickets/api/request_asset_perm.py:110
|
||||
msgid "From request ticket: {} {}"
|
||||
msgstr "来自工单申请: {} {}"
|
||||
|
||||
#: tickets/api/request_asset_perm.py:113
|
||||
#: tickets/api/request_asset_perm.py:112
|
||||
msgid "{} request assets, approved by {}"
|
||||
msgstr "{} 申请资产,通过人 {}"
|
||||
|
||||
#: tickets/models/ticket.py:19 tickets/models/ticket.py:75
|
||||
#: tickets/exceptions.py:23
|
||||
msgid "Ticket closed"
|
||||
msgstr "工单已关闭"
|
||||
|
||||
#: tickets/exceptions.py:32
|
||||
msgid "Only assignee can operate ticket"
|
||||
msgstr "只有审批人可以操作工单"
|
||||
|
||||
#: tickets/exceptions.py:37
|
||||
msgid "Ticket can not be operated"
|
||||
msgstr "不能操作该工单"
|
||||
|
||||
#: tickets/models/ticket.py:18 tickets/models/ticket.py:69
|
||||
msgid "Open"
|
||||
msgstr "开启"
|
||||
|
||||
#: tickets/models/ticket.py:20
|
||||
#: tickets/models/ticket.py:19
|
||||
msgid "Closed"
|
||||
msgstr "关闭"
|
||||
|
||||
#: tickets/models/ticket.py:26
|
||||
#: tickets/models/ticket.py:22
|
||||
msgid "General"
|
||||
msgstr "一般"
|
||||
|
||||
#: tickets/models/ticket.py:28
|
||||
#: tickets/models/ticket.py:24
|
||||
msgid "Request asset permission"
|
||||
msgstr "申请资产权限"
|
||||
|
||||
#: tickets/models/ticket.py:33
|
||||
#: tickets/models/ticket.py:27
|
||||
msgid "Approve"
|
||||
msgstr "同意"
|
||||
|
||||
#: tickets/models/ticket.py:34
|
||||
#: tickets/models/ticket.py:28
|
||||
msgid "Reject"
|
||||
msgstr "拒绝"
|
||||
|
||||
#: tickets/models/ticket.py:37 tickets/models/ticket.py:136
|
||||
#: tickets/models/ticket.py:31 tickets/models/ticket.py:138
|
||||
msgid "User display name"
|
||||
msgstr "用户显示名称"
|
||||
|
||||
#: tickets/models/ticket.py:39
|
||||
#: tickets/models/ticket.py:33
|
||||
msgid "Title"
|
||||
msgstr "标题"
|
||||
|
||||
#: tickets/models/ticket.py:40 tickets/models/ticket.py:137
|
||||
#: tickets/models/ticket.py:34 tickets/models/ticket.py:139
|
||||
msgid "Body"
|
||||
msgstr "内容"
|
||||
|
||||
#: tickets/models/ticket.py:42
|
||||
#: tickets/models/ticket.py:36
|
||||
msgid "Assignee"
|
||||
msgstr "处理人"
|
||||
|
||||
#: tickets/models/ticket.py:43
|
||||
#: tickets/models/ticket.py:37
|
||||
msgid "Assignee display name"
|
||||
msgstr "处理人名称"
|
||||
|
||||
#: tickets/models/ticket.py:44
|
||||
#: tickets/models/ticket.py:38
|
||||
msgid "Assignees"
|
||||
msgstr "待处理人"
|
||||
|
||||
#: tickets/models/ticket.py:45
|
||||
#: tickets/models/ticket.py:39
|
||||
msgid "Assignees display name"
|
||||
msgstr "待处理人名称"
|
||||
|
||||
#: tickets/models/ticket.py:76
|
||||
#: tickets/models/ticket.py:70
|
||||
msgid "{} {} this ticket"
|
||||
msgstr "{} {} 这个工单"
|
||||
|
||||
#: tickets/models/ticket.py:87
|
||||
#: tickets/models/ticket.py:85
|
||||
msgid "this ticket"
|
||||
msgstr "这个工单"
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from django.db.transaction import atomic
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.decorators import action
|
||||
|
@ -26,7 +25,7 @@ from ..permissions import IsAssignee
|
|||
|
||||
|
||||
class RequestAssetPermTicketViewSet(JMSModelViewSet):
|
||||
queryset = Ticket.origin_objects.filter(type=Ticket.TYPE_REQUEST_ASSET_PERM)
|
||||
queryset = Ticket.origin_objects.filter(type=Ticket.TYPE.REQUEST_ASSET_PERM)
|
||||
serializer_classes = {
|
||||
'default': serializers.RequestAssetPermTicketSerializer,
|
||||
'approve': EmptySerializer,
|
||||
|
@ -38,10 +37,10 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet):
|
|||
search_fields = ['user_display', 'title']
|
||||
|
||||
def _check_can_set_action(self, instance, action):
|
||||
if instance.status == instance.STATUS_CLOSED:
|
||||
raise TicketClosed(detail=_('Ticket closed'))
|
||||
if instance.status == instance.STATUS.CLOSED:
|
||||
raise TicketClosed
|
||||
if instance.action == action:
|
||||
action_display = dict(instance.ACTION_CHOICES).get(action)
|
||||
action_display = instance.ACTION.get(action)
|
||||
raise TicketActionAlready(detail=_('Ticket has %s') % action_display)
|
||||
|
||||
@action(detail=False, methods=[GET], permission_classes=[IsValidUser])
|
||||
|
@ -72,7 +71,7 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet):
|
|||
@action(detail=True, methods=[POST], permission_classes=[IsAssignee, IsValidUser])
|
||||
def reject(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
action = instance.ACTION_REJECT
|
||||
action = instance.ACTION.REJECT
|
||||
self._check_can_set_action(instance, action)
|
||||
instance.perform_action(action, request.user, self._get_extra_comment(instance))
|
||||
return Response()
|
||||
|
@ -80,7 +79,7 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet):
|
|||
@action(detail=True, methods=[POST], permission_classes=[IsAssignee, IsValidUser])
|
||||
def approve(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
action = instance.ACTION_APPROVE
|
||||
action = instance.ACTION.APPROVE
|
||||
self._check_can_set_action(instance, action)
|
||||
|
||||
meta = instance.meta
|
||||
|
@ -100,10 +99,10 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet):
|
|||
if system_user is None:
|
||||
raise ConfirmedSystemUserChanged(detail=_('Confirmed system-user changed'))
|
||||
|
||||
self._create_asset_permission(instance, assets, system_user, request.user)
|
||||
self._create_asset_permission(instance, assets, system_user)
|
||||
return Response({'detail': _('Succeed')})
|
||||
|
||||
def _create_asset_permission(self, instance: Ticket, assets, system_user, user):
|
||||
def _create_asset_permission(self, instance: Ticket, assets, system_user):
|
||||
meta = instance.meta
|
||||
request = self.request
|
||||
|
||||
|
@ -120,13 +119,12 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet):
|
|||
if date_expired:
|
||||
ap_kwargs['date_expired'] = date_expired
|
||||
|
||||
with atomic():
|
||||
instance.perform_action(instance.ACTION_APPROVE,
|
||||
request.user,
|
||||
self._get_extra_comment(instance))
|
||||
ap = AssetPermission.objects.create(**ap_kwargs)
|
||||
ap.system_users.add(system_user)
|
||||
ap.assets.add(*assets)
|
||||
ap.users.add(user)
|
||||
instance.perform_action(instance.ACTION.APPROVE,
|
||||
request.user,
|
||||
self._get_extra_comment(instance))
|
||||
ap = AssetPermission.objects.create(**ap_kwargs)
|
||||
ap.system_users.add(system_user)
|
||||
ap.assets.add(*assets)
|
||||
ap.users.add(instance.user)
|
||||
|
||||
return ap
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from common.exceptions import JMSException
|
||||
|
||||
|
||||
|
@ -18,12 +20,19 @@ class ConfirmedSystemUserChanged(JMSException):
|
|||
|
||||
|
||||
class TicketClosed(JMSException):
|
||||
pass
|
||||
default_detail = _('Ticket closed')
|
||||
default_code = 'ticket_closed'
|
||||
|
||||
|
||||
class TicketActionAlready(JMSException):
|
||||
pass
|
||||
|
||||
|
||||
class OrgIdRequiredException(JMSException):
|
||||
pass
|
||||
class OnlyTicketAssigneeCanOperate(JMSException):
|
||||
default_detail = _('Only assignee can operate ticket')
|
||||
default_code = 'can_not_operate'
|
||||
|
||||
|
||||
class TicketCanNotOperate(JMSException):
|
||||
default_detail = _('Ticket can not be operated')
|
||||
default_code = 'ticket_can_not_be_operated'
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.10 on 2020-08-04 07:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0002_auto_20200728_1146'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='assignee_display',
|
||||
field=models.CharField(blank=True, default='', max_length=128, null=True, verbose_name='Assignee display name'),
|
||||
),
|
||||
]
|
|
@ -5,6 +5,7 @@ from django.db import models
|
|||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.db.models import ChoiceSet
|
||||
from common.mixins.models import CommonModelMixin
|
||||
from common.fields.model import JsonDictTextField
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
|
@ -13,26 +14,19 @@ __all__ = ['Ticket', 'Comment']
|
|||
|
||||
|
||||
class Ticket(OrgModelMixin, CommonModelMixin):
|
||||
STATUS_OPEN = 'open'
|
||||
STATUS_CLOSED = 'closed'
|
||||
STATUS_CHOICES = (
|
||||
(STATUS_OPEN, _("Open")),
|
||||
(STATUS_CLOSED, _("Closed"))
|
||||
)
|
||||
TYPE_GENERAL = 'general'
|
||||
TYPE_LOGIN_CONFIRM = 'login_confirm'
|
||||
TYPE_REQUEST_ASSET_PERM = 'request_asset'
|
||||
TYPE_CHOICES = (
|
||||
(TYPE_GENERAL, _("General")),
|
||||
(TYPE_LOGIN_CONFIRM, _("Login confirm")),
|
||||
(TYPE_REQUEST_ASSET_PERM, _('Request asset permission'))
|
||||
)
|
||||
ACTION_APPROVE = 'approve'
|
||||
ACTION_REJECT = 'reject'
|
||||
ACTION_CHOICES = (
|
||||
(ACTION_APPROVE, _('Approve')),
|
||||
(ACTION_REJECT, _('Reject')),
|
||||
)
|
||||
class STATUS(ChoiceSet):
|
||||
OPEN = 'open', _("Open")
|
||||
CLOSED = 'closed', _("Closed")
|
||||
|
||||
class TYPE(ChoiceSet):
|
||||
GENERAL = 'general', _("General")
|
||||
LOGIN_CONFIRM = 'login_confirm', _("Login confirm")
|
||||
REQUEST_ASSET_PERM = 'request_asset', _('Request asset permission')
|
||||
|
||||
class ACTION(ChoiceSet):
|
||||
APPROVE = 'approve', _('Approve')
|
||||
REJECT = 'reject', _('Reject')
|
||||
|
||||
user = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, related_name='%(class)s_requested', verbose_name=_("User"))
|
||||
user_display = models.CharField(max_length=128, verbose_name=_("User display name"))
|
||||
|
||||
|
@ -40,12 +34,12 @@ class Ticket(OrgModelMixin, CommonModelMixin):
|
|||
body = models.TextField(verbose_name=_("Body"))
|
||||
meta = JsonDictTextField(verbose_name=_("Meta"), default='{}')
|
||||
assignee = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, related_name='%(class)s_handled', verbose_name=_("Assignee"))
|
||||
assignee_display = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Assignee display name"))
|
||||
assignee_display = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Assignee display name"), default='')
|
||||
assignees = models.ManyToManyField('users.User', related_name='%(class)s_assigned', verbose_name=_("Assignees"))
|
||||
assignees_display = models.CharField(max_length=128, verbose_name=_("Assignees display name"), blank=True)
|
||||
type = models.CharField(max_length=16, choices=TYPE_CHOICES, default=TYPE_GENERAL, verbose_name=_("Type"))
|
||||
status = models.CharField(choices=STATUS_CHOICES, max_length=16, default='open')
|
||||
action = models.CharField(choices=ACTION_CHOICES, max_length=16, default='', blank=True)
|
||||
type = models.CharField(max_length=16, choices=TYPE.choices, default=TYPE.GENERAL, verbose_name=_("Type"))
|
||||
status = models.CharField(choices=STATUS.choices, max_length=16, default='open')
|
||||
action = models.CharField(choices=ACTION.choices, max_length=16, default='', blank=True)
|
||||
|
||||
origin_objects = models.Manager()
|
||||
|
||||
|
@ -69,30 +63,38 @@ class Ticket(OrgModelMixin, CommonModelMixin):
|
|||
return self.get_action_display()
|
||||
|
||||
def create_status_comment(self, status, user):
|
||||
if status == self.STATUS_CLOSED:
|
||||
if status == self.STATUS.CLOSED:
|
||||
action = _("Close")
|
||||
else:
|
||||
action = _("Open")
|
||||
body = _('{} {} this ticket').format(self.user, action)
|
||||
self.comments.create(user=user, body=body)
|
||||
|
||||
def perform_status(self, status, user):
|
||||
if self.status == status:
|
||||
return
|
||||
def perform_status(self, status, user, extra_comment=None):
|
||||
self.create_comment(
|
||||
self.STATUS.get(status),
|
||||
user,
|
||||
extra_comment
|
||||
)
|
||||
self.status = status
|
||||
self.assignee = user
|
||||
self.assignees_display = str(user)
|
||||
self.save()
|
||||
|
||||
def create_action_comment(self, action, user, extra_comment=None):
|
||||
action_display = dict(self.ACTION_CHOICES).get(action)
|
||||
def create_comment(self, action_display, user, extra_comment=None):
|
||||
body = '{} {} {}'.format(user, action_display, _("this ticket"))
|
||||
if extra_comment is not None:
|
||||
body += extra_comment
|
||||
self.comments.create(body=body, user=user, user_display=str(user))
|
||||
|
||||
def perform_action(self, action, user, extra_comment=None):
|
||||
self.create_action_comment(action, user, extra_comment)
|
||||
self.create_comment(
|
||||
self.ACTION.get(action),
|
||||
user,
|
||||
extra_comment
|
||||
)
|
||||
self.action = action
|
||||
self.status = self.STATUS_CLOSED
|
||||
self.status = self.STATUS.CLOSED
|
||||
self.assignee = user
|
||||
self.assignees_display = str(user)
|
||||
self.save()
|
||||
|
|
|
@ -17,7 +17,7 @@ from ..models import Ticket
|
|||
class RequestAssetPermTicketSerializer(serializers.ModelSerializer):
|
||||
ips = serializers.ListField(child=serializers.IPAddressField(), source='meta.ips',
|
||||
default=list, label=_('IP group'))
|
||||
hostname = serializers.CharField(max_length=256, source='meta.hostname', default=None,
|
||||
hostname = serializers.CharField(max_length=256, source='meta.hostname', default='',
|
||||
allow_blank=True, label=_('Hostname'))
|
||||
system_user = serializers.CharField(max_length=256, source='meta.system_user', default='',
|
||||
allow_blank=True, label=_('System user'))
|
||||
|
@ -135,7 +135,7 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer):
|
|||
|
||||
def _create_body(self, validated_data):
|
||||
meta = validated_data['meta']
|
||||
type = dict(Ticket.TYPE_CHOICES).get(validated_data.get('type', ''))
|
||||
type = Ticket.TYPE.get(validated_data.get('type', ''))
|
||||
date_start = dt_parser(meta.get('date_start')).strftime(settings.DATETIME_DISPLAY_FORMAT)
|
||||
date_expired = dt_parser(meta.get('date_expired')).strftime(settings.DATETIME_DISPLAY_FORMAT)
|
||||
|
||||
|
@ -159,7 +159,7 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer):
|
|||
|
||||
def create(self, validated_data):
|
||||
# `type` 与 `user` 用户不可提交,
|
||||
validated_data['type'] = self.Meta.model.TYPE_REQUEST_ASSET_PERM
|
||||
validated_data['type'] = self.Meta.model.TYPE.REQUEST_ASSET_PERM
|
||||
validated_data['user'] = self.context['request'].user
|
||||
# `confirmed` 相关字段只能审批人修改,所以创建时直接清理掉
|
||||
self._pop_confirmed_fields()
|
||||
|
|
|
@ -3,14 +3,18 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from .. import models
|
||||
from ..exceptions import (
|
||||
TicketClosed, OnlyTicketAssigneeCanOperate,
|
||||
TicketCanNotOperate
|
||||
)
|
||||
from ..models import Ticket, Comment
|
||||
|
||||
__all__ = ['TicketSerializer', 'CommentSerializer']
|
||||
|
||||
|
||||
class TicketSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.Ticket
|
||||
model = Ticket
|
||||
fields = [
|
||||
'id', 'user', 'user_display', 'title', 'body',
|
||||
'assignees', 'assignees_display', 'assignee', 'assignee_display',
|
||||
|
@ -32,17 +36,33 @@ class TicketSerializer(serializers.ModelSerializer):
|
|||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
action = validated_data.get("action")
|
||||
user = self.context["request"].user
|
||||
action = validated_data.get('action')
|
||||
user = self.context['request'].user
|
||||
|
||||
if instance.type not in (Ticket.TYPE.GENERAL,
|
||||
Ticket.TYPE.LOGIN_CONFIRM):
|
||||
# 暂时的兼容操作吧,后期重构工单
|
||||
raise TicketCanNotOperate
|
||||
|
||||
if instance.status == instance.STATUS.CLOSED:
|
||||
raise TicketClosed
|
||||
|
||||
if action:
|
||||
if user not in instance.assignees.all():
|
||||
raise OnlyTicketAssigneeCanOperate
|
||||
|
||||
# 有 `action` 时忽略 `status`
|
||||
validated_data.pop('status', None)
|
||||
|
||||
instance = super().update(instance, validated_data)
|
||||
if not instance.status == instance.STATUS.CLOSED and action:
|
||||
instance.perform_action(action, user)
|
||||
else:
|
||||
status = validated_data.get('status')
|
||||
instance = super().update(instance, validated_data)
|
||||
if status:
|
||||
instance.perform_status(status, user)
|
||||
|
||||
if action and user not in instance.assignees.all():
|
||||
error = {"action": "Only assignees can update"}
|
||||
raise serializers.ValidationError(error)
|
||||
if instance.status == instance.STATUS_CLOSED:
|
||||
validated_data.pop('action')
|
||||
instance = super().update(instance, validated_data)
|
||||
if not instance.status == instance.STATUS_CLOSED and action:
|
||||
instance.perform_action(action, user)
|
||||
return instance
|
||||
|
||||
|
||||
|
@ -65,7 +85,7 @@ class CommentSerializer(serializers.ModelSerializer):
|
|||
)
|
||||
|
||||
class Meta:
|
||||
model = models.Comment
|
||||
model = Comment
|
||||
fields = [
|
||||
'id', 'ticket', 'body', 'user', 'user_display',
|
||||
'date_created', 'date_updated'
|
||||
|
|
|
@ -20,7 +20,7 @@ def send_new_ticket_mail_to_assignees(ticket: Ticket, assignees):
|
|||
subject = '{}: {}'.format(_("New ticket"), ticket.title)
|
||||
|
||||
# 这里要设置前端地址,因为要直接跳转到页面
|
||||
if ticket.type == ticket.TYPE_REQUEST_ASSET_PERM:
|
||||
if ticket.type == ticket.TYPE.REQUEST_ASSET_PERM:
|
||||
detail_url = urljoin(settings.SITE_URL, f'/tickets/tickets/request-asset-perm/{ticket.id}')
|
||||
else:
|
||||
detail_url = urljoin(settings.SITE_URL, f'/tickets/tickets/{ticket.id}')
|
||||
|
|
Loading…
Reference in New Issue