Skip to content

Commit

Permalink
feat: 添加ITSM上下文配置功能
Browse files Browse the repository at this point in the history
  • Loading branch information
wmcowen committed Dec 5, 2024
1 parent 79d8376 commit 3122c33
Show file tree
Hide file tree
Showing 16 changed files with 326 additions and 7 deletions.
5 changes: 4 additions & 1 deletion common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

from common.log import logger
from common.pxfilter import XssHtml
from itsm.meta.models import ContextService


def html_escape(html, is_json=False):
Expand Down Expand Up @@ -107,5 +108,7 @@ def notice_receiver_filter(receivers):
receiver_type = "str"
receivers = receivers.strip().split(",")

receivers = [i for i in receivers if i not in settings.NOTICE_IGNORE_LIST]
context_service = ContextService()
notice_blacklist = context_service.get_context_value_list("notice_blacklist")
receivers = [i for i in receivers if i not in notice_blacklist]
return receivers if receiver_type == "list" else ",".join(receivers)
7 changes: 2 additions & 5 deletions config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@
"blueapps.opentelemetry.instrument_app",
"itsm.plugin_service",
"bk_notice_sdk",
"pipeline.contrib.engine_admin"
"pipeline.contrib.engine_admin",
"itsm.meta",
)

INSTALLED_APPS = ("itsm.helper",) + INSTALLED_APPS
Expand Down Expand Up @@ -962,10 +963,6 @@ def redirect_func(request):
BK_SHARED_RES_URL = os.getenv("BKPAAS_SHARED_RES_URL") or os.getenv("BKAPP_SHARED_RES_URL")
BK_PLATFORM_NAME = os.getenv("BKAPP_PLATFORM_NAME", "")

# 通知过滤
NOTICE_IGNORE_LIST = os.getenv("BKAPP_NOTICE_IGNORE_LIST", [])
if isinstance(NOTICE_IGNORE_LIST, str):
NOTICE_IGNORE_LIST = [i.lower().strip() for i in NOTICE_IGNORE_LIST.split(",")]

# SMS 邀请评价限额
TICKET_INVITE_SMS_COUNT = int(os.getenv("BKAPP_TICKET_INVITE_SMS_COUNT", 10))
Expand Down
8 changes: 8 additions & 0 deletions itsm/component/bkchat/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from itsm.component.constants import APPROVE_RESULT, API, RUNNING, SHOW_BY_CONDITION
from itsm.component.exceptions import ComponentCallError
from itsm.component.utils.conversion import show_conditions_validate, format_exp_value
from itsm.meta.models import ContextService
from itsm.ticket.models import Ticket, Status, TicketField, SignTask

# 当前运行环境
Expand Down Expand Up @@ -113,6 +114,13 @@ def send_fast_approval_message(title, content, receivers, ticket, state_id):

# 更新详情url
ticket.generate_ticket_url(state_id, receivers)

# 如果ticket的service_id在黑名单中则不发送快速通知
context_service = ContextService()
service_approval_blacklist = context_service.get_context_value_list(key="service_approval_blacklist")
if str(ticket.service_id) in service_approval_blacklist:
logger.info(f"[fast_approval] service id is in service_approval_blacklist=>{ticket_id}")
return

# 接收人过滤
receivers = notice_receiver_filter(receivers)
Expand Down
Empty file added itsm/meta/__init__.py
Empty file.
11 changes: 11 additions & 0 deletions itsm/meta/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.contrib import admin
from .models import Context

# Register your models here.
class ContextAdmin(admin.ModelAdmin):
list_display = ("id", "key", "value", "created_at", "updated_at")
search_fields = ("key", "value")
list_filter = ("key", )


admin.site.register(Context, ContextAdmin)
6 changes: 6 additions & 0 deletions itsm/meta/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class MetaConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'itsm.meta'
27 changes: 27 additions & 0 deletions itsm/meta/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 3.2.25 on 2024-12-05 17:48

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Context',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('key', models.CharField(max_length=255, unique=True)),
('value', models.TextField(blank=True)),
],
options={
'db_table': 'meta_context',
},
),
]
Empty file.
43 changes: 43 additions & 0 deletions itsm/meta/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from django.core.cache import cache
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver


