Skip to content

Commit

Permalink
feat: 新增配置- 账户有效期 (#1361)
Browse files Browse the repository at this point in the history
  • Loading branch information
neronkl authored Nov 13, 2023
1 parent 7e99acd commit a852eb7
Show file tree
Hide file tree
Showing 12 changed files with 550 additions and 29 deletions.
2 changes: 2 additions & 0 deletions src/bk-user/bkuser/apis/web/tenant/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ def post(self, request, *args, **kwargs):
]
# 本地数据源密码初始化配置
config = PasswordInitialConfig(**data["password_initial_config"])

# 创建租户和租户管理员
tenant_id = TenantHandler.create_with_managers(tenant_info, managers, config)

return Response(TenantCreateOutputSLZ(instance={"id": tenant_id}).data)
Expand Down
32 changes: 31 additions & 1 deletion src/bk-user/bkuser/apis/web/tenant_setting/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from bkuser.apps.tenant.constants import UserFieldDataType
from bkuser.apps.tenant.constants import NotificationMethod, NotificationScene, UserFieldDataType
from bkuser.apps.tenant.data_models import TenantUserCustomFieldOptions
from bkuser.apps.tenant.models import TenantUserCustomField, UserBuiltinField

Expand Down Expand Up @@ -163,3 +163,33 @@ def validate(self, attrs):
_validate_multi_enum_default(default, opt_ids)

return attrs


class NotificationTemplatesInputSLZ(serializers.Serializer):
method = serializers.ChoiceField(help_text="通知方式", choices=NotificationMethod.get_choices())
scene = serializers.ChoiceField(help_text="通知场景", choices=NotificationScene.get_choices())
title = serializers.CharField(help_text="通知标题", allow_null=True)
sender = serializers.CharField(help_text="发送人")
content = serializers.CharField(help_text="通知内容")
content_html = serializers.CharField(help_text="通知内容,页面展示使用")


class TenantUserValidityPeriodConfigInputSLZ(serializers.Serializer):
enabled = serializers.BooleanField(help_text="是否启用账户有效期")
validity_period = serializers.IntegerField(help_text="账户有效期,单位:天")
remind_before_expire = serializers.ListField(
help_text="临过期提醒时间",
child=serializers.IntegerField(min_value=1),
)
enabled_notification_methods = serializers.ListField(
help_text="通知方式",
child=serializers.ChoiceField(choices=NotificationMethod.get_choices()),
allow_empty=False,
)
notification_templates = serializers.ListField(
help_text="通知模板", child=NotificationTemplatesInputSLZ(), allow_empty=False
)


class TenantUserValidityPeriodConfigOutputSLZ(TenantUserValidityPeriodConfigInputSLZ):
pass
5 changes: 5 additions & 0 deletions src/bk-user/bkuser/apis/web/tenant_setting/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@
views.TenantUserCustomFieldUpdateDeleteApi.as_view(),
name="tenant_setting_custom_fields.update_delete",
),
path(
"settings/tenant-user-validity-period/",
views.TenantUserValidityPeriodConfigRetrieveUpdateApi.as_view(),
name="tenant_user_validity_period_config.retrieve_update",
),
]
67 changes: 63 additions & 4 deletions src/bk-user/bkuser/apis/web/tenant_setting/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""
from drf_yasg.utils import swagger_auto_schema
from rest_framework import generics, status
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response

from bkuser.apis.web.mixins import CurrentUserTenantMixin
Expand All @@ -18,9 +19,17 @@
TenantUserCustomFieldCreateOutputSLZ,
TenantUserCustomFieldUpdateInputSLZ,
TenantUserFieldOutputSLZ,
TenantUserValidityPeriodConfigInputSLZ,
TenantUserValidityPeriodConfigOutputSLZ,
)
from bkuser.apps.tenant.models import TenantUserCustomField, UserBuiltinField
from bkuser.common.views import ExcludePutAPIViewMixin
from bkuser.apps.tenant.models import (
TenantManager,
TenantUserCustomField,
TenantUserValidityPeriodConfig,
UserBuiltinField,
)
from bkuser.common.error_codes import error_codes
from bkuser.common.views import ExcludePatchAPIViewMixin, ExcludePutAPIViewMixin


