@@ -20,7 +20,7 @@ from account.models import UserInfo |
||
20 | 20 |
from api.encrypt_views import get_ciphertext |
21 | 21 |
from coupon.models import CouponInfo, UserCouponInfo |
22 | 22 |
from integral.models import SaleclerkSubmitLogInfo |
23 |
-from logs.models import ComplementCodeLogInfo, MchInfoEncryptLogInfo |
|
23 |
+from logs.models import ComplementCodeLogInfo, MchInfoEncryptLogInfo, AdministratorLoginLogInfo |
|
24 | 24 |
from mch.models import AdministratorInfo, BrandInfo, ConsumeInfoSubmitLogInfo, DistributorInfo, ModelInfo |
25 | 25 |
from member.models import (GoodsInfo, GoodsOrderInfo, MemberActivityGroupShareInfo, MemberActivityInfo, |
26 | 26 |
MemberActivitySignupInfo) |
@@ -1709,7 +1709,7 @@ def administrator_update(request): |
||
1709 | 1709 |
admin_id = request.POST.get('admin_id', '') |
1710 | 1710 |
|
1711 | 1711 |
target_admin_id = request.POST.get('target_admin_id', '') |
1712 |
- admin_type = int(request.POST.get('admin_type', 3)) |
|
1712 |
+ admin_type = int(request.POST.get('admin_type', -1)) |
|
1713 | 1713 |
phone = request.POST.get('phone', '') |
1714 | 1714 |
name = request.POST.get('name', '') |
1715 | 1715 |
password = request.POST.get('password', '') |
@@ -1725,15 +1725,24 @@ def administrator_update(request): |
||
1725 | 1725 |
if administrator.admin_type != AdministratorInfo.ADMINISTRATOR: |
1726 | 1726 |
return response(AdministratorStatusCode.ADMINISTRATOR_PERMISSION_DENIED) |
1727 | 1727 |
|
1728 |
- encryption = make_password(strip(password), settings.MAKE_PASSWORD_SALT, settings.MAKE_PASSWORD_HASHER) |
|
1729 |
- |
|
1730 |
- AdministratorInfo.objects.filter(admin_id=target_admin_id, brand_id=brand_id).update( |
|
1731 |
- admin_type=admin_type, |
|
1732 |
- phone=phone, |
|
1733 |
- name=name, |
|
1734 |
- password='', |
|
1735 |
- encryption=encryption, |
|
1736 |
- ) |
|
1728 |
+ target_admin = AdministratorInfo.objects.get(admin_id=target_admin_id, status=True) |
|
1729 |
+ |
|
1730 |
+ if admin_type != -1: |
|
1731 |
+ target_admin.admin_type = admin_type |
|
1732 |
+ |
|
1733 |
+ if phone: |
|
1734 |
+ target_admin.phone = phone |
|
1735 |
+ |
|
1736 |
+ if name: |
|
1737 |
+ target_admin.name = name |
|
1738 |
+ AdministratorLoginLogInfo.objects.filter(admin_id=target_admin_id).update(admin_name=name) |
|
1739 |
+ |
|
1740 |
+ |
|
1741 |
+ if password: |
|
1742 |
+ encryption = make_password(strip(password), settings.MAKE_PASSWORD_SALT, settings.MAKE_PASSWORD_HASHER) |
|
1743 |
+ target_admin.encryption = encryption |
|
1744 |
+ |
|
1745 |
+ target_admin.save() |
|
1737 | 1746 |
|
1738 | 1747 |
return response(200, 'Update Admin Success', u'更新后台管理员成功') |
1739 | 1748 |
|
@@ -1757,3 +1766,36 @@ def administrator_delete(request): |
||
1757 | 1766 |
AdministratorInfo.objects.filter(admin_id=target_admin_id).update(status=False) |
1758 | 1767 |
|
1759 | 1768 |
return response(200, 'Delete Admin Success', u'删除后台管理员成功') |
1769 |
+ |
|
1770 |
+ |
|
1771 |
+def administrator_login_list(request): |
|
1772 |
+ brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
1773 |
+ admin_id = request.POST.get('admin_id', '') |
|
1774 |
+ page = request.POST.get('page', 1) |
|
1775 |
+ num = request.POST.get('num', 20) |
|
1776 |
+ target_admin_id = request.POST.get('target_admin_id', '') |
|
1777 |
+ |
|
1778 |
+ if brand_id != settings.KODO_DEFAULT_BRAND_ID: |
|
1779 |
+ return response(ProductBrandStatusCode.BRAND_NOT_MATCH) |
|
1780 |
+ |
|
1781 |
+ try: |
|
1782 |
+ administrator = AdministratorInfo.objects.get(admin_id=admin_id, user_status=AdministratorInfo.ACTIVATED, status=True) |
|
1783 |
+ except AdministratorInfo.DoesNotExist: |
|
1784 |
+ return response(AdministratorStatusCode.ADMINISTRATOR_NOT_FOUND) |
|
1785 |
+ |
|
1786 |
+ if administrator.admin_type != AdministratorInfo.ADMINISTRATOR: |
|
1787 |
+ return response(AdministratorStatusCode.ADMINISTRATOR_PERMISSION_DENIED) |
|
1788 |
+ |
|
1789 |
+ logs = AdministratorLoginLogInfo.objects.filter(status=True).order_by('-login_at') |
|
1790 |
+ |
|
1791 |
+ if target_admin_id: |
|
1792 |
+ logs = logs.filter(admin_id=target_admin_id) |
|
1793 |
+ |
|
1794 |
+ count = logs.count() |
|
1795 |
+ logs, left = pagination(logs, page, num) |
|
1796 |
+ logs = [log.admindata for log in logs] |
|
1797 |
+ return response(200, 'Get Administrator Login List Success', u'获取后台管理员登录日志成功', data={ |
|
1798 |
+ 'logs': logs, |
|
1799 |
+ 'left': left, |
|
1800 |
+ 'count': count |
|
1801 |
+ }) |
@@ -9,6 +9,7 @@ from django.contrib.auth.hashers import check_password |
||
9 | 9 |
from django.db import transaction |
10 | 10 |
from django_logit import logit |
11 | 11 |
from django_response import response |
12 |
+from ipaddr import client_ip |
|
12 | 13 |
from pywe_miniapp import get_phone_number |
13 | 14 |
from pywe_storage import RedisStorage |
14 | 15 |
from TimeConvert import TimeConvert as tc |
@@ -16,7 +17,7 @@ from TimeConvert import TimeConvert as tc |
||
16 | 17 |
from account.models import UserInfo |
17 | 18 |
from coupon.models import CouponInfo, UserCouponInfo |
18 | 19 |
from integral.models import SaleclerkSubmitLogInfo |
19 |
-from logs.models import MchInfoEncryptLogInfo |
|
20 |
+from logs.models import MchInfoEncryptLogInfo, AdministratorLoginLogInfo |
|
20 | 21 |
from mch.models import (ActivityInfo, AdministratorInfo, BrandInfo, ConsumeInfoSubmitLogInfo, DistributorInfo, |
21 | 22 |
LatestAppInfo, LatestAppScreenInfo, ModelInfo, OperatorInfo) |
22 | 23 |
from statistic.models import ConsumeModelSaleStatisticInfo, ConsumeSaleStatisticInfo, ConsumeUserStatisticInfo |
@@ -78,6 +79,14 @@ def admin_login_api(request): |
||
78 | 79 |
if not check_password(password, administrator.encryption): |
79 | 80 |
return response(AdministratorStatusCode.ADMINISTRATOR_PASSWORD_ERROR) |
80 | 81 |
|
82 |
+ |
|
83 |
+ AdministratorLoginLogInfo.objects.create( |
|
84 |
+ admin_id=administrator.admin_id, |
|
85 |
+ admin_name=administrator.name, |
|
86 |
+ login_ip=client_ip(request), |
|
87 |
+ login_at=tc.utc_datetime(), |
|
88 |
+ ) |
|
89 |
+ |
|
81 | 90 |
request.session['admin_id'] = administrator.admin_id |
82 | 91 |
|
83 | 92 |
return response(200, 'Admin Login Success', u'管理员登录成功', data={ |
@@ -186,6 +186,9 @@ urlpatterns += [ |
||
186 | 186 |
url(r'^admin/administrator/create$', admin_views.administrator_create, name='administrator_create'), |
187 | 187 |
url(r'^admin/administrator/update$', admin_views.administrator_update, name='administrator_update'), |
188 | 188 |
url(r'^admin/administrator/delete$', admin_views.administrator_delete, name='administrator_delete'), |
189 |
+ |
|
190 |
+ url(r'^admin/administrator/login/list$', admin_views.administrator_login_list, name='administrator_login_list'), |
|
191 |
+ |
|
189 | 192 |
] |
190 | 193 |
|
191 | 194 |
urlpatterns += [ |
@@ -0,0 +1,19 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+# Generated by Django 3.2.9 on 2022-07-04 12:01 |
|
3 |
+ |
|
4 |
+from django.db import migrations, models |
|
5 |
+ |
|
6 |
+ |
|
7 |
+class Migration(migrations.Migration): |
|
8 |
+ |
|
9 |
+ dependencies = [ |
|
10 |
+ ('coupon', '0016_auto_20201202_1203'), |
|
11 |
+ ] |
|
12 |
+ |
|
13 |
+ operations = [ |
|
14 |
+ migrations.AlterField( |
|
15 |
+ model_name='usercouponinfo', |
|
16 |
+ name='coupon_from', |
|
17 |
+ field=models.CharField(blank=True, db_index=True, default='MEMBER_BENEFITS', help_text=u'劵来源', max_length=32, null=True, verbose_name='coupon_from'), |
|
18 |
+ ), |
|
19 |
+ ] |
@@ -4,7 +4,7 @@ from django.contrib import admin |
||
4 | 4 |
from django_admin import ReadOnlyModelAdmin |
5 | 5 |
|
6 | 6 |
from logs.models import (ComplementCodeLogInfo, MchInfoDecryptLogInfo, MchInfoEncryptLogInfo, MchLogInfo, |
7 |
- MchSearchModelAndCameraLogInfo) |
|
7 |
+ MchSearchModelAndCameraLogInfo, AdministratorLoginLogInfo) |
|
8 | 8 |
|
9 | 9 |
|
10 | 10 |
class MchInfoEncryptLogInfoAdmin(ReadOnlyModelAdmin, admin.ModelAdmin): |
@@ -32,9 +32,14 @@ class ComplementCodeLogInfoAdmin(admin.ModelAdmin): |
||
32 | 32 |
list_display = ('user_id', 'log_id', 'name', 'phone', 'model_id', 'model_name', 'sn', 'shot_image', 'invoice_image', 'audit_status', 'ciphertext', 'is_contacted', 'is_upload_qiniu', 'status', 'created_at', 'updated_at') |
33 | 33 |
list_filter = ('model_id', 'audit_status', 'is_contacted', 'is_upload_qiniu', 'status') |
34 | 34 |
|
35 |
+class AdministratorLoginLogInfoAdmin(admin.ModelAdmin): |
|
36 |
+ list_display = ('admin_id', 'admin_name', 'login_ip', 'login_at', 'status', 'created_at', 'updated_at') |
|
37 |
+ list_filter = ('admin_id', 'admin_name') |
|
38 |
+ |
|
35 | 39 |
|
36 | 40 |
admin.site.register(MchInfoDecryptLogInfo, MchInfoDecryptLogInfoAdmin) |
37 | 41 |
admin.site.register(MchInfoEncryptLogInfo, MchInfoEncryptLogInfoAdmin) |
38 | 42 |
admin.site.register(MchSearchModelAndCameraLogInfo, MchSearchModelAndCameraLogInfoAdmin) |
39 | 43 |
admin.site.register(MchLogInfo, MchLogInfoAdmin) |
40 | 44 |
admin.site.register(ComplementCodeLogInfo, ComplementCodeLogInfoAdmin) |
45 |
+admin.site.register(AdministratorLoginLogInfo, AdministratorLoginLogInfoAdmin) |
@@ -0,0 +1,30 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+# Generated by Django 3.2.9 on 2022-07-04 12:01 |
|
3 |
+ |
|
4 |
+from django.db import migrations, models |
|
5 |
+ |
|
6 |
+ |
|
7 |
+class Migration(migrations.Migration): |
|
8 |
+ |
|
9 |
+ dependencies = [ |
|
10 |
+ ('logs', '0017_complementcodeloginfo_is_upload_qiniu'), |
|
11 |
+ ] |
|
12 |
+ |
|
13 |
+ operations = [ |
|
14 |
+ migrations.CreateModel( |
|
15 |
+ name='AdministratorLoginLogInfo', |
|
16 |
+ fields=[ |
|
17 |
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|
18 |
+ ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')), |
|
19 |
+ ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')), |
|
20 |
+ ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')), |
|
21 |
+ ('admin_id', models.CharField(blank=True, help_text='管理员唯一标识', max_length=32, null=True, verbose_name='admin_id')), |
|
22 |
+ ('admin_name', models.CharField(blank=True, help_text='管理员姓名', max_length=255, null=True, verbose_name='name')), |
|
23 |
+ ('login_ip', models.CharField(blank=True, help_text='登录IP', max_length=32, null=True, verbose_name='login_ip')), |
|
24 |
+ ('login_at', models.DateTimeField(blank=True, help_text='登录时间', null=True, verbose_name='login_at')), |
|
25 |
+ ], |
|
26 |
+ options={ |
|
27 |
+ 'abstract': False, |
|
28 |
+ }, |
|
29 |
+ ), |
|
30 |
+ ] |
@@ -193,3 +193,19 @@ class ComplementCodeLogInfo(BaseModelMixin): |
||
193 | 193 |
'ciphertext': self.ciphertext, |
194 | 194 |
'created_at': tc.local_string(utc_dt=self.created_at, format='%Y-%m-%d %H:%M:%S') |
195 | 195 |
} |
196 |
+ |
|
197 |
+class AdministratorLoginLogInfo(BaseModelMixin): |
|
198 |
+ admin_id = models.CharField(_(u'admin_id'), max_length=32, blank=True, null=True, help_text=u'管理员唯一标识') |
|
199 |
+ admin_name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'管理员姓名') |
|
200 |
+ login_ip = models.CharField(_(u'login_ip'), max_length=32, blank=True, null=True, help_text=_(u'登录IP')) |
|
201 |
+ login_at = models.DateTimeField(_(u'login_at'), blank=True, null=True, help_text=_(u'登录时间')) |
|
202 |
+ |
|
203 |
+ |
|
204 |
+ @property |
|
205 |
+ def admindata(self): |
|
206 |
+ return { |
|
207 |
+ 'admin_id': self.admin_id, |
|
208 |
+ 'admin_name': self.admin_name, |
|
209 |
+ 'login_ip': self.login_ip, |
|
210 |
+ 'login_at': tc.local_string(utc_dt=self.login_at, format='%Y-%m-%d %H:%M:%S'), |
|
211 |
+ } |
@@ -0,0 +1,60 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+# Generated by Django 3.2.9 on 2022-07-04 12:01 |
|
3 |
+ |
|
4 |
+from django.db import migrations, models |
|
5 |
+import simditor.fields |
|
6 |
+ |
|
7 |
+ |
|
8 |
+class Migration(migrations.Migration): |
|
9 |
+ |
|
10 |
+ dependencies = [ |
|
11 |
+ ('member', '0031_auto_20211124_1850'), |
|
12 |
+ ] |
|
13 |
+ |
|
14 |
+ operations = [ |
|
15 |
+ migrations.AlterField( |
|
16 |
+ model_name='memberactivityinfo', |
|
17 |
+ name='brand_id', |
|
18 |
+ field=models.CharField(blank=True, db_index=True, default='', help_text='品牌唯一标识', max_length=32, verbose_name='brand_id'), |
|
19 |
+ ), |
|
20 |
+ migrations.AlterField( |
|
21 |
+ model_name='memberactivityinfo', |
|
22 |
+ name='brand_name', |
|
23 |
+ field=models.CharField(blank=True, default='', help_text='品牌名称', max_length=255, verbose_name='brand_name'), |
|
24 |
+ ), |
|
25 |
+ migrations.AlterField( |
|
26 |
+ model_name='memberactivityinfo', |
|
27 |
+ name='city', |
|
28 |
+ field=models.CharField(blank=True, default='', help_text='活动城市', max_length=255, verbose_name='city'), |
|
29 |
+ ), |
|
30 |
+ migrations.AlterField( |
|
31 |
+ model_name='memberactivityinfo', |
|
32 |
+ name='content_rich_text', |
|
33 |
+ field=simditor.fields.RichTextField(blank=True, default='', help_text='活动描述', verbose_name='content_rich_text'), |
|
34 |
+ ), |
|
35 |
+ migrations.AlterField( |
|
36 |
+ model_name='memberactivityinfo', |
|
37 |
+ name='location', |
|
38 |
+ field=models.CharField(blank=True, default='', help_text='活动地点', max_length=255, verbose_name='location'), |
|
39 |
+ ), |
|
40 |
+ migrations.AlterField( |
|
41 |
+ model_name='memberactivityinfo', |
|
42 |
+ name='share_h5_link', |
|
43 |
+ field=models.CharField(blank=True, default='', help_text='活动H5分享', max_length=255, verbose_name='share_h5_link'), |
|
44 |
+ ), |
|
45 |
+ migrations.AlterField( |
|
46 |
+ model_name='memberactivityinfo', |
|
47 |
+ name='share_img_link', |
|
48 |
+ field=models.CharField(blank=True, default='', help_text='活动图片分享', max_length=255, verbose_name='share_img_link'), |
|
49 |
+ ), |
|
50 |
+ migrations.AlterField( |
|
51 |
+ model_name='memberactivityinfo', |
|
52 |
+ name='subtitle', |
|
53 |
+ field=models.CharField(blank=True, default='', help_text='活动二级名称', max_length=255, verbose_name='subtitle'), |
|
54 |
+ ), |
|
55 |
+ migrations.AlterField( |
|
56 |
+ model_name='memberactivityinfo', |
|
57 |
+ name='title', |
|
58 |
+ field=models.CharField(blank=True, default='', help_text='活动名称', max_length=255, verbose_name='title'), |
|
59 |
+ ), |
|
60 |
+ ] |
@@ -20,7 +20,7 @@ django-redis-connector==1.0.4 |
||
20 | 20 |
django-response==1.1.1 |
21 | 21 |
django-rlog==1.0.7 |
22 | 22 |
django-shortuuidfield==0.1.3 |
23 |
-# django-simpleui==3.9.1 |
|
23 |
+django-simpleui==3.9.1 |
|
24 | 24 |
django-six==1.0.4 |
25 | 25 |
django-uniapi==1.0.10 |
26 | 26 |
django-we==1.5.6 |