class Context(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
key = models.CharField(max_length=255, unique=True)
value = models.TextField(blank=True)

def __str__(self):
return self.key

class Meta:
db_table = "meta_context"


@receiver(post_save, sender=Context)
def update_cache(sender, instance, **kwargs):
cache_key = f'meta_context_{instance.key}'
cache.set(cache_key, instance.value, 30)


class ContextService:
@staticmethod
def get_context_value(key):
cache_key = f'meta_context_{key}'
context_value = cache.get(cache_key)
if not context_value:
try:
context_value = Context.objects.get(key=key).value
except Context.DoesNotExist:
context_value = ""
cache.set(cache_key, context_value, 30)
return context_value

@staticmethod
def get_context_value_list(key):
context_value = ContextService.get_context_value(key)
context_value = [item.strip() for item in context_value.split(",")] if context_value else []
return context_value
3 changes: 3 additions & 0 deletions itsm/meta/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.shortcuts import render

# Create your views here.
3 changes: 3 additions & 0 deletions itsm/meta/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.shortcuts import render

# Create your views here.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from django.conf import settings
from django.core.cache import cache
from itsm.component.constants import PROCESS_COUNT
from itsm.meta.models import ContextService
from itsm.ticket.models import Ticket, Status
from pipeline.component_framework.component import Component

Expand Down Expand Up @@ -56,12 +57,20 @@ def execute(self, data, parent_data):
state_id = data.inputs.state_id
ticket = Ticket.objects.get(id=ticket_id)

# 调用ticket的do_before_enter_sign_state方法,获取变量、条件、code_key
# 在这个方法里会发送bkchat快速审批消息,以及通知
variables, finish_condition, code_key = ticket.do_before_enter_sign_state(
state_id, by_flow=self.by_flow
)
is_multi = ticket.flow.get_state(state_id)["is_multi"]
user_count = str(self.get_user_count(ticket_id, state_id)) if is_multi else "1"
ticket.create_moa_ticket(state_id)

# 如果service_id不在service_approval_blacklist中,则创建moa单据
context_service = ContextService()
service_approval_blacklist = context_service.get_context_value_list(
"service_approval_blacklist")
if str(ticket.service_id) not in service_approval_blacklist:
ticket.create_moa_ticket(state_id)

# 如果是普通的审批节点,则自动生成条件
if not is_multi:
Expand Down
28 changes: 28 additions & 0 deletions itsm/postman/migrations/0011_auto_20241115_1515.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.25 on 2024-11-15 15:15

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('postman', '0010_auto_20210621_1206'),
]

operations = [
migrations.AlterModelManagers(
name='remoteapi',
managers=[
],
),
migrations.AlterModelManagers(
name='remoteapiinstance',
managers=[
],
),
migrations.AlterModelManagers(
name='remotesystem',
managers=[
],
),
]
28 changes: 28 additions & 0 deletions itsm/task/migrations/0029_auto_20241115_1515.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.25 on 2024-11-15 15:15

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('task', '0028_auto_20210226_1745'),
]

operations = [
migrations.AlterField(
model_name='task',
name='processors_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='处理人类型'),
),
migrations.AlterField(
model_name='taskfield',
name='source_type',
field=models.CharField(choices=[('CUSTOM', '自定义数据'), ('API', '接口数据'), ('DATADICT', '数据字典'), ('RPC', '系统数据'), ('CUSTOM_API', '自定义API')], default='CUSTOM', max_length=32, verbose_name='数据来源类型'),
),
migrations.AlterField(
model_name='taskfield',
name='type',
field=models.CharField(choices=[('STRING', '单行文本'), ('TEXT', '多行文本'), ('INT', '数字'), ('DATE', '日期'), ('DATETIME', '日期时间'), ('DATETIMERANGE', '时间间隔'), ('TABLE', '表格'), ('SELECT', '单选下拉框'), ('INPUTSELECT', '可输入单选下拉框'), ('MULTISELECT', '多选下拉框'), ('CHECKBOX', '复选框'), ('RADIO', '单选框'), ('MEMBER', '单个人员选择'), ('MEMBERS', '多个人员选择'), ('RICHTEXT', '富文本'), ('FILE', '附件上传'), ('CUSTOMTABLE', '自定义表格'), ('TREESELECT', '树形选择'), ('LINK', '链接'), ('CUSTOM-FORM', '自定义表单'), ('CASCADE', '级联')], default='STRING', max_length=32, verbose_name='字段类型'),
),
]
70 changes: 70 additions & 0 deletions itsm/ticket/migrations/0073_auto_20241115_1515.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Generated by Django 3.2.25 on 2024-11-15 15:15