class TenantUserFieldListApi(CurrentUserTenantMixin, generics.ListAPIView):
Expand Down Expand Up @@ -82,8 +91,7 @@ def put(self, request, *args, **kwargs):
tenant_id = self.get_current_tenant_id()

slz = TenantUserCustomFieldUpdateInputSLZ(
data=request.data,
context={"tenant_id": tenant_id, "current_custom_field_id": kwargs["id"]},
data=request.data, context={"tenant_id": tenant_id, "current_custom_field_id": kwargs["id"]}
)
slz.is_valid(raise_exception=True)
data = slz.validated_data
Expand All @@ -104,3 +112,54 @@ def put(self, request, *args, **kwargs):
)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)


class TenantUserValidityPeriodConfigRetrieveUpdateApi(
ExcludePatchAPIViewMixin, CurrentUserTenantMixin, generics.RetrieveUpdateAPIView
):
def get_object(self):
queryset = TenantUserValidityPeriodConfig.objects.all()
filter_kwargs = {"tenant_id": self.get_current_tenant_id()}
return get_object_or_404(queryset, **filter_kwargs)

@swagger_auto_schema(
tags=["tenant-setting"],
operation_description="当前租户的账户有效期配置",
responses={
status.HTTP_200_OK: TenantUserValidityPeriodConfigOutputSLZ(),
},
)
def get(self, request, *args, **kwargs):
instance = self.get_object()
return Response(TenantUserValidityPeriodConfigOutputSLZ(instance=instance).data)

@swagger_auto_schema(
tags=["tenant-setting"],
operation_description="更新当前租户的账户有效期配置",
request_body=TenantUserValidityPeriodConfigInputSLZ(),
responses={
status.HTTP_204_NO_CONTENT: "",
},
)
def put(self, request, *args, **kwargs):
instance = self.get_object()

# TODO (su) 权限调整为 perm_class 当前租户的管理才可做更新操作
operator = request.user.username
if not TenantManager.objects.filter(tenant_id=instance.tenant_id, tenant_user_id=operator).exists():
raise error_codes.NO_PERMISSION

slz = TenantUserValidityPeriodConfigInputSLZ(data=request.data)
slz.is_valid(raise_exception=True)
data = slz.validated_data

instance.enabled = data["enabled"]
instance.validity_period = data["validity_period"]
instance.remind_before_expire = data["remind_before_expire"]
instance.enabled_notification_methods = data["enabled_notification_methods"]
instance.notification_templates = data["notification_templates"]
instance.updater = operator

instance.save()

return Response(status=status.HTTP_204_NO_CONTENT)
92 changes: 92 additions & 0 deletions src/bk-user/bkuser/apps/tenant/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,95 @@ class UserFieldDataType(str, StructuredEnum):
NUMBER = EnumField("number", label=_("数字"))
ENUM = EnumField("enum", label=_("枚举"))
MULTI_ENUM = EnumField("multi_enum", label=_("多选枚举"))


class NotificationMethod(str, StructuredEnum):
"""通知方式"""

EMAIL = EnumField("email", label=_("邮件通知"))
SMS = EnumField("sms", label=_("短信通知"))


class NotificationScene(str, StructuredEnum):
"""通知场景"""

TENANT_USER_EXPIRING = EnumField("tenant_user_expiring", label=_("租户用户即将过期"))
TENANT_USER_EXPIRED = EnumField("tenant_user_expired", label=_("租户用户已过期"))


