# coding=utf-8 from django.db import models from django.db.models import Q from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, Group, AbstractUser from django.utils import timezone from rest_framework.utils import model_meta from utils.exceptions import CustomError from django.conf import settings from utils.wx.WXBizDataCrypt import WXBizDataCrypt from utils.wx.wechat import WeChat from apps.WechatApplet.models import WechatApplet class UserManager(BaseUserManager): def create_superuser(self, username, password, **extra_fields): u = self.create_user(User.EMPLOYEE, username, password, **extra_fields) u.is_active = True u.is_superuser = True u.save(using=self._db) return u def create_user(self, type, username, password=None, **extra_fields): if not username: raise CustomError(u'请输入账号!') count = User.objects.filter(username=username).count() if count > 0: raise CustomError(u'该账号已存在!') user = self.model( type=type, username=username, is_superuser=False, last_login=timezone.now(), **extra_fields ) user.set_password(password) user.save(using=self._db) return user class User(AbstractBaseUser, PermissionsMixin): EMPLOYEE = 1 CUSTOMER = 2 TYPE_CHOICES = ( (EMPLOYEE, u'员工'), # 内部员工 (CUSTOMER, u'客户'), # 客户 ) username = models.CharField(verbose_name=u'用户名', max_length=30, unique=True, db_index=True, help_text=u'不多于20个字符。只能用字母、数字和字符。') # password = models.CharField(u'密码', max_length=128, blank=True,) is_active = models.BooleanField(verbose_name=u'是否可用', default=True, editable=False) date_joined = models.DateTimeField(verbose_name=u'注册时间', auto_now_add=True, editable=False) type = models.PositiveSmallIntegerField(verbose_name=u"类型", choices=TYPE_CHOICES, editable=False, default=CUSTOMER) name = models.CharField(max_length=20, verbose_name=u"姓名") gender = models.PositiveSmallIntegerField(choices=settings.GENDER_CHOICES, verbose_name=u"性别", default=settings.MALE) face = models.CharField(max_length=200, verbose_name=u'头像', null=True) ID_card = models.CharField(max_length=18, verbose_name=u"身份证号", null=True, blank=True) address = models.CharField(max_length=100, verbose_name=u"家庭住址", null=True, blank=True) tel = models.CharField(max_length=15, verbose_name=u"手机", null=True, ) position = models.CharField(max_length=15, verbose_name=u"岗位", null=True) create_user = models.ForeignKey('self', verbose_name='创建者', null=True, on_delete=models.PROTECT) objects = UserManager() USERNAME_FIELD = 'username' REQUIRED_FIELDS = [] class Meta: db_table = "auth_user" verbose_name = u"人员管理" unique_together = [ ('username') ] ordering = ['-id'] default_permissions = () permissions = [ ] def __unicode__(self): return self.username def change_password(self, new_password, confirm_password, old_password): if new_password != confirm_password: raise CustomError(u'两次输入的密码不一致,请检查') if not self.check_password(old_password): raise CustomError(u'原密码输入错误,请检查') self.set_password(new_password) self.save() def update_item(self, validated_data): def update(): info = model_meta.get_field_info(self) for attr, value in validated_data.items(): if attr in info.relations and info.relations[attr].to_many: field = getattr(self, attr) field.set(value) else: setattr(self, attr, value) if not 'username' in validated_data: raise CustomError(u'账号不能为空!') count = User.objects.filter(username=validated_data['username']).exclude(id=self.id).count() if count > 0: raise CustomError(u'该账号已存在!') if not 'password' in validated_data or not validated_data['password']: validated_data['password'] = self.password update() else: update() self.set_password(validated_data['password']) self.save() return self def is_login(self): if self.is_authenticated and self: return True return False class CustomerWechat(models.Model): wechat_app = models.ForeignKey(WechatApplet, verbose_name=u'小程序', on_delete=models.PROTECT, editable=False) customer = models.ForeignKey(User, verbose_name=u'用户', on_delete=models.PROTECT, editable=False, null=True) openid = models.CharField(max_length=512, verbose_name=u"openid") session_key = models.CharField(max_length=512, verbose_name=u'session_key', null=True) # 保存客户+小程序的关联信息。如果一个客户登录多个小程序,这里会有多条数据 class Meta: db_table = 'customer_wechat' verbose_name = u'微信客户' unique_together = [ ('openid', 'wechat_app') ] default_permissions = () @staticmethod def login(code, appid): wechat_applet = WechatApplet.getByAppid(appid) res = WeChat.code2Session(appid, wechat_applet.secret, code) instance = CustomerWechat.objects.filter(openid=res['openid'], wechat_app__authorizer_appid=appid).first() if not instance: instance = CustomerWechat.objects.create( wechat_app=wechat_applet, openid=res['openid'], session_key=res['session_key'] ) else: instance.session_key = res['session_key'] instance.save() return instance @staticmethod def bindWechat(appid, openid, phoneEncryptedData, phoneIv): customer_wechat = CustomerWechat.objects.filter(openid=openid, wechat_app__authorizer_appid=appid).first() if not customer_wechat: raise CustomError(u'未找到相应的微信客户!') pc = WXBizDataCrypt(appid, customer_wechat.session_key) phon_data = pc.decrypt(phoneEncryptedData, phoneIv) tel = phon_data['purePhoneNumber'] if customer_wechat.customer and customer_wechat.customer.username == tel: # 已绑定用户,且用户账号和手机号一致 return customer_wechat.customer # 用户用手机号、密码登录后,在绑定微信,两个号码可能会不符 # 张三是绑定用户。 张三的账号,在李四小程序上登录,绑定信息时,手机号可能不符。 # 这种情况,应该返回tel对应的账号,或者创建tel账号 user = User.objects.filter(username=tel).first() if not user: # 密码默认手机号 user = User.objects.create_user(User.CUSTOMER, tel, password=tel, **{ 'tel': tel, 'name': tel, } ) customer_wechat.customer = user customer_wechat.save() return user Group.add_to_class('create_user', models.ForeignKey(User, verbose_name=u"创建人", on_delete=models.PROTECT, editable=False))