from django.db import migrations, models
import django.db.models.manager


class Migration(migrations.Migration):

dependencies = [
('ticket', '0072_auto_20240513_1608'),
]

operations = [
migrations.AlterModelManagers(
name='ticketfollowernotifylog',
managers=[
('_objects', django.db.models.manager.Manager()),
],
),
migrations.AlterField(
model_name='status',
name='assignors_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='派单人类型'),
),
migrations.AlterField(
model_name='status',
name='delivers_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='转单人类型'),
),
migrations.AlterField(
model_name='status',
name='processors_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='处理人类型'),
),
migrations.AlterField(
model_name='taskfield',
name='type',
field=models.CharField(choices=[('STRING', '单行文本'), ('TEXT', '多行文本'), ('INT', '数字'), ('DATE', '日期'), ('DATETIME', '日期时间'), ('DATETIMERANGE', '时间间隔'), ('TABLE', '表格'), ('SELECT', '单选下拉框'), ('INPUTSELECT', '可输入单选下拉框'), ('MULTISELECT', '多选下拉框'), ('CHECKBOX', '复选框'), ('RADIO', '单选框'), ('MEMBER', '单个人员选择'), ('MEMBERS', '多个人员选择'), ('RICHTEXT', '富文本'), ('FILE', '附件上传'), ('CUSTOMTABLE', '自定义表格'), ('TREESELECT', '树形选择'), ('LINK', '链接'), ('CUSTOM-FORM', '自定义表单'), ('CASCADE', '级联')], default='STRING', max_length=32, verbose_name='字段类型'),
),
migrations.AlterField(
model_name='ticket',
name='current_assignor_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='分派人类型'),
),
migrations.AlterField(
model_name='ticket',
name='current_processors_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='处理者类型'),
),
migrations.AlterField(
model_name='ticket',
name='supervise_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='督办人类型'),
),
migrations.AlterField(
model_name='ticketeventlog',
name='processors_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='OPEN', max_length=32, verbose_name='处理人类型'),
),
migrations.AlterField(
model_name='ticketfield',
name='type',
field=models.CharField(choices=[('STRING', '单行文本'), ('TEXT', '多行文本'), ('INT', '数字'), ('DATE', '日期'), ('DATETIME', '日期时间'), ('DATETIMERANGE', '时间间隔'), ('TABLE', '表格'), ('SELECT', '单选下拉框'), ('INPUTSELECT', '可输入单选下拉框'), ('MULTISELECT', '多选下拉框'), ('CHECKBOX', '复选框'), ('RADIO', '单选框'), ('MEMBER', '单个人员选择'), ('MEMBERS', '多个人员选择'), ('RICHTEXT', '富文本'), ('FILE', '附件上传'), ('CUSTOMTABLE', '自定义表格'), ('TREESELECT', '树形选择'), ('LINK', '链接'), ('CUSTOM-FORM', '自定义表单'), ('CASCADE', '级联')], default='STRING', max_length=32, verbose_name='字段类型'),
),
migrations.AlterField(
model_name='ticketfollowernotifylog',
name='followers_type',
field=models.CharField(choices=[('CMDB', 'CMDB业务公用角色'), ('GENERAL', '通用角色表'), ('OPEN', '不限'), ('PERSON', '个人'), ('STARTER', '提单人'), ('STARTER_LEADER', '提单人上级'), ('ASSIGN_LEADER', '指定节点处理人上级'), ('BY_ASSIGNOR', '派单人指定'), ('EMPTY', '无'), ('ORGANIZATION', '组织架构'), ('VARIABLE', '引用变量'), ('VARIABLE_LEADER', '指定变量上级'), ('IAM', '权限中心角色')], default='EMPTY', max_length=32, verbose_name='处理者类型/角色类型'),
),
]
Loading

0 comments on commit 3122c33

Please sign in to comment.