DEFAULT_TENANT_USER_VALIDITY_PERIOD_CONFIG = {
"enabled": True,
"validity_period": 365,
"remind_before_expire": [7],
"enabled_notification_methods": [NotificationMethod.EMAIL],
"notification_templates": [
{
"method": NotificationMethod.EMAIL,
"scene": NotificationScene.TENANT_USER_EXPIRING,
"title": "蓝鲸智云 - 账号即将到期提醒!",
"sender": "蓝鲸智云",
"content": (
"{{ username }}, 您好:\n "
+ "您的蓝鲸智云平台账号将于 {{ remind_before_expire_days }} 天后到期。"
+ "为避免影响使用,请尽快联系平台管理员进行续期。\n "
+ "此邮件为系统自动发送,请勿回复。\n "
),
"content_html": (
"<p>{{ username }}, 您好:</p>"
+ "<p>您的蓝鲸智云平台账号将于 {{ remind_before_expire_days }} 天后到期。"
+ "为避免影响使用,请尽快联系平台管理员进行续期。</p>"
+ "<p>此邮件为系统自动发送,请勿回复。</p>"
),
},
{
"method": NotificationMethod.EMAIL,
"scene": NotificationScene.TENANT_USER_EXPIRED,
"title": "蓝鲸智云 - 账号到期提醒!",
"sender": "蓝鲸智云",
"content": (
"{{ username }},您好:\n "
+ "您的蓝鲸智云平台账号已过期。为避免影响使用,请尽快联系平台管理员进行续期。\n " # noqa: E501
+ "该邮件为系统自动发送,请勿回复。" # noqa: E501
),
"content_html": (
"<p>{{ username }},您好:</p>"
+ "<p>您的蓝鲸智云平台账号已过期,如需继续使用,请尽快联系平台管理员进行续期。</p>" # noqa: E501
+ "<p>此邮件为系统自动发送,请勿回复。</p>"
),
},
{
"method": NotificationMethod.SMS,
"scene": NotificationScene.TENANT_USER_EXPIRING,
"title": None,
"sender": "蓝鲸智云",
"content": (
"{{ username }},您好:\n "
+ "您的蓝鲸智云平台账号将于 {{ remind_before_expire_days }} 天后到期。"
+ "为避免影响使用,请尽快联系平台管理员进行续期。\n "
+ "该短信为系统自动发送,请勿回复。"
),
"content_html": (
"<p>{{ username }},您好:</p>"
+ "<p>您的蓝鲸智云平台账号将于 {{ remind_before_expire_days }} 天后到期。"
+ "为避免影响使用,请尽快联系平台管理员进行续期。</p>"
+ "<p>该短信为系统自动发送,请勿回复。</p>"
),
},
{
"method": NotificationMethod.SMS,
"scene": NotificationScene.TENANT_USER_EXPIRED,
"title": None,
"sender": "蓝鲸智云",
"content": (
"{{ username }}您好:\n "
+ "您的蓝鲸智云平台账号已过期,如需继续使用,请尽快联系平台管理员进行续期。\n " # noqa: E501
+ "该短信为系统自动发送,请勿回复。" # noqa: E501
),
"content_html": (
"<p>{{ username }}您好:</p>"
+ "<p>您的蓝鲸智云平台账号已过期,如需继续使用,请尽快联系平台管理员进行续期。</p>" # noqa: E501
+ "<p>该短信为系统自动发送,请勿回复。</p>"
),
},
],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 3.2.20 on 2023-11-13 12:17

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


class Migration(migrations.Migration):

dependencies = [
('tenant', '0002_init_builtin_user_fields'),
]

operations = [
migrations.AlterField(
model_name='tenantuser',
name='wx_openid',
field=models.CharField(blank=True, default='', max_length=64, null=True, verbose_name='微信公众号 用户OpenID'),
),
migrations.CreateModel(
name='TenantUserValidityPeriodConfig',
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)),
('creator', models.CharField(blank=True, max_length=128, null=True)),
('updater', models.CharField(blank=True, max_length=128, null=True)),
('enabled', models.BooleanField(default=True, verbose_name='是否启用账户有效期')),
('validity_period', models.IntegerField(default=-1, verbose_name='有效期(单位:天)')),
('remind_before_expire', models.JSONField(default=list, verbose_name='临X天过期发送提醒(单位:天)')),
('enabled_notification_methods', models.JSONField(default=list, verbose_name='通知方式')),
('notification_templates', models.JSONField(default=list, verbose_name='通知模板')),
('tenant', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='tenant.tenant')),
],
options={
'abstract': False,
},
),
]
41 changes: 21 additions & 20 deletions src/bk-user/bkuser/apps/tenant/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
from django.db import models

from bkuser.apps.data_source.models import DataSource, DataSourceDepartment, DataSourceUser
from bkuser.apps.tenant.constants import TenantFeatureFlag, UserFieldDataType
from bkuser.apps.tenant.constants import TIME_ZONE_CHOICES, TenantFeatureFlag, UserFieldDataType
from bkuser.common.constants import PERMANENT_TIME, BkLanguageEnum
from bkuser.common.models import TimestampedModel
from bkuser.common.models import AuditedModel, TimestampedModel
from bkuser.common.time import datetime_to_display

from .constants import TIME_ZONE_CHOICES


class Tenant(TimestampedModel):
id = models.CharField("租户唯一标识", primary_key=True, max_length=128)
Expand Down Expand Up @@ -56,7 +54,7 @@ class TenantUser(TimestampedModel):

# wx_userid/wx_openid 兼容旧版本迁移
wx_userid = models.CharField("微信ID", null=True, blank=True, default="", max_length=64)
wx_openid = models.CharField("微信公众号OpenID", null=True, blank=True, default="", max_length=64)
wx_openid = models.CharField("微信公众号 用户OpenID", null=True, blank=True, default="", max_length=64)

# 账号有效期相关
account_expired_at = models.DateTimeField("账号过期时间", null=True, blank=True, default=PERMANENT_TIME)
Expand All @@ -83,6 +81,14 @@ class Meta:
def account_expired_at_display(self) -> str:
return datetime_to_display(self.account_expired_at)

@property
def real_phone(self) -> str:
return self.data_source_user.phone if self.is_inherited_phone else self.custom_phone

@property
def real_email(self) -> str:
return self.data_source_user.email if self.is_inherited_email else self.custom_email


class TenantDepartment(TimestampedModel):
"""
Expand Down Expand Up @@ -143,21 +149,16 @@ class Meta:
]


# # TODO: 是否直接定义 TenantCommonConfig 表,AccountValidityPeriod是一个JSON字段?
# class AccountValidityPeriodConfig:
# """账号时效配置"""
#
# tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, db_index=True, unique=True)
#
# enabled = models.BooleanField("是否启用", default=True)
# # TODO: 定义枚举,设置默认值为永久
# validity_period_seconds = models.IntegerField("有效期(单位:秒)", default=-1)
# # TODO: 定义枚举,设置默认值为7天
# reminder_period_days = models.IntegerField("提醒周期(单位:天)", default=7)
# # TODO: 定义枚举,同时需要考虑到与企业ESB配置的支持的通知方式有关,是否定义字段?
# notification_method = models.CharField("通知方式", max_length=32, default="email")
# # TODO: 需要考虑不同通知方式,可能无法使用相同模板,或者其他设计方式
# notification_content_template = models.TextField("通知模板", default="")
class TenantUserValidityPeriodConfig(AuditedModel):
"""账号有效期-配置"""

tenant = models.OneToOneField(Tenant, on_delete=models.CASCADE, db_index=True, unique=True)

enabled = models.BooleanField("是否启用账户有效期", default=True)
validity_period = models.IntegerField("有效期(单位:天)", default=-1)
remind_before_expire = models.JSONField("临X天过期发送提醒(单位:天)", default=list)
enabled_notification_methods = models.JSONField("通知方式", default=list)
notification_templates = models.JSONField("通知模板", default=list)


# class TenantUserSocialAccountRelation(TimestampedModel):
Expand Down
Loading

0 comments on commit a852eb7

Please sign in to